# 자동 설치 (권장) npx storybook@latest init # 수동 설치 npm install --save-dev @storybook/react @storybook/react-vite npm install --save-dev @storybook/addon-essentials npm install --save-dev @storybook/addon-interactions npm install --save-dev @storybook/addon-links npm install --save-dev @storybook/blocks
.storybook/
main.ts # Storybook 설정
preview.ts # 전역 데코레이터
src/
components/
Button/
Button.tsx
Button.stories.tsx # Story 파일
Card/
Card.tsx
Card.stories.tsximport type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: '@storybook/react-vite',
};
export default config;// src/components/Button/Button.tsx
import React from 'react';
export interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'medium',
disabled = false,
children,
onClick,
}) => {
const baseStyles = 'rounded font-semibold transition-colors';
const variantStyles = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
danger: 'bg-red-600 hover:bg-red-700 text-white',
};
const sizeStyles = {
small: 'px-3 py-1 text-sm',
medium: 'px-4 py-2 text-base',
large: 'px-6 py-3 text-lg',
};
return (
<button
className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};// src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
// Meta 정보 정의
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
description: '버튼 스타일 변형',
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
description: '버튼 크기',
},
disabled: {
control: 'boolean',
description: '비활성화 상태',
},
onClick: { action: 'clicked' },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
// Story 정의
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Primary Button',
},
};
export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Secondary Button',
},
};
export const Danger: Story = {
args: {
variant: 'danger',
children: 'Danger Button',
},
};
export const Small: Story = {
args: {
size: 'small',
children: 'Small Button',
},
};
export const Large: Story = {
args: {
size: 'large',
children: 'Large Button',
},
};
export const Disabled: Story = {
args: {
disabled: true,
children: 'Disabled Button',
},
};# 개발 모드 npm run storybook # 빌드 npm run build-storybook
"scripts": {
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}