Storybook 기초

Storybook 설치부터 첫 Story 작성까지 기본기를 다집니다.

← Storybook 가이드로 돌아가기

📦 Storybook 설치

# 자동 설치 (권장)
npx storybook@latest init

# 수동 설치
npm install --save-dev @storybook/react @storybook/react-vite
npm install --save-dev @storybook/addon-essentials
npm install --save-dev @storybook/addon-interactions
npm install --save-dev @storybook/addon-links
npm install --save-dev @storybook/blocks
💡 Tip: npx storybook@latest init 명령어가 프로젝트를 자동으로 감지하고 최적의 설정을 생성합니다.

📁 폴더 구조

.storybook/
  main.ts          # Storybook 설정
  preview.ts       # 전역 데코레이터
src/
  components/
    Button/
      Button.tsx
      Button.stories.tsx  # Story 파일
    Card/
      Card.tsx
      Card.stories.tsx

⚙️ main.ts 설정

import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
  ],
  framework: '@storybook/react-vite',
};

export default config;

✍️ CSF 3.0으로 Story 작성하기

1️⃣ 기본 Button 컴포넌트

// src/components/Button/Button.tsx
import React from 'react';

export interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  children: React.ReactNode;
  onClick?: () => void;
}

export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'medium',
  disabled = false,
  children,
  onClick,
}) => {
  const baseStyles = 'rounded font-semibold transition-colors';
  
  const variantStyles = {
    primary: 'bg-blue-600 hover:bg-blue-700 text-white',
    secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
    danger: 'bg-red-600 hover:bg-red-700 text-white',
  };
  
  const sizeStyles = {
    small: 'px-3 py-1 text-sm',
    medium: 'px-4 py-2 text-base',
    large: 'px-6 py-3 text-lg',
  };
  
  return (
    <button
      className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

2️⃣ Story 파일 작성 (CSF 3.0)

// src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

// Meta 정보 정의
const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger'],
      description: '버튼 스타일 변형',
    },
    size: {
      control: 'select',
      options: ['small', 'medium', 'large'],
      description: '버튼 크기',
    },
    disabled: {
      control: 'boolean',
      description: '비활성화 상태',
    },
    onClick: { action: 'clicked' },
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

// Story 정의
export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Primary Button',
  },
};

export const Secondary: Story = {
  args: {
    variant: 'secondary',
    children: 'Secondary Button',
  },
};

export const Danger: Story = {
  args: {
    variant: 'danger',
    children: 'Danger Button',
  },
};

export const Small: Story = {
  args: {
    size: 'small',
    children: 'Small Button',
  },
};

export const Large: Story = {
  args: {
    size: 'large',
    children: 'Large Button',
  },
};

export const Disabled: Story = {
  args: {
    disabled: true,
    children: 'Disabled Button',
  },
};

✅ CSF 3.0 장점

  • • 간결한 문법
  • • 타입 안정성
  • • 자동 완성
  • • 재사용성
  • • 성능 최적화

📋 주요 속성

  • title: 계층 구조
  • component: 컴포넌트
  • tags: 자동 문서화
  • argTypes: 컨트롤
  • args: 기본값

🎯 Story 작성 팁

  • • 상태별로 분리
  • • 명확한 이름
  • • 실제 사용 예시
  • • 엣지 케이스
  • • 접근성 고려

🚀 Storybook 실행

# 개발 모드
npm run storybook

# 빌드
npm run build-storybook
기본 포트: http://localhost:6006
package.json 스크립트:
"scripts": {
  "storybook": "storybook dev -p 6006",
  "build-storybook": "storybook build"
}

💡 Best Practices

  • • Story 파일은 컴포넌트와 같은 폴더에
  • • 명명 규칙: ComponentName.stories.tsx
  • • 모든 주요 상태를 Story로 작성
  • • argTypes로 문서화 강화
  • • actions로 이벤트 핸들러 테스트

⚠️ 주의사항

  • • Story는 독립적으로 동작해야 함
  • • 외부 상태에 의존하지 않기
  • • API 호출은 Mock 사용
  • • 복잡한 로직은 컴포넌트에서 분리
  • • 파일 크기 주의 (이미지 등)