Storybook
Storybook은 UI 컴포넌트를 앱과 분리해서 독립적으로 개발/테스트할 수 있는 도구입니다.
핵심 기능
- 컴포넌트 격리 렌더링: 앱 실행 없이 컴포넌트 하나만 띄워서 확인
- Controls: props를 UI에서 실시간으로 변경하며 테스트 (색상, 크기, 텍스트 등)
- Docs: 컴포넌트 API 문서 자동 생성
- 다양한 상태 테스트: 로딩, 에러, 빈 데이터 등 각 상태를 "스토리"로 정의
- 시각적 회귀 테스트: 스냅샷 비교로 UI 깨짐 감지
Component-Driven Development (컴포넌트 주도 개발; CDD)
Storybook 가이드 문서에도 언급되는 내용이지만, Storybook이 CDD 방법론과 함꼐 개발하기 좋은 도구라는 것을 강조합니다.
Component-Driven Development, 간단하게 알아만 가보자면. 컴포넌트를 모듈식 요소로 사용하면서 UI를 구축하는 개발 방법론입니다. 즉, 기본 컴포넌트부터 시작해 '상향식'으로 구축되고 점진적으로 결합하는 방법입니다.
장점
- 품질: 컴포넌트를 별도로 구축하고 상태를 정의하면서 다양한 시나리오에 작동하는지 확인
- 내구성: 세부사항 까지 버그를 찾아낸다.
- 속도: 기본 컴포넌트를 재사용하면서 빠르게 조립가능 하다.
- 효율성: 분해된 기본 컴포넌트들이 병렬화되어(나뉘어) 개발될 수 있다.
설계 틀은 3가지로 나뉩니다.
- 컴포넌트
- 컨테이너 (2개 이상의 컴포넌트 조합)
- 페이지 (2개 이상 컨테이너)
Storybook이 권장하는 테스트 크로마틱 (Chromatic) 도 있습니다.
Component Story Format (CSF)
Storybook은 이름처럼 Story 단위로 작성됩니다.
Story 파일에 구성요소를 정의하며 사용하는데 이를 Component Story Format (CSF) 라고 표현합니다. (Story가 작성된 파일이 CSF 파일)
이 파일들은 개발 전용이기 때문에 배포시 번들에는 포함되지 않아 안심해도 괜찮습니다.
별도 포트(보통 6006)에서 독립 서버가 뜨고, 각 컴포넌트별로 .stories.tsx 파일을 작성합니다:
장단점
장점:
- 업계 표준, 생태계 풍부
- Controls/Docs 자동화
- 디자이너 협업에 유리
- 애드온 풍부 (a11y, viewport 등)
단점:
- 의존성 무거움 (수십 개 패키지)
- 설정 복잡
- 스토리 파일 유지보수 필요
- 빌드 시간 증가
Skills
기본 사용법
Story는 간단하게 메타데이터를 Export default 하는식으로 제공합니다.
// Button.stories.ts | .tsx
import type { Meta } from "@storybook/react";
import { Button } from "./Button";
const meta: Meta<typeof Button> = {
component: Button,
};
export default meta; // 메타데이터 export default
스토리 정의
type Story = StoryObj<typeof Button>;
// Button에 대해 Primary 정의가 필요하다면 해당 컴포넌트를 사용한 Story 타입으로 export 합니다.
export const Primary: Story = {
args: { variant: 'primary', children: 'Click me' },
};
export const PrimaryCustomRender: Story = {
render: () => <Button primary label="Button" />,
};
export const Disabled: Story = {
args: { variant: 'primary', disabled: true, children: 'Disabled' },
};
React Hooks 사용
// Button.stories.ts | .tsx
// ..위 와 같이 메타데이터 export default
type Story = StoryObj<typeof Button>;
const ButtonWithHooks = () => {
const [cnt, setCnt] = useState(0);
const handleOnChange = () => {
setCnt((prev) => prev + 1);
};
return <Button primary={isPrimary} onClick={handleOnChange} />;
};
export const Primary: Story = {
name: "I am the primary",
render: () => <ButtonWithHooks />,
};
React의 hooks를 사용한 컴포넌트를 만들 수도 있습니다. 물론, 사용할 hooks와 같은 컴포넌트를 렌더시키는 렌더를 하나 더 정의해야 합니다.
See also
- kickstartDS - 디자인 시스템을 위한 스타터킷 오픈소스
- React
- Ladle