[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
---
|
||||
id: frontend-storybook-modern
|
||||
title: Storybook 9 — component dev / test / docs
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [frontend, storybook, vibe-coding]
|
||||
tech_stack: { language: "TS", applicable_to: ["Frontend"] }
|
||||
applied_in: []
|
||||
aliases: [Storybook 9, CSF 3, MDX, Chromatic, visual test, component library, design system]
|
||||
---
|
||||
|
||||
# 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
|
||||
```bash
|
||||
npx storybook@latest init
|
||||
```
|
||||
|
||||
### CSF 3 (modern)
|
||||
```tsx
|
||||
// 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)
|
||||
```tsx
|
||||
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)
|
||||
```mdx
|
||||
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)
|
||||
```bash
|
||||
npm i -D chromatic
|
||||
npx chromatic --project-token=...
|
||||
```
|
||||
|
||||
→ 매 PR 이 visual diff. Approve / reject.
|
||||
|
||||
### Test runner
|
||||
```bash
|
||||
npm i -D @storybook/test-runner
|
||||
npx test-storybook
|
||||
```
|
||||
|
||||
→ 매 story 의 play 가 test.
|
||||
Headless run.
|
||||
|
||||
### Mock (MSW)
|
||||
```tsx
|
||||
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
|
||||
```tsx
|
||||
const meta: Meta = {
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Story />
|
||||
</ThemeProvider>
|
||||
),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
### Args + controls
|
||||
```tsx
|
||||
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
|
||||
```tsx
|
||||
const meta: Meta = {
|
||||
tags: ['autodocs'],
|
||||
};
|
||||
```
|
||||
|
||||
→ `Docs` tab 자동 생성 (props + variant).
|
||||
|
||||
### Vite
|
||||
```js
|
||||
// .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
|
||||
```bash
|
||||
npm i -D @storybook/addon-a11y
|
||||
```
|
||||
|
||||
→ 매 story 가 axe 검증.
|
||||
|
||||
### Build (deploy)
|
||||
```bash
|
||||
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
|
||||
```yaml
|
||||
- run: npm run build-storybook
|
||||
- run: npx test-storybook --url http://localhost:6006
|
||||
- run: npx chromatic
|
||||
```
|
||||
|
||||
### Composition (multi-storybook)
|
||||
```ts
|
||||
// .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
|
||||
```ts
|
||||
// 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 항상.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Testing_Visual_Regression]]
|
||||
- [[Frontend_Design_Tokens]]
|
||||
- [[Frontend_shadcn_Radix_Patterns]]
|
||||
Reference in New Issue
Block a user