Essentials/Controls

김동현·2026년 3월 22일

컨트롤 (Controls)

React | Vue | Angular | Web Components | 그 외(More)

스토리북의 컨트롤(Controls) 패널은 코드를 직접 수정할 필요 없이, 컴포넌트의 인자(arguments, 줄여서 args)들을 시각적인 UI를 통해 동적으로 조작해 볼 수 있게 해주는 기능입니다. Controls 패널에서 스토리의 입력값들을 이리저리 바꿔가며 실시간으로 변하는 컴포넌트의 모습을 확인해 보세요. 컴포넌트의 다양한 상태를 탐색하고 테스트하기에 이보다 더 좋은 방법은 없답니다!

비디오: 애드온 컨트롤 데모 (Addon Controls Demo)

컨트롤 기능을 사용하기 위해 컴포넌트 코드를 수정할 필요는 전혀 없습니다. 컨트롤을 위한 스토리는 이런 장점들이 있어요:

  • 편리함 (Convenient): React, Vue, Angular 등 다양한 프레임워크의 컴포넌트들을 바탕으로 컨트롤 UI를 알아서 척척 만들어냅니다.
  • 재사용성 (Portable): 이렇게 만든 인터랙티브한 스토리들은 공식 문서, 테스트, 심지어 디자인 도구 안에서도 요긴하게 재사용할 수 있습니다.
  • 풍부함 (Rich): 내 입맛에 딱 맞게 컨트롤의 형태나 인터랙티브한 데이터들을 직접 커스터마이징할 수 있습니다.

컨트롤 기능을 100% 활용하려면, args를 사용해서 스토리를 작성해야 합니다. 스토리북은 여러분이 작성한 args와 컴포넌트 내부 정보를 싹 분석해서 알맞은 UI 컨트롤을 자동으로 뿅! 하고 생성해 주거든요. 물론, 더 세밀한 조정이 필요하다면 아래에서 설명할 argTypes를 통해 컨트롤을 직접 설정할 수도 있습니다.

💡 스토리북 6.0 이전 버전의 옛날 방식으로 작성된 스토리가 있다면, args & controls 마이그레이션 가이드를 참고해서 요즘 스타일(args 사용)로 업그레이드해 보세요!

컨트롤 타입 선택하기 (Choosing the control type)

기본적으로 스토리북은 arg에 설정된 초기값(initial value)의 타입(예: boolean인지 string인지)을 보고 그에 맞는 컨트롤을 알아서 선택해 줍니다. 이 자동 완성 기능을 제대로 쓰려면, 스토리 파일의 meta(default export) 부분에 component 속성을 꼭 지정해 주셔야 해요. 그러면 스토리북이 react-docgen(TypeScript 지원이 빵빵한 React 컴포넌트 문서화 생성기) 같은 도구를 사용해서 컴포넌트의 정보를 쫙 뽑아낸 다음, 찰떡같은 컨트롤과 argTypes를 자동으로 만들어줍니다.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

이렇게 하면 variant 같은 속성에는 자유롭게 텍스트를 입력할 수 있는 텍스트 컨트롤이 기본으로 만들어집니다:

Control using a string

사실 자동 생성된 텍스트 컨트롤 창에 올바른 문자열만 잘 입력하면 작동하는 데는 문제가 없습니다. 하지만 컴포넌트의 variant 속성이 primary 아니면 secondary 두 가지 값만 받는다면, 그냥 텍스트를 치게 두는 것보단 라디오 버튼(radio) 그룹으로 콕 집어 선택하게 해주는 게 훨씬 직관적이고 좋겠죠? 한번 스토리북의 라디오 컴포넌트로 바꿔보겠습니다.

이럴 땐 특정 속성에 어떤 컨트롤을 쓸지 명시적으로 지정해 줄 수 있는데, 바로 variant 속성에 커스텀 argType을 선언해 주면 됩니다. ArgTypes는 이름, 설명, 기본값 같은 arg의 기본적인 메타데이터를 담고 있어요. (이 정보들은 나중에 스토리북 공식 문서(Docs) 페이지에 예쁘게 자동으로 들어갑니다.)

ArgTypes에는 사용자가 마음대로 설정을 바꿀 수 있는 추가적인 어노테이션(annotations)도 넣을 수 있어요. variant는 컴포넌트 전체에서 쓰이는 속성이니까, 스토리별로 달아주기보단 전체 설정인 meta(default export) 부분에 달아주는 게 낫겠네요.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  //👇 options와 type을 지정해서 특정 argTypes를 직접 만들어줍니다.
  argTypes: {
    variant: {
      options: ['primary', 'secondary'],
      control: { type: 'radio' },
    },
  },
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    variant: 'primary',
  },
};

💡 ArgTypes는 여러분의 스토리에 달릴 컨트롤들을 내 맘대로 주무를 수 있게 해주는 아주 강력한 무기입니다. 더 깊게 파고들고 싶으시다면 argTypes 어노테이션을 활용한 컨트롤 커스터마이징 문서를 꼭 읽어보세요.

짜잔! 텍스트 입력창이 훨씬 직관적이고 쓰기 편한 라디오 버튼 그룹으로 바뀌었습니다.

Control with a radio group

커스텀 컨트롤 타입 자동 매칭 (Custom control type matchers)

보통 컨트롤은 arg의 이름을 보고 정규 표현식(regex)을 이용해 알아서 적절한 타입을 찾아 매칭시켜 줍니다. 단, 현재 이 자동 매칭 기능은 색상 선택기(color picker)와 날짜 선택기(date picker) 컨트롤에만 적용되어 있어요. 스토리북 CLI를 사용해서 프로젝트를 처음 세팅하셨다면, .storybook/preview.js|ts 파일 안에 아래와 같은 기본 설정이 자동으로 들어가 있을 겁니다:

컨트롤 (Control)기본 정규 표현식 (Default regex)설명 (Description)
color/(background\|color)$/i이름이 이 패턴에 맞는 arg들에 대해 색상 선택기(color picker) UI를 보여줍니다.
date/Date$/이름이 이 패턴에 맞는 arg들에 대해 날짜 선택기(date picker) UI를 보여줍니다.

만약 CLI로 세팅하지 않으셨거나, 아니면 여러분만의 특별한 자동 매칭 패턴을 만들고 싶다면 controls 파라미터 안의 matchers 속성을 살짝 만져주시면 됩니다:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};
 
export default preview;

완전히 커스텀된 Args 사용하기 (Fully custom args)

지금까지는 우리가 스토리를 만들고 있는 해당 '컴포넌트'의 속성들만을 바탕으로 자동 생성된 컨트롤들을 다뤘습니다. 하지만 여러 컴포넌트가 얽혀있는 복잡한 스토리(complex stories)를 짤 때면, 정작 테스트 대상 컴포넌트의 속성은 아니지만 다른 부수적인 조작을 위해 컨트롤이 필요할 때가 있어요. 예를 들어, 자식 컴포넌트 안에 텍스트를 밀어 넣기 위해 footer라는 가상의 arg를 만들어서 사용하는 방법을 보여드릴게요:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Page } from './Page';
 
type PagePropsAndCustomArgs = React.ComponentProps<typeof Page> & { footer?: string };
 
const meta = {
  component: Page,
  render: ({ footer, ...args }) => (
    <Page {...args}>
      <footer>{footer}</footer>
    </Page>
  ),
} satisfies Meta<PagePropsAndCustomArgs>;
export default meta;
 
type Story = StoryObj<typeof meta>;
 
export const CustomFooter = {
  args: {
    footer: 'Built with Storybook',
  },
} satisfies Story;

스토리북은 다음과 같은 조건을 만족하는 arg가 있으면 기본적으로 알아서 컨트롤 창을 만들어 줍니다:

  • 해당 프레임워크가 지원한다면 컴포넌트의 정의(definition)를 보고 속성을 쏙쏙 뽑아내어 만듭니다.
  • 스토리에 정의된 args 목록에 그 이름이 떡하니 적혀있을 때 만듭니다.

이 상태에서 argTypes를 사용하면 방금 만들어진 각 컨트롤들의 생김새나 동작을 마음대로 주무를 수 있게 됩니다.

복잡한 값 다루기 (Dealing with complex values)

문자열이나 숫자 같은 기본(primitive) 값이 아닌 복잡한 객체나 함수 같은 값들을 다룰 때는 몇 가지 아쉬운 제한 사항들과 부딪히게 될 거예요.

가장 큰 문제는 모든 값을 URL의 args 파라미터로 깔끔하게 변환(serialize)할 수가 없다는 겁니다. 즉, 현재의 완벽한 상태를 복사해서 남들에게 공유하거나 특정 상태로 바로 진입하는 딥 링크(deep link) 기능을 쓸 수 없게 되죠. 더 나아가, JSX 덩어리 같은 아주 복잡한 값들은 매니저 영역(예: Controls 패널)과 프리뷰 영역(여러분이 보는 스토리 화면) 사이에서 동기화(synchronize)될 수 없습니다.

이 문제를 부드럽게 넘기는 한 가지 방법은, arg 값으로는 그냥 단순한 문자열(string)을 쓰고, 렌더링 직전에 이 단순한 문자열을 원본의 복잡한 값으로 변환해 주는 나만의 커스텀 render 함수를 하나 중간에 끼워 넣는 겁니다. 완벽하게 예쁜 방법은 아니지만(아래 예시 참고), 확실히 가장 유연하게 문제를 해결할 수 있는 방법이죠.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { YourComponent } from './your-component';
 
const meta = {
  component: YourComponent,
  //👇 특정 argTypes를 직접 만들고 옵션들을 줍니다.
  argTypes: {
    propertyA: {
      options: ['Item One', 'Item Two', 'Item Three'],
      control: { type: 'select' }, // 'options'가 있으니 사실 타입은 알아서 'select'로 추론됩니다.
    },
    propertyB: {
      options: ['Another Item One', 'Another Item Two', 'Another Item Three'],
    },
  },
} satisfies Meta<typeof YourComponent>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
const someFunction = (valuePropertyA, valuePropertyB) => {
  // 여기에 복잡한 로직을 넣으세요!
};
 
export const ExampleStory: Story = {
  render: (args) => {
    const { propertyA, propertyB } = args;
    //👇 복잡한 로직을 처리한 결과값을 변수에 예쁘게 담아줍니다.
    const someFunctionResult = someFunction(propertyA, propertyB);
 
    return <YourComponent {...args} someProperty={someFunctionResult} />;
  },
  args: {
    propertyA: 'Item One',
    propertyB: 'Another Item One',
  },
};

만약 방금처럼 복잡한 로직을 가진 함수를 꼭 써야만 하는 상황이 아니라면, 단순한 원시값(primitive)을 복잡한 값으로 화면에 렌더링 되기 전에 매핑(mapping)해주는 아주 간편한 방법이 있습니다. 바로 mapping 속성을 정의하는 거죠! 게다가 control.labels를 같이 설정하면 체크박스, 라디오, 셀렉트 입력 창에 나오는 글자(label)들을 내 맘대로 예쁘게 꾸밀 수도 있어요.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from './icons';
 
const arrows = { ArrowUp, ArrowDown, ArrowLeft, ArrowRight };
 
const meta = {
  component: Button,
  argTypes: {
    arrow: {
      options: Object.keys(arrows), // 직렬화(serializable) 가능한 이름값들의 배열
      mapping: arrows, // 직렬화 가능한 옵션 이름들을 복잡한 진짜 arg 값으로 매핑(연결)해 줍니다.
      control: {
        type: 'select', // 'options'가 정의되었으니 사실 안 적어도 알아서 'select'로 인식됩니다.
        labels: {
          // 'labels'는 옵션 값 대신 화면에 보여줄 친절한 문자열 라벨을 짝지어줍니다.
          ArrowUp: 'Up',
          ArrowDown: 'Down',
          ArrowLeft: 'Left',
          ArrowRight: 'Right',
        },
      },
    },
  },
} satisfies Meta<typeof Button>;
 
export default meta;

참고로 mapping이랑 control.labels 객체 안에 가능한 모든 경우의 수를 억지로 꽉꽉 채워 넣을 필요는 없습니다. 만약 현재 선택된 옵션 값이 저 목록에 없다면? 그냥 그 값 문자열 자체가 날것 그대로(verbatim) 사용되거든요.

Controls 패널에서 스토리 생성하고 수정하기 (Creating and editing stories from controls)

Controls 패널 안에서 옵션들을 틱틱 만지다가 마음에 드는 화면이 나오면, 그걸 바로 새로운 스토리로 저장하거나 기존 스토리에 덮어써서 수정할 수도 있어요!

새 스토리 만들기 (Create a new story)

Controls 패널을 열고 컨트롤 값들을 이리저리 수정해 봅니다. 그러다 딱 원하는 화면이 나왔을 때, 그 변경 사항들을 아예 새로운 스토리로 저장(save)할 수 있습니다.

비디오: Controls 패널에서 새로운 스토리 만들기

만약 어떤 컴포넌트에 아직 만들어진 스토리가 하나도 없다면요? 사이드바에 있는 ➕(플러스) 버튼을 누르고 그 컴포넌트를 검색하면 아주 기본적인 베이스 스토리를 하나 뚝딱 만들어 준답니다.

비디오: 플러스 버튼으로 새 컴포넌트 스토리 생성하기

스토리 수정하기 (Edit a story)

컨트롤의 값들을 바꾼 다음, 그 바뀐 상태 그대로를 현재 스토리에 저장(save)할 수도 있습니다. 스토리북이 스토리 파일의 코드를 알아서 깔끔하게 업데이트해 줄 거예요. 참 똑똑하죠?

비디오: Controls 패널에서 스토리 수정하기

스토리 생성 및 수정 기능 끄기 (Disable creating and editing of stories)

만약 다른 사람들이 Controls 패널에서 맘대로 스토리를 만들거나 수정하는 걸 꽉 막아두고 싶으시다면, 이 기능을 꺼버릴 수 있습니다. .storybook/preview.js|ts 파일 안의 parameters.controls 파라미터에서 disableSaveFromUI 값을 true로 설정하시면 됩니다.

설정하기 (Configuration)

컨트롤 패널은 두 가지 방법으로 입맛에 맞게 설정할 수 있습니다:

  • 개별 컨트롤 단위로 어노테이션(annotation)을 추가하여 설정하기
  • 패널 전체의 모양이나 동작을 파라미터(parameters)를 통해 설정하기

어노테이션 추가하기 (Annotation)

위에서 본 것처럼, 컴포넌트나 스토리 파일의 argTypes 영역 안에 control 속성을 넣어서 컨트롤을 개별적으로 설정할 수 있습니다.

아래는 스토리북에서 사용 가능한 모든 컨트롤 타입들을 한눈에 볼 수 있도록 요약한 표입니다.

데이터 타입 (Data Type)사용될 컨트롤 (Control)설명 (Description)
booleanboolean껐다 켰다 할 수 있는 토글(toggle) 스위치를 제공합니다.
argTypes: { active: { control: 'boolean' }}
numbernumber가능한 범위의 숫자를 직접 입력하거나 화살표로 조절할 수 있는 입력 창을 제공합니다.
argTypes: { even: { control: { type: 'number', min:1, max:30, step: 2 } }}
range마우스로 쭉 끌어서 범위를 지정할 수 있는 슬라이더 컴포넌트를 제공합니다.
argTypes: { odd: { control: { type: 'range', min: 1, max: 30, step: 3 } }}
objectobject객체(object)의 값을 다룰 수 있는 JSON 기반 에디터를 제공합니다. 날것 그대로(raw mode)의 텍스트 편집도 지원해요.
argTypes: { user: { control: 'object' }}
arrayobject배열(array)의 값을 다룰 수 있는 JSON 기반 에디터를 제공합니다. 마찬가지로 원시 모드(raw mode) 편집이 가능합니다.
argTypes: { odd: { control: 'object' }}
file파일 첨부 기능을 제공합니다. 첨부된 파일들은 URL 배열 형태로 반환돼요. 특정 파일 형식(예: png)만 받도록 커스터마이징할 수도 있습니다.
argTypes: { avatar: { control: { type: 'file', accept: '.png' } }}
enumradio주어진 옵션들 중에 하나를 선택할 수 있는 라디오 버튼(세로 정렬)을 제공합니다.
argTypes: { contact: { control: 'radio', options: ['email', 'phone', 'mail'] }}
inline-radio가로로 나란히(inlined) 정렬된 라디오 버튼 세트를 제공합니다.
argTypes: { contact: { control: 'inline-radio', options: ['email', 'phone', 'mail'] }}
check여러 옵션을 중복해서 선택할 수 있는 체크박스(세로 정렬) 세트를 제공합니다.
argTypes: { contact: { control: 'check', options: ['email', 'phone', 'mail'] }}
inline-check가로로 나란히(inlined) 정렬된 다중 선택용 체크박스 세트를 제공합니다.
argTypes: { contact: { control: 'inline-check', options: ['email', 'phone', 'mail'] }}
select여러 값 중 하나를 선택할 수 있는 드롭다운(drop-down) 목록을 제공합니다.
argTypes: { age: { control: 'select', options: [20, 30, 40, 50] }}
multi-select여러 값을 동시에 선택할 수 있는 다중 드롭다운 목록을 제공합니다.
argTypes: { countries: { control: 'multi-select', options: ['USA', 'Canada', 'Mexico'] }}
stringtext글자를 자유롭게 입력할 수 있는 텍스트 필드를 제공합니다.
argTypes: { label: { control: 'text' }}
color색상을 선택할 수 있는 컬러 피커(color picker)를 제공합니다. 미리 설정된(preset) 색상 팔레트를 띄워주도록 추가 설정할 수도 있어요.
argTypes: { color: { control: { type: 'color', presetColors: ['red', 'green']} }}
date날짜를 선택할 수 있는 데이트 피커(datepicker)를 제공합니다.
argTypes: { startDate: { control: 'date' }}

💡 참고: date 컨트롤에서 날짜를 선택하면 그 값은 UNIX 타임스탬프(숫자)로 변환되어 전달됩니다. 만약 컴포넌트에서 진짜 Date 객체가 필요하다면, 스토리 내부에서 타임스탬프 값을 다시 Date 객체로 변환해 주는 로직을 추가해야 해요. (이 불편한 점은 앞으로 개선될 예정입니다!)

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { YourComponent } from './YourComponent';
 
const meta = {
  component: YourComponent,
  //👇 특정 argTypes를 지정해 주고 그에 맞는 컨트롤을 설정해 줍니다.
  argTypes: {
    propertyA: {
      options: ['Item One', 'Item Two', 'Item Three'],
      control: { type: 'select' }, // 'options' 속성이 있으면 알아서 'select'로 추론해주긴 합니다!
    },
    propertyB: {
      options: ['Another Item One', 'Another Item Two', 'Another Item Three'],
    },
  },
} satisfies Meta<typeof YourComponent>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
const someFunction = (valuePropertyA, valuePropertyB) => {
  // 여기에 복잡한 로직을 추가하세요!
};
 
export const ExampleStory: Story = {
  render: (args) => {
    const { propertyA, propertyB } = args;
    //👇 함수의 결괏값을 변수에 예쁘게 할당합니다.
    const someFunctionResult = someFunction(propertyA, propertyB);
 
    return <YourComponent {...args} someProperty={someFunctionResult} />;
  },
  args: {
    propertyA: 'Item One',
    propertyB: 'Another Item One',
  },
};

조건부 컨트롤 띄우기 (Conditional controls)

"A 옵션을 선택했을 때만 B 옵션을 화면에 보여주고 싶어!"
이럴 때 아주 유용하게 쓸 수 있는 기능이 바로 조건부 컨트롤입니다. if 속성에 간단한 조건 쿼리를 적어주면, 그 조건이 맞을 때만 해당 컨트롤이 패널에 나타나도록 만들 수 있어요.

예를 들어, 사용자가 '고급(advanced) 설정 보기' 토글을 켰을 때만 숨겨진 세부 설정들이 짠! 하고 나타나게 만들고 싶다면 이렇게 작성하시면 됩니다.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  argTypes: {
    // label 컨트롤은 언제나 화면에 표시됩니다.
    label: { control: 'text' },
    // advanced 컨트롤은 토글 스위치 형태입니다.
    advanced: { control: 'boolean' },
    // 👇 margin, padding, cornerRadius는 'advanced'가 true일 때만 화면에 나타납니다!
    margin: { control: 'number', if: { arg: 'advanced' } },
    padding: { control: 'number', if: { arg: 'advanced' } },
    cornerRadius: { control: 'number', if: { arg: 'advanced' } },
  },
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    variant: 'primary',
  },
};

어떤 컨트롤 값을 선택하면 아예 다른 컨트롤의 입력 자체를 막아버리고 싶을 때도 활용할 수 있어요. Button 컴포넌트가 텍스트(label)를 받을 수도 있고 이미지(image)를 받을 수도 있는데, 둘 다 동시에 받는 건 말이 안 되는 상황이라고 가정해 봅시다.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  argTypes: {
    // Button은 label이나 image 둘 중 하나만 가져야 합니다.
    label: {
      control: 'text',
      // 👇 'image' arg가 '거짓(falsy)'일 때만 나타납니다. 즉, 이미지가 없을 때만 나타나죠.
      if: { arg: 'image', truthy: false },
    },
    image: {
      control: { type: 'select', options: ['foo.jpg', 'bar.jpg'] },
      // 👇 반대로 'label' arg가 '거짓'일 때만 나타납니다.
      if: { arg: 'label', truthy: false },
    },
  },
} satisfies Meta<typeof Button>;
 
export default meta;

if 안에 들어가는 조건 객체는 무조건 arg 또는 global 둘 중 하나의 타겟 속성을 가지고 있어야 해요.

속성 (field)데이터 타입 (type)의미 (meaning)
argstring조건을 확인할 대상 arg의 ID (이름) 입니다.
globalstring조건을 확인할 대상 global(전역 변수)의 ID 입니다.

여기에 조건을 판단할 추가적인 연산자(operator) 속성을 딱 하나만 더 쓸 수 있습니다.

연산자 (operator)데이터 타입 (type)의미 (meaning)
truthyboolean대상의 값이 "참(truthy)" 인가요? (비어있지 않은지, 0이 아닌지 등)
existsboolean대상의 값이 정의(defined)되어 있나요?
eqany대상의 값이 내가 제시한 값과 정확히 일치하나요?
neqany대상의 값이 내가 제시한 값과 일치하지 않나요?

만약 타겟만 적어놓고 연산자를 하나도 안 적어주면, 스토리북은 알아서 { truthy: true } 조건으로 해석한답니다.


문제 해결 (Troubleshooting)

자동 생성된 문서(Docs) 페이지에서 컨트롤을 만져도 스토리가 안 변해요! (The controls are not updating the story within the auto-generated documentation)

혹시 스토리북 프리뷰 설정 파일(preview.js|ts)에서 스토리를 화면에 띄우는 옵션인 inline 값을 끄셨나요? 그렇다면 으레 발생하는 문제입니다. 현재 시스템 구조상 inline 렌더링이 꺼져 있으면 Docs 페이지에 들어있는 컨트롤 패널을 아무리 돌려도 스토리에 반영되지 않는 한계가 있어요. 이 문제는 추후 업데이트를 통해 싹 고쳐질 예정입니다. 조금만 기다려주세요!

API

파라미터 (Parameters)

컨트롤 기능은 스토리북의 파라미터(parameters) 영역 안, controls 네임스페이스 아래에 다음과 같은 옵션들을 제공합니다.

disable

타입: boolean

이 옵션을 켜면 컨트롤 패널 자체의 기능이 작동하지 않습니다. 아예 스토리북 전체에서 컨트롤을 날려버리고 싶다면, 여기서 하지 마시고 메인 설정 파일(main.js|ts)에서 플러그인을 빼버리는 게(disable) 맞습니다.

이 속성은 특정 컴포넌트나 스토리에서만 쏙 골라서 컨트롤을 보여주고 싶을 때 아주 유용합니다. 예를 들어 프로젝트 전역 설정으로 컨트롤을 꺼버렸더라도(disable: true), 특정 스토리에서만 이 값을 false로 덮어씌워주면 그 스토리에만 컨트롤 패널이 짠! 하고 부활하거든요.

exclude

타입: string[] | RegExp

컨트롤 패널에서 아예 숨겨버리고 싶은 속성들의 이름을 배열(문자열)이나 정규 표현식으로 적어줍니다. 여기에 걸러진 속성들은 패널에서 보이지 않게 됩니다. 위에서 본 컨트롤 필터링(Filtering controls) 예시를 참고하세요.

expanded

타입: boolean

이 값을 true로 설정하면 컨트롤 패널에서 각 속성들의 상세한 설명(description)과 기본값(default value)까지 전부 펼쳐서(expanded) 보여줍니다. 스토리북 Docs 페이지에 나오는 완전한 형태의 Controls 블록을 패널 안으로 그대로 들고 오는 거죠. 설명이나 기본값이 어떻게 렌더링될지는 Doc 블록을 수정하듯 자유롭게 커스터마이징할 수 있습니다.

프로젝트 전체에 이 확장 모드를 적용하고 싶다면 .storybook/preview.js|ts 파일에 다음처럼 적어주세요.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    controls: { expanded: true },
  },
};
 
export default preview;

이렇게 하면 모든 스토리의 컨트롤 패널이 시원시원하게 펼쳐진(expanded) 모습으로 나타납니다.

Controls table expanded

include

타입: string[] | RegExp

위의 exclude와 반대입니다. 컨트롤 패널에 꼭 보여주고 싶은 속성들의 이름만 배열이나 정규 표현식으로 쏙쏙 골라서 적어줍니다. 여기에 포함되지 않은 속성들은 전부 패널에서 숨겨집니다. 위에서 본 컨트롤 필터링(Filtering controls) 예시를 다시 한번 참고해 보세요.

presetColors

타입: (string | { color: string; title?: string })[]

color 컨트롤을 사용할 때 패널 하단에 보여줄 기본 색상 팔레트(preset color swatches)를 지정합니다. 문자열로 색상 값만 딱 적어줄 수도 있고, { color: '색상', title: '설명' } 형태로 객체를 만들어 툴팁 설명(title)까지 달아줄 수도 있어요. 여기서 사용하는 색상 값은 전부 유효한 CSS 색상 문자열이어야 합니다.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    controls: {
      presetColors: [{ color: '#ff4785', title: 'Coral' }, 'rgba(0, 159, 183, 1)', '#fe4a49'],
    },
  },
};
 
export default preview;

sort

타입: 'none' | 'alpha' | 'requiredFirst'

기본값: 'none'

컨트롤 패널에 나오는 항목들의 정렬 순서를 결정합니다.

  • none: (기본값) 특별한 정렬 없이, 스토리북이 argType을 읽어 들인 순서 그대로 보여줍니다.
  • alpha: 항목 이름(알파벳) 순서대로 깔끔하게 정렬합니다.
  • requiredFirst: alpha 방식과 똑같이 이름 순으로 정렬하되, '필수값(required)'인 항목들만 맨 위로 쭉 끌어올려 줍니다.

아래처럼 코드를 적으면 필수 항목들이 맨 위로 올라오게 정렬할 수 있어요.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
 
import { YourComponent } from './YourComponent';
 
const meta = {
  component: YourComponent,
  parameters: { controls: { sort: 'requiredFirst' } },
} satisfies Meta<typeof YourComponent>;
 
export default meta;

disableSaveFromUI

타입: boolean

기본값: false

이 값을 true로 주면, 누군가가 Controls 패널에서 값을 끄적거리다가 그걸 냅다 새로운 스토리로 저장하거나 기존 스토리를 수정해 버리는 기능을 꽉 막아버릴 수 있습니다.


이 페이지가 유용했나요? 👍 👎
✍️ 깃허브에서 편집하기 (Edit on Github)

profile
프론트에_가까운_풀스택_개발자

0개의 댓글