storybook

安装量: 312
排名: #2962

安装

npx skills add https://github.com/dalestudy/skills --skill storybook

Storybook 모범 관례 1. CSF 3.0 형식 사용

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

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

export const Primary = () => ;

// ✅ 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;

export default meta; type Story = StoryObj;

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

  1. Args 기반 스토리 작성

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

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

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

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

  1. 타입 안전한 Meta 정의

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

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

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

export default meta; type Story = StoryObj;

  1. Decorators로 컨텍스트 제공

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

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

// 모든 스토리에 Decorator 적용 const meta = { component: Button, decorators: [ (Story) => (

), ], } satisfies Meta;

  1. Parameters로 동작 커스터마이즈 const meta = { component: Button, parameters: { layout: 'centered', // 스토리를 중앙 정렬 backgrounds: { default: 'light', values: [ { name: 'light', value: '#ffffff' }, { name: 'dark', value: '#000000' }, ], }, }, } satisfies Meta;

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

  1. 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;

권장 스토리 구조 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', }, argTypes: { variant: { control: 'select', options: ['primary', 'secondary', 'tertiary'], }, }, } satisfies Meta;

export default meta; type Story = StoryObj;

// 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) => ( ), ], };

자주 사용되는 ArgTypes 옵션 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) => (

)

// 2. Theme Provider (Story) => ( )

// 3. Router Provider (React Router 사용 시) (Story) => ( )

// 4. 다국어 Provider (Story) => ( )

// 5. 전역 상태 Provider (Story) => ( )

파일 명명 규칙 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) => (

), ], };

export default preview;

返回排行榜