Skip to content

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

Favorite site