Storybook Addons

핵심 애드온을 활용하여 Storybook의 기능을 확장합니다.

← Storybook 가이드로 돌아가기

🔌 Addons란?

Storybook Addons는 개발자 경험을 향상시키는 플러그인입니다. 테스트, 문서화, 디버깅 등 다양한 기능을 제공합니다.

Essentials 번들:가장 많이 사용되는 6개 애드온이 기본 포함

📄Docs Addon

컴포넌트 문서를 자동 생성하고 MDX 지원

// .storybook/main.ts
addons: [
  '@storybook/addon-essentials', // Docs 포함
]

// Story에서 autodocs 활성화
const meta: Meta<typeof Button> = {
  title: 'UI/Button',
  component: Button,
  tags: ['autodocs'], // 자동 문서화
};
기능: Props 테이블, Description, 코드 스니펫

Actions Addon

이벤트 핸들러 호출을 Actions 패널에 로깅

// Story 정의
export const Default: Story = {
  args: {
    onClick: action('button-clicked'),
    onHover: action('button-hovered'),
  },
};

// 자동 액션 (naming convention)
const meta: Meta = {
  argTypes: {
    onClick: { action: 'clicked' },
    onChange: { action: 'changed' },
  },
};
활용: 이벤트 디버깅, 콜백 테스트

📱Viewport Addon

다양한 디바이스 크기에서 컴포넌트 테스트

// .storybook/preview.ts
export const parameters = {
  viewport: {
    viewports: {
      mobile: {
        name: 'Mobile',
        styles: { width: '375px', height: '667px' },
      },
      tablet: {
        name: 'Tablet',
        styles: { width: '768px', height: '1024px' },
      },
      desktop: {
        name: 'Desktop',
        styles: { width: '1920px', height: '1080px' },
      },
    },
  },
};
프리셋: iPhone, iPad, Galaxy 등

🎨Backgrounds Addon

다양한 배경색에서 컴포넌트 확인

// .storybook/preview.ts
export const parameters = {
  backgrounds: {
    default: 'light',
    values: [
      { name: 'light', value: '#ffffff' },
      { name: 'dark', value: '#1a1a1a' },
      { name: 'blue', value: '#3b82f6' },
    ],
  },
};

// Story별 설정
export const OnDark: Story = {
  parameters: {
    backgrounds: { default: 'dark' },
  },
};
용도: 다크모드 테스트, 대비 확인

🎭Interactions Addon

Testing Library로 사용자 인터랙션 시뮬레이션

import { userEvent, within } from '@storybook/test';

export const FilledForm: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    
    // 입력 필드 찾기
    const input = canvas.getByRole('textbox');
    const button = canvas.getByRole('button');
    
    // 사용자 인터랙션 시뮬레이션
    await userEvent.type(input, 'Hello World');
    await userEvent.click(button);
    
    // 결과 확인
    await expect(canvas.getByText('Success')).toBeInTheDocument();
  },
};
장점: 자동 E2E 테스트, 디버깅

A11y Addon

WCAG 접근성 기준 자동 검사

// 설치
npm install --save-dev @storybook/addon-a11y

// .storybook/main.ts
addons: ['@storybook/addon-a11y']

// .storybook/preview.ts
export const parameters = {
  a11y: {
    config: {
      rules: [
        { id: 'color-contrast', enabled: true },
        { id: 'label', enabled: true },
      ],
    },
  },
};
체크: 색상 대비, ARIA, 키보드 탐색

🎯 실전 예제: Form 컴포넌트

// LoginForm.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within, expect } from '@storybook/test';
import { LoginForm } from './LoginForm';

const meta: Meta<typeof LoginForm> = {
  title: 'Forms/LoginForm',
  component: LoginForm,
  tags: ['autodocs'],
  parameters: {
    // Backgrounds 설정
    backgrounds: {
      default: 'light',
      values: [
        { name: 'light', value: '#f5f5f5' },
        { name: 'dark', value: '#1a1a1a' },
      ],
    },
    // Viewport 설정
    viewport: {
      defaultViewport: 'mobile1',
    },
    // A11y 설정
    a11y: {
      config: {
        rules: [
          { id: 'label', enabled: true },
          { id: 'color-contrast', enabled: true },
        ],
      },
    },
  },
  argTypes: {
    // Actions 설정
    onSubmit: { action: 'form-submitted' },
    onError: { action: 'validation-error' },
  },
};

export default meta;
type Story = StoryObj<typeof LoginForm>;

// 기본 Story
export const Default: Story = {};

// Interactions로 자동 입력
export const FilledForm: Story = {
  play: async ({ canvasElement, args }) => {
    const canvas = within(canvasElement);
    
    // 필드 찾기
    const emailInput = canvas.getByLabelText('Email');
    const passwordInput = canvas.getByLabelText('Password');
    const submitButton = canvas.getByRole('button', { name: /submit/i });
    
    // 입력 시뮬레이션
    await userEvent.type(emailInput, 'user@example.com');
    await userEvent.type(passwordInput, 'password123');
    
    // 제출
    await userEvent.click(submitButton);
    
    // 검증
    await expect(args.onSubmit).toHaveBeenCalledWith({
      email: 'user@example.com',
      password: 'password123',
    });
  },
};

// 에러 상태
export const WithErrors: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    
    // 빈 상태로 제출
    const submitButton = canvas.getByRole('button');
    await userEvent.click(submitButton);
    
    // 에러 메시지 확인
    await expect(canvas.getByText(/email is required/i)).toBeInTheDocument();
  },
};

// 다크모드
export const DarkMode: Story = {
  parameters: {
    backgrounds: { default: 'dark' },
  },
};

// 모바일 뷰
export const MobileView: Story = {
  parameters: {
    viewport: {
      defaultViewport: 'mobile1',
    },
  },
};

🔧 유용한 추가 Addons

  • Pseudo States: hover, focus 상태
  • Design Assets: Figma 연동
  • Measure: 픽셀 측정
  • Outline: 레이아웃 디버깅
  • Links: Story 간 네비게이션

✅ Best Practices

  • ✓ Essentials 번들 기본 사용
  • ✓ 팀에 필요한 것만 추가
  • ✓ Interactions로 핵심 시나리오 테스트
  • ✓ A11y 체크 자동화
  • ✓ Actions로 이벤트 검증

⚠️ 주의사항

  • • 너무 많은 애드온 → 느려짐
  • • play 함수는 비동기 처리
  • • 버전 호환성 확인
  • • CI/CD에서 테스트 실행
  • • 커스텀 애드온은 신중히

🚀 Addon 설치 & 설정

설치

npm install --save-dev @storybook/addon-a11y
npx storybook add @storybook/addon-a11y

등록

// .storybook/main.ts
addons: [
  '@storybook/addon-a11y',
]