6.3 KiB
6.3 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| frontend-storybook-modern | Storybook 9 — component dev / test / docs | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Storybook Modern (9)
Component 의 isolated dev + test + docs. CSF 3 (component story format), MDX, Chromatic visual test.
📖 핵심 개념
- 매 component 가 isolated story.
- Variant (default, error, loading) 가 visible.
- Visual test (Chromatic).
- Design system 친화.
💻 코드 패턴
Setup
npx storybook@latest init
CSF 3 (modern)
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
component: Button,
args: { children: 'Click me' },
argTypes: {
variant: { control: 'select', options: ['primary', 'secondary'] },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: { variant: 'primary' },
};
export const Secondary: Story = {
args: { variant: 'secondary' },
};
export const Disabled: Story = {
args: { disabled: true },
};
→ Args + render = story.
Interaction (play function)
import { userEvent, within, expect } from '@storybook/test';
export const ClickAction: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const btn = canvas.getByRole('button');
await userEvent.click(btn);
await expect(btn).toHaveTextContent('Clicked');
},
};
→ Story 가 test 도. Storybook UI 에서 step replay.
MDX (docs + story)
import { Meta, Story } from '@storybook/blocks';
import { Button } from './Button';
<Meta title='Button' component={Button} />
# Button
A primary button component.
## Variants
<Canvas>
<Story name='Primary'>
<Button variant='primary'>Primary</Button>
</Story>
</Canvas>
## API
<ArgTypes />
→ Markdown + component embed.
Chromatic (visual regression)
npm i -D chromatic
npx chromatic --project-token=...
→ 매 PR 이 visual diff. Approve / reject.
Test runner
npm i -D @storybook/test-runner
npx test-storybook
→ 매 story 의 play 가 test. Headless run.
Mock (MSW)
import { http, HttpResponse } from 'msw';
export const Loaded: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', () => HttpResponse.json([{ name: 'Alice' }])),
],
},
},
};
→ Real-like data.
Theme / decorator
const meta: Meta = {
decorators: [
(Story) => (
<ThemeProvider theme={theme}>
<Story />
</ThemeProvider>
),
],
};
Args + controls
const meta: Meta<typeof Avatar> = {
argTypes: {
src: { control: 'text' },
size: { control: { type: 'range', min: 16, max: 128, step: 8 } },
variant: { control: 'select', options: ['circle', 'square'] },
},
};
→ Storybook UI 에서 live tweak.
Auto-docs
const meta: Meta = {
tags: ['autodocs'],
};
→ Docs tab 자동 생성 (props + variant).
Vite
// .storybook/main.ts
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx|mdx)'],
framework: { name: '@storybook/react-vite', options: {} },
addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'],
};
→ Vite 친화 (빠른).
a11y addon
npm i -D @storybook/addon-a11y
→ 매 story 가 axe 검증.
Build (deploy)
npm run build-storybook
# → static-storybook/ 에 deploy.
→ Vercel / Netlify / GitHub Pages.
Use case
- Design system (Bento, Radix UI examples).
- Component library showcase.
- Cross-team review.
- QA reproduction.
- Visual regression.
- Onboarding (이 component 가 무엇).
vs Histoire / Ladle / React Cosmos
Histoire: Vue 친화.
Ladle: lighter, faster.
React Cosmos: legacy.
Storybook: 가장 mature.
→ Storybook 가 default.
Performance
Storybook 9: 빠름 (Vite default).
큰 codebase: 1000+ story 가 OK.
→ 매 story file 가 lazy load.
Production tips
- 매 component 의 매 variant.
- Edge case (loading, error, empty).
- Long content (truncation).
- Different size / theme.
- A11y test 항상.
- Visual diff CI gate.
CI integration
- run: npm run build-storybook
- run: npx test-storybook --url http://localhost:6006
- run: npx chromatic
Composition (multi-storybook)
// .storybook/main.ts
const config = {
refs: {
'design-system': { url: 'https://design.example.com' },
'component-library': { url: 'https://components.example.com' },
},
};
→ 다른 storybook 가 1 navigation.
Args from URL
?args=variant:primary;size:lg
→ Bookmark / share.
Custom addon
// addon-my-feature/manager.ts
import { addons, types } from '@storybook/manager-api';
addons.register('my-addon', () => {
addons.add('my-addon/panel', {
type: types.PANEL,
title: 'My Panel',
render: ({ active }) => <div>Custom UI</div>,
});
});
LLM 도움
- Story generation (component → variant).
- Visual diff explanation.
- A11y violation fix.
함정
- 모든 story 가 매 변경 = Chromatic cost.
- Story 가 stale (component 변경 후 안 update).
- Decorator 가 prod 가 다른 (theme).
- Args 가 너무 많음 = noisy.
- Build 가 slow (큰 codebase).
🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| Component dev | Storybook |
| Visual test | Chromatic + Storybook |
| Design system | Storybook + composition |
| Vue | Histoire / Storybook |
| 작은 / 빠름 | Ladle |
| Documentation | MDX + autodocs |
❌ 안티패턴
- Story 안 maintain: stale.
- No edge case: production 가 first 진실.
- Storybook + Cypress 둘 다 component test: redundant.
- No visual diff: silent regression.
- No a11y test: accessibility 깨짐.
🤖 LLM 활용 힌트
- CSF 3 가 modern.
- Chromatic + Storybook 가 visual test.
- Play function 가 interaction test.
- a11y addon 항상.