Storybook 완벽 가이드 | UI 컴포넌트 개발·문서화·테스트·실전 활용
이 글의 핵심
Storybook으로 UI 컴포넌트를 개발하고 문서화하는 완벽 가이드입니다. Stories, Args, Actions, Addons, Visual Testing까지 실전 예제로 정리했습니다.
실무 경험 공유: Storybook을 도입하면서, 컴포넌트 개발 속도가 50% 향상되고 디자이너와의 협업이 원활해진 경험을 공유합니다.
들어가며: “컴포넌트 개발이 비효율적이에요”
실무 문제 시나리오
시나리오 1: 컴포넌트를 격리해서 개발하기 어려워요
전체 앱을 실행해야 합니다. Storybook은 격리된 환경을 제공합니다.
시나리오 2: 모든 상태를 테스트하기 어려워요
수동으로 상태를 만들어야 합니다. Storybook은 모든 상태를 Story로 정의합니다.
시나리오 3: 문서화가 번거로워요
별도로 문서를 작성해야 합니다. Storybook은 자동으로 문서화합니다.
1. Storybook이란?
핵심 특징
Storybook은 UI 컴포넌트 개발 도구입니다.
주요 장점:
- 격리된 개발: 컴포넌트만 개발
- 모든 상태: Stories로 정의
- 자동 문서화: Props, Events
- Addons: 수백 개의 확장
- Visual Testing: 시각적 회귀 테스트
2. 설치 및 설정
React 프로젝트
npx storybook@latest init
수동 설치
npm install -D @storybook/react @storybook/react-vite storybook
.storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
};
export default config;
3. 첫 Story 작성
Button 컴포넌트
// src/components/Button.tsx
interface ButtonProps {
label: string;
variant?: 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
onClick?: () => void;
}
export default function Button({ label, variant = 'primary', size = 'medium', onClick }: ButtonProps) {
return (
<button className={`btn btn-${variant} btn-${size}`} onClick={onClick}>
{label}
</button>
);
}
Story
// src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import Button from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary'],
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
label: 'Primary Button',
variant: 'primary',
},
};
export const Secondary: Story = {
args: {
label: 'Secondary Button',
variant: 'secondary',
},
};
export const Small: Story = {
args: {
label: 'Small Button',
size: 'small',
},
};
export const Large: Story = {
args: {
label: 'Large Button',
size: 'large',
},
};
4. Args & Actions
Args
export const Interactive: Story = {
args: {
label: 'Click me',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
await userEvent.click(button);
},
};
Actions
import { action } from '@storybook/addon-actions';
export const WithAction: Story = {
args: {
label: 'Click me',
onClick: action('button-clicked'),
},
};
5. Decorators
전역 Decorator
// .storybook/preview.tsx
import type { Preview } from '@storybook/react';
import '../src/index.css';
const preview: Preview = {
decorators: [
(Story) => (
<div style={{ padding: '3rem' }}>
<Story />
</div>
),
],
};
export default preview;
Story별 Decorator
export const Centered: Story = {
args: {
label: 'Centered Button',
},
decorators: [
(Story) => (
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Story />
</div>
),
],
};
6. Parameters
export const DarkMode: Story = {
args: {
label: 'Dark Button',
},
parameters: {
backgrounds: {
default: 'dark',
},
},
};
7. Addons
인기 Addons
npm install -D @storybook/addon-a11y
npm install -D @storybook/addon-viewport
npm install -D @storybook/addon-storysource
.storybook/main.ts
const config: StorybookConfig = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y',
'@storybook/addon-viewport',
'@storybook/addon-storysource',
],
};
8. Vue 통합
설치
npx storybook@latest init
Story
// src/components/Button.stories.ts
import type { Meta, StoryObj } from '@storybook/vue3';
import Button from './Button.vue';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
label: 'Primary Button',
variant: 'primary',
},
};
9. Visual Testing
Chromatic
npm install -D chromatic
npx chromatic --project-token=YOUR_TOKEN
Storybook Test Runner
npm install -D @storybook/test-runner
{
"scripts": {
"test-storybook": "test-storybook"
}
}
10. 실전 예제: Form 컴포넌트
Input 컴포넌트
// src/components/Input.tsx
interface InputProps {
label: string;
type?: 'text' | 'email' | 'password';
placeholder?: string;
error?: string;
value?: string;
onChange?: (value: string) => void;
}
export default function Input({ label, type = 'text', placeholder, error, value, onChange }: InputProps) {
return (
<div className="input-group">
<label>{label}</label>
<input
type={type}
placeholder={placeholder}
value={value}
onChange={(e) => onChange?.(e.target.value)}
className={error ? 'error' : ''}
/>
{error && <span className="error-message">{error}</span>}
</div>
);
}
Story
// src/components/Input.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import Input from './Input';
const meta: Meta<typeof Input> = {
title: 'Components/Input',
component: Input,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof Input>;
export const Default: Story = {
args: {
label: 'Email',
type: 'email',
placeholder: '[email protected]',
},
};
export const WithError: Story = {
args: {
label: 'Email',
type: 'email',
placeholder: '[email protected]',
error: 'Invalid email address',
},
};
export const Password: Story = {
args: {
label: 'Password',
type: 'password',
placeholder: 'Enter password',
},
};
정리 및 체크리스트
핵심 요약
- Storybook: UI 컴포넌트 개발 도구
- 격리된 개발: 컴포넌트만 개발
- Stories: 모든 상태 정의
- 자동 문서화: Props, Events
- Addons: 수백 개의 확장
- Visual Testing: 시각적 회귀 테스트
구현 체크리스트
- Storybook 설치
- 첫 Story 작성
- Args 활용
- Actions 추가
- Decorators 사용
- Addons 설치
- Visual Testing 설정
- 문서화
같이 보면 좋은 글
- React 완벽 가이드
- Playwright 완벽 가이드
- shadcn/ui 완벽 가이드
이 글에서 다루는 키워드
Storybook, UI, Component, Documentation, Testing, React, Vue
자주 묻는 질문 (FAQ)
Q. 프로덕션 빌드에 포함되나요?
A. 아니요, 개발 도구로만 사용됩니다.
Q. 디자이너와 공유할 수 있나요?
A. 네, Storybook을 정적 사이트로 배포할 수 있습니다.
Q. 테스트 도구인가요?
A. 개발 도구이지만, Visual Testing도 지원합니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, 수많은 대기업에서 사용하고 있습니다.