storybook

storybook

Storybook 스토리 작성 및 CSF 3.0 베스트 프랙티스 스킬. 다음 상황에서 사용: (1) 새 스토리 파일(.stories.tsx, .stories.ts) 작성 시, (2) 기존 스토리 수정 시, (3) Args, Decorators, Parameters 설정 시, (4) Storybook 설정 파일(.storybook/) 작업 시, (5) 'story', 'stories', 'storybook', 'CSF' 키워드가 포함된 작업 시

3星標
0分支
更新於 2/5/2026
SKILL.md
readonlyread-only
name
storybook
description

"Storybook 스토리 작성 및 CSF 3.0 베스트 프랙티스 스킬. 다음 상황에서 사용: (1) 새 스토리 파일(.stories.tsx, .stories.ts) 작성 시, (2) 기존 스토리 수정 시, (3) Args, Decorators, Parameters 설정 시, (4) Storybook 설정 파일(.storybook/) 작업 시, (5) 'story', 'stories', 'storybook', 'CSF' 키워드가 포함된 작업 시"

version
"1.0.0"

Storybook

모범 관례

1. CSF 3.0 형식 사용

최신 Component Story Format 3.0 사용. 더 간결하고 타입 안전.

// ❌ CSF 2.0 (구형)
export default {
  title: 'Components/Button',
  component: Button,
};

export const Primary = () => <Button variant="primary">Click me</Button>;

// ✅ CSF 3.0 (권장)
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'], // 자동 문서 생성
} satisfies Meta<typeof Button>;

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

export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Click me',
  },
};

2. Args 기반 스토리 작성

컴포넌트 Props를 Args로 정의하여 Controls 패널에서 인터랙티브하게 조작 가능.

기본값은 args에서 선언 (❌ argTypes.defaultValue 사용 금지). Meta의 args에 기본값을 두면 Controls 패널에서 자동으로 해당 값이 선택

// ❌ 하드코딩된 Props
export const Disabled: Story = {
  render: () => <Button disabled>Disabled</Button>,
};

// ✅ Args 사용
export const Disabled: Story = {
  args: {
    disabled: true,
    children: 'Disabled',
  },
};

// ✅ Args 재사용 및 오버라이드
export const DisabledPrimary: Story = {
  args: {
    ...Primary.args,
    disabled: true,
  },
};

3. 타입 안전한 Meta 정의

satisfies 키워드로 타입 체크와 타입 추론 동시 활용.

// ❌ 타입 추론 불가
const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
};

// ✅ 타입 체크와 추론 모두 가능
const meta = {
  title: 'Components/Button',
  component: Button,
  args: {
    size: 'md',
    variant: 'primary',
  },
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'tertiary'],
    },
  },
} satisfies Meta<typeof Button>;

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

4. Decorators로 컨텍스트 제공

공통 래퍼나 Provider를 Decorator로 적용.

// 개별 스토리에 Decorator 적용
export const WithTheme: Story = {
  decorators: [
    (Story) => (
      <ThemeProvider theme="dark">
        <Story />
      </ThemeProvider>
    ),
  ],
};

// 모든 스토리에 Decorator 적용
const meta = {
  component: Button,
  decorators: [
    (Story) => (
      <div style={{ padding: '3rem' }}>
        <Story />
      </div>
    ),
  ],
} satisfies Meta<typeof Button>;

5. Parameters로 동작 커스터마이즈

const meta = {
  component: Button,
  parameters: {
    layout: 'centered', // 스토리를 중앙 정렬
    backgrounds: {
      default: 'light',
      values: [
        { name: 'light', value: '#ffffff' },
        { name: 'dark', value: '#000000' },
      ],
    },
  },
} satisfies Meta<typeof Button>;

// 개별 스토리에서 오버라이드
export const OnDark: Story = {
  parameters: {
    backgrounds: { default: 'dark' },
  },
};

6. ArgTypes로 Controls 세밀하게 제어

const meta = {
  component: Button,
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'tertiary'],
      description: '버튼 스타일 변형',
    },
    size: {
      control: 'radio',
      options: ['sm', 'md', 'lg'],
    },
    onClick: {
      action: 'clicked', // Actions 패널에 표시
    },
    children: {
      control: 'text',
    },
    disabled: {
      control: 'boolean',
    },
  },
} satisfies Meta<typeof Button>;

권장 스토리 구조

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

// 1. Meta 정의
const meta = {
  title: 'Components/Button', // 사이드바 계층 구조
  component: Button,
  tags: ['autodocs'],
  parameters: {
    layout: 'centered',
  },
  args: {
    size: 'md',
    variant: 'primary',
  },
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'tertiary'],
    },
  },
} satisfies Meta<typeof Button>;

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

// 2. 기본 스토리
export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Button',
  },
};

// 3. 변형 스토리들
export const Secondary: Story = {
  args: {
    ...Primary.args,
    variant: 'secondary',
  },
};

export const Disabled: Story = {
  args: {
    ...Primary.args,
    disabled: true,
  },
};

// 4. 복잡한 상태나 컨텍스트가 필요한 경우
export const WithCustomTheme: Story = {
  args: Primary.args,
  decorators: [
    (Story) => (
      <ThemeProvider theme="custom">
        <Story />
      </ThemeProvider>
    ),
  ],
};

자주 사용되는 ArgTypes 옵션

참고: 기본값은 argTypes.defaultValue가 아닌 **args**에서 선언

argTypes: {
  // Select dropdown
  variant: {
    control: 'select',
    options: ['primary', 'secondary'],
  },

  // Radio buttons
  size: {
    control: 'radio',
    options: ['sm', 'md', 'lg'],
  },

  // Boolean toggle
  disabled: {
    control: 'boolean',
  },

  // Text input
  label: {
    control: 'text',
  },

  // Number input
  count: {
    control: 'number',
  },

  // Range slider
  opacity: {
    control: { type: 'range', min: 0, max: 1, step: 0.1 },
  },

  // Color picker
  backgroundColor: {
    control: 'color',
  },

  // Date picker
  date: {
    control: 'date',
  },

  // Action logger (이벤트 핸들러)
  onClick: {
    action: 'clicked',
  },

  // Control 비활성화
  className: {
    control: false,
  },
}

자주 사용되는 Parameters

parameters: {
  // 레이아웃 설정
  layout: 'centered' | 'fullscreen' | 'padded',

  // 배경 설정
  backgrounds: {
    default: 'light',
    values: [
      { name: 'light', value: '#ffffff' },
      { name: 'dark', value: '#333333' },
    ],
  },

  // Actions 패널 설정
  actions: {
    argTypesRegex: '^on[A-Z].*', // on으로 시작하는 Props 자동 감지
  },

  // Docs 설정
  docs: {
    description: {
      component: '버튼 컴포넌트 상세 설명',
    },
  },
}

Decorators 패턴

// 1. 스타일 래퍼
(Story) => (
  <div style={{ padding: '3rem' }}>
    <Story />
  </div>
)

// 2. Theme Provider
(Story) => (
  <ThemeProvider theme="dark">
    <Story />
  </ThemeProvider>
)

// 3. Router Provider (React Router 사용 시)
(Story) => (
  <MemoryRouter initialEntries={['/']}>
    <Story />
  </MemoryRouter>
)

// 4. 다국어 Provider
(Story) => (
  <I18nProvider locale="ko">
    <Story />
  </I18nProvider>
)

// 5. 전역 상태 Provider
(Story) => (
  <Provider store={mockStore}>
    <Story />
  </Provider>
)

파일 명명 규칙

Component.tsx           # 컴포넌트 구현
Component.stories.tsx   # 스토리 파일 (같은 디렉토리)
Component.test.tsx      # 테스트 파일

Storybook 설정 파일

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

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  addons: [
    '@storybook/addon-essentials', // Controls, Actions, Docs 등
    '@storybook/addon-interactions', // Play functions
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
};

export default config;
// .storybook/preview.ts
import type { Preview } from '@storybook/react';

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
  // 모든 스토리에 적용될 전역 Decorators
  decorators: [
    (Story) => (
      <div style={{ fontFamily: 'Arial, sans-serif' }}>
        <Story />
      </div>
    ),
  ],
};

export default preview;

You Might Also Like

Related Skills

verify

verify

243K

Use when you want to validate changes before committing, or when you need to check all React contribution requirements.

facebook avatarfacebook
獲取
test

test

243K

Use when you need to run tests for React core. Supports source, www, stable, and experimental channels.

facebook avatarfacebook
獲取

Use when feature flag tests fail, flags need updating, understanding @gate pragmas, debugging channel-specific test failures, or adding new flags to React.

facebook avatarfacebook
獲取

Use when adding new error messages to React, or seeing "unknown error code" warnings.

facebook avatarfacebook
獲取
flow

flow

243K

Use when you need to run Flow type checking, or when seeing Flow type errors in React code.

facebook avatarfacebook
獲取
flags

flags

243K

Use when you need to check feature flag states, compare channels, or debug why a feature behaves differently across release channels.

facebook avatarfacebook
獲取