Storybook은 TypeScript 타입과 JSDoc 주석을 분석하여 Props 테이블, 설명, 예제를 포함한 문서를 자동으로 생성합니다.
타입 정보 기반
코드와 동기화
한 번 설정으로 완료
const meta: Meta<typeof Button> = {
title: 'UI/Button',
component: Button,
tags: ['autodocs'], // 👈 이것만 추가!
};
export default meta;export const parameters = {
docs: {
autodocs: 'tag', // 또는 true
},
};// Button.tsx
export interface ButtonProps {
/**
* 버튼에 표시될 텍스트
*/
children: React.ReactNode;
/**
* 버튼의 시각적 스타일
* @default 'primary'
*/
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
/**
* 버튼의 크기
* @default 'medium'
*/
size?: 'small' | 'medium' | 'large';
/**
* 비활성화 상태 여부
* 비활성화되면 클릭할 수 없고 시각적으로 구분됩니다.
* @default false
*/
disabled?: boolean;
/**
* 전체 너비를 차지할지 여부
* @default false
*/
fullWidth?: boolean;
/**
* 로딩 중 표시 여부
* 로딩 중에는 자동으로 disabled 상태가 됩니다.
* @default false
*/
loading?: boolean;
/**
* 아이콘 (왼쪽)
* @example <Icon name="plus" />
*/
leftIcon?: React.ReactNode;
/**
* 아이콘 (오른쪽)
*/
rightIcon?: React.ReactNode;
/**
* 클릭 이벤트 핸들러
*/
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
/**
* 버튼 타입
* @default 'button'
*/
type?: 'button' | 'submit' | 'reset';
/**
* ARIA 레이블
* 접근성을 위해 명확한 설명 제공
*/
'aria-label'?: string;
/**
* 테스트 ID
* @internal
*/
testId?: string;
}
export const Button: React.FC<ButtonProps> = ({
children,
variant = 'primary',
size = 'medium',
disabled = false,
fullWidth = false,
loading = false,
leftIcon,
rightIcon,
onClick,
type = 'button',
'aria-label': ariaLabel,
testId,
}) => {
// 구현...
};// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
/**
* Button 컴포넌트는 사용자 액션을 트리거하는 기본 UI 요소입니다.
*
* ## 사용 가이드
* - Primary: 주요 액션 (저장, 제출 등)
* - Secondary: 보조 액션 (취소, 뒤로 등)
* - Danger: 위험한 액션 (삭제, 초기화 등)
* - Ghost: 최소한의 시각적 강조
*
* ## 접근성
* - 키보드 탐색 지원 (Tab, Enter, Space)
* - ARIA 레이블 지원
* - 색상 대비 WCAG AA 준수
*/
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'], // 자동 문서화 활성화
parameters: {
docs: {
description: {
component: '이 컴포넌트는 모든 플랫폼에서 일관된 버튼 경험을 제공합니다.',
},
},
},
argTypes: {
variant: {
control: 'select',
description: '버튼 스타일 변형',
table: {
type: { summary: "'primary' | 'secondary' | 'danger' | 'ghost'" },
defaultValue: { summary: 'primary' },
},
},
size: {
control: 'radio',
options: ['small', 'medium', 'large'],
},
disabled: {
control: 'boolean',
},
loading: {
control: 'boolean',
description: '로딩 상태일 때 spinner가 표시됩니다.',
},
onClick: {
action: 'clicked',
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
/**
* 기본 Primary 버튼입니다.
* 가장 중요한 액션에 사용하세요.
*/
export const Primary: Story = {
args: {
children: 'Primary Button',
variant: 'primary',
},
};
/**
* Secondary 버튼은 보조 액션에 적합합니다.
*/
export const Secondary: Story = {
args: {
children: 'Secondary Button',
variant: 'secondary',
},
};
/**
* 삭제나 초기화 같은 위험한 액션에 사용합니다.
*/
export const Danger: Story = {
args: {
children: 'Delete',
variant: 'danger',
},
parameters: {
docs: {
description: {
story: '사용자에게 경고 메시지를 먼저 표시하는 것이 좋습니다.',
},
},
},
};
/**
* 아이콘과 함께 사용하는 예제
*/
export const WithIcons: Story = {
args: {
children: 'Add Item',
leftIcon: <span>➕</span>,
},
};
/**
* 비동기 작업 중 로딩 상태
*/
export const Loading: Story = {
args: {
children: 'Loading...',
loading: true,
},
};@default기본값 명시
@deprecated더 이상 사용 안 함
@example사용 예제
@internal내부 전용 (문서 제외)
@see참고 링크
// .storybook/preview.ts
import { DocsContainer } from '@storybook/blocks';
export const parameters = {
docs: {
// 테마
theme: themes.dark,
// 커스텀 컨테이너
container: DocsContainer,
// Props 테이블 설정
extractArgTypes: (component) => {
// 커스텀 로직
},
// 소스 코드 포맷
source: {
language: 'tsx',
format: true,
},
// 캔버스 설정
canvas: {
sourceState: 'shown', // 기본으로 표시
},
},
};
// Story별 커스터마이징
export const Advanced: Story = {
parameters: {
docs: {
description: {
story: '고급 사용 예제',
},
source: {
code: `
<Button
variant="primary"
size="large"
onClick={handleClick}
>
Custom Code
</Button>
`,
},
},
},
};// 유니온 타입
export type ButtonVariant =
| 'primary'
| 'secondary'
| 'danger'
| 'ghost';
// 제네릭 타입
export interface SelectProps<T = string> {
/**
* 선택 가능한 옵션들
*/
options: Array<{
/** 옵션 값 */
value: T;
/** 표시될 레이블 */
label: string;
/** 비활성화 여부 */
disabled?: boolean;
}>;
/**
* 현재 선택된 값
*/
value?: T;
/**
* 값 변경 핸들러
*/
onChange?: (value: T) => void;
}
// 객체 타입
export interface Theme {
/**
* 색상 팔레트
*/
colors: {
/** 주요 색상 */
primary: string;
/** 보조 색상 */
secondary: string;
/** 배경색 */
background: {
/** 기본 배경 */
default: string;
/** 강조 배경 */
paper: string;
};
};
/**
* 타이포그래피 설정
*/
typography: {
fontFamily: string;
fontSize: {
small: string;
medium: string;
large: string;
};
};
}
// 함수 타입
export type OnSubmitHandler = (
/** 폼 데이터 */
data: FormData,
/** 이벤트 객체 */
event: React.FormEvent
) => void | Promise<void>;타입만 정의
+ JSDoc 주석
+ 예제 + 가이드
JSDoc 주석은 IDE에서도 표시되어 개발 경험 향상
TypeScript로 타입 안정성과 자동 문서화 동시 달성
코드 변경 시 문서도 자동 업데이트
디자이너, PM도 쉽게 이해할 수 있는 문서