🤔 넌 왜 쓰니?
내가 처음 storybook을 배워야 겠다고 생각한 것은 공통 컴포넌트를 만들고 싶어서였다. 프로젝트를 만들 때마다 비슷하지만 조금씩 다른 UI요소들을 볼 때마다 새로 만들곤 했다. 뭔가 코드 낭비 같기도 하고 시간도 오래걸려서 이 문제를 해결할만한 도구가 없을까 고민하다가 storybook이란 것을 발견했다. 그래서 꼭 "배워야지.. 배워야지..." 미루다 지금와서야 공부를 시작한다.
🙃 Storybook이란?
사용해보니 storybook은 내가 생각한 장점 말고도 훨씬 더 많은 장점을 가지고 있었다. 컴포넌트를 어디서 부터 만들어야 할지 모르겠다면 storybook을 사용해라!
storybook은 자연스럽게 컴포넌트 주도의 개발을 하게 해준다. 예를 들어 TextField를 만든다고 가정하자. TextField를 만들려면 input란, 성공 및 실패 아이콘, 에러메세지 란 등등 필요한 구성요소들이 있다. 이것들 각각을 컴포넌트로 만들어서 관리하다 보면 중복되는 곳에 또 사용할 수 있다. 즉, 가장 기본적이고 독립적인 컴포넌트부터 개발을 하게 되기에 컴포넌트 주도 개발을 할 수 있으며 재사용성 또한 증가한다.
그리고 내가 어떤 컴포넌트를 만들었는지도 한눈에 확인하기 쉽다. 코드로만 작성되어 있다면 이게 무슨 UI인지 확인하기 어렵지만 storybook에서는 어떤 UI인지 눈으로 확인할 수 있기 때문에 좋은것 같다.
마지막으로 디자이너들과 협업하기 좋다. 스토리북을 배포한 링크를 디자이너들에게 보여주면 되기 때문에 일일이 코드를 실행시켜 보여줄 필요가 없다.
그리고 내 생각엔 이런점도 좋은거 같다. 예를들어 결제 페이지의 UI인 경우 개발자가 로그인을 하고 물건을 담아서 결제페이지까지 가야 볼 수 있다. 이럴때 storybook을 사용한다면 바로 확인할 수 있다는 장점이 있는거 같다.
🎇 사용방법
공식문서에 아주 자세하게 프레임워크별로 설치 방법이 나와 있으니 이것을 보고 따라하자
Get started with Storybook • Storybook docs
Storybook is a frontend workshop for building UI components and pages in isolation. Thousands of teams use it for UI development, testing, and documentation. It’s open source and free.
storybook.js.org
만약 storybook이 설치가 다 되었다면 stories 폴더안에 스토리와 컴포넌트가 같이 있을 것이다. 나는 이것을 따로 관리하는 것이 더 보기 편할 거 같아서 분리해줬다.
1️⃣ 컴포넌트 제작하기
예를들어 button 컴포넌트를 만들다고 가정하겠다. 이 버튼은 클릭 되었을 때랑 아닐 때 2가지 버전이 있다. 그럼 우리는 버튼의 클릭이벤트, 버튼 이름, 체크되었는지 안되었는지 확인 하는 값, 총 3가지가 필요하다.
interface ITagButton {
children: string;
isChecked: boolean;
onClick?: () => void;
}
export default function TagButton({ children, onClick, isChecked }: ITagButton) {
const buttonStyle = isChecked ? "bg-white text-primary" : "text-white bg-dark-opacity";
return (
<button
className={`text-sm font-medium rounded-tag-button px-[10px] border border-white h-[33px] ${buttonStyle}`}
onClick={onClick}
>
{children}
</button>
);
}
우선 interface로 필요한 요소들을 정의해준다. 이후 컴포넌트를 만들어서 값을 넣어준다.
나는 tailwindCSS를 사용해서 checked 값에 따라 스타일링을 다르게 해줬다.
2️⃣ 스토리 제작하기
import type { Meta, StoryObj } from "@storybook/react";
import TagButton from "../components/TagButton";
const meta = {
title: "Buttons/TagButton",
component: TagButton,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {
children: {
control: "text",
description: "버튼의 텍스트",
defaultValue: "button",
},
isChecked: {
control: "boolean",
description: "버튼 활성화 여부",
defaultValue: true,
},
onClick: { action: "clicked", description: "버튼 클릭 이벤트" },
},
} satisfies Meta<typeof TagButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: "button",
isChecked: false,
},
};
story는 처음 보기 때문에 meta객체에 있는 요소들을 하나씩 살펴보겠다.
title : 스토리북에 위치시킬 곳을 나타낸다. (Button이란 폴더에 TagButton파일이 있는 느낌이다.)
component : 해당 스토리에 맞는 컴포넌트를 써준다.
parameters : storybook에서의 요소 위치
argTypes : storybook에서 컨트롤 할 수 있는 값이다. (컴포넌트를 만들 때 사용했던 요소들을 적어준다.)
이렇게 만들어진 컴포넌트는 아래와 같이 쓰인다.
<TagButton
key={tag}
isChecked={tag === selectedTag}
onClick={() => {
setSelectedTag(tag);
}}
>
리스트
</TagButton>