Essentials/Toolbars & globals

김동현·2026년 3월 22일

툴바와 전역 변수 (Toolbars & globals)

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

스토리북은 스토리가 렌더링되는 화면의 뷰포트(viewport)배경색(background)을 툴바(상단 메뉴)에서 쉽게 조작할 수 있는 기능을 기본으로 제공합니다. 이와 비슷한 원리로, 여러분도 직접 툴바에 버튼이나 메뉴를 만들어서 "전역 변수(globals)"라는 특별한 값들을 쥐락펴락할 수 있어요. 그리고 이 전역 변수값을 읽어와서 데코레이터(decorators)를 만들면, 스토리가 렌더링되는 방식을 내 마음대로 완벽하게 제어할 수 있게 됩니다.

Toolbars and globals

전역 변수 (Globals)

스토리북에서 전역 변수(Globals)란, (특정 스토리에만 묶여 있는 게 아니라) 스토리가 렌더링될 때 영향을 미치는 "전역적인" 입력값을 뜻합니다. 스토리에 종속된 값이 아니기 때문에 스토리 함수의 args 매개변수로는 전달되지 않습니다. (대신 context.globals를 통해 접근할 수는 있어요.) 이 전역 변수들은 주로 모든 스토리에 공통으로 적용되는 '데코레이터' 안에서 핵심적인 역할을 합니다.

툴바 등에서 이 전역 변수값이 바뀌면, 스토리가 곧바로 다시 렌더링(re-render)되면서 데코레이터도 새로운 값을 받아 다시 실행됩니다. 전역 변수를 가장 쉽게 조작하는 방법은 바로 툴바에 전용 아이템(버튼/메뉴)을 하나 만들어 주는 거예요.

globalTypestoolbar 어노테이션 (Global types and the toolbar annotation)

스토리북은 툴바 메뉴를 아주 쉽고 직관적으로 만들 수 있는 문법을 제공합니다. 프로젝트의 전역 설정 파일인 .storybook/preview.js|ts 안에 globalTypes를 정의하고 toolbar 어노테이션을 쓱 끼워 넣어주면 나만의 툴바가 완성됩니다.

예를 들어, 다국어 지원을 위해 화면 오른쪽 상단 툴바에 멋진 지구본 아이콘과 함께 언어(locale)를 선택할 수 있는 드롭다운 메뉴를 만들어 볼게요.

// 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 = {
  globalTypes: {
    // 👇 우리가 만들 전역 변수의 이름입니다 (locale)
    locale: {
      description: 'Internationalization locale',
      toolbar: {
        // 툴바에 띄울 아이콘 모양
        icon: 'globe',
        // 드롭다운 메뉴에 들어갈 항목들 (배열 형태)
        items: [
          { value: 'en', right: '🇺🇸', title: 'English' },
          { value: 'fr', right: '🇫🇷', title: 'Français' },
          { value: 'es', right: '🇪🇸', title: 'Español' },
          { value: 'zh', right: '🇨🇳', title: '中文' },
          { value: 'kr', right: '🇰🇷', title: '한국어' },
        ],
        // 사용자가 선택한 값에 따라 툴바 버튼의 텍스트가 휙휙 바뀌게 할 건가요? (추천: true)
        dynamicTitle: true,
      },
    },
  },
  initialGlobals: {
    // 스토리북이 켜질 때 기본으로 선택되어 있을 언어(locale) 값입니다.
    locale: 'en',
  },
};
 
export default preview;

💡 팁: 예시 코드에 있는 icon 속성의 'globe' 모양은 스토리북의 @storybook/icons 패키지에서 가져온 겁니다. 툴바에 쓸 수 있는 수많은 아이콘 목록이 궁금하시다면 여기를 확인해 보세요!

위 코드처럼 메뉴 항목 설정에 right 속성을 추가하면 데코레이터와 연결되었을 때 드롭다운 메뉴 오른쪽 구석에 앙증맞은 국기 이모지를 띄울 수 있습니다.

toolbar 어노테이션 안에서 메뉴를 설정할 때 쓸 수 있는 옵션들은 다음과 같아요:

속성 (MenuItem)데이터 타입 (Type)설명 (Description)필수 여부 (Required)
valueString전역 변수(globals)에 실제로 저장될 메뉴 항목의 '값'입니다.예 (Yes)
titleString메뉴 항목에 표시될 메인 '텍스트'입니다.예 (Yes)
rightString메뉴 항목의 오른쪽 끝에 살짝 표시될 텍스트(주로 이모지나 단축키 등)입니다.아니오 (No)
iconString이 항목이 선택되었을 때 툴바(상단)에 표시될 아이콘의 이름입니다.아니오 (No)

스토리 안에서 전역 변수 사용하기 (Consuming globals from within a story)

전역 변수는 이름 그대로 '전역' 설정이므로 가급적 데코레이터 안에서 사용하여 모든 스토리에 일괄 적용되게 하는 것을 가장 권장합니다.

하지만, 가끔은 특정 스토리 하나에서만 그 전역 변수값을 쏙 뽑아서 써야 할 때가 있죠. 그럴 땐 렌더링 함수에 전달되는 context 객체 안에서 globals 값을 바로 꺼내 쓸 수 있습니다.

방금 만든 툴바 메뉴에서 Locale 값을 선택했을 때, 스토리가 그 값을 받아서 인사말을 다르게 렌더링하는 예시를 볼까요?

// 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';
 
const meta = {
  component: MyComponent, // (실제로는 보여주기용 텍스트만 렌더링할 거라 컴포넌트는 안 씁니다)
} satisfies Meta<typeof MyComponent>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
// 선택된 언어(locale)에 맞춰 인사말을 반환하는 간단한 함수입니다.
const getCaptionForLocale = (locale) => {
  switch (locale) {
    case 'es':
      return 'Hola!';
    case 'fr':
      return 'Bonjour!';
    case 'kr':
      return '안녕하세요!';
    case 'zh':
      return '你好!';
    default:
      return 'Hello!';
  }
};
 
export const StoryWithLocale: Story = {
  // render 함수의 두 번째 인자인 context에서 globals.locale 값을 쏙 빼옵니다.
  render: (args, { globals: { locale } }) => {
    const caption = getCaptionForLocale(locale);
    // 선택된 언어에 맞는 인사말을 화면에 렌더링합니다!
    return <p>{caption}</p>;
  },
};

특정 스토리에만 고정된 전역 변수값 설정하기 (Setting globals on a story)

스토리북 툴바에서 전역 변수(예: 테마, 언어 등) 값을 바꾸면, 그 상태로 이 스토리, 저 스토리를 넘나들어도 값이 그대로 유지됩니다. 참 편리하죠? 하지만 "이 컴포넌트의 이 스토리는 무조건 '어두운 테마(dark)' 환경에서만 테스트해야 해!"처럼 스토리가 특정한 환경(값)을 강제로 요구할 때가 있습니다.

이럴 땐 특정 스토리나 컴포넌트 전체에 globals 어노테이션을 달아서 값을 확정 지어버릴 수 있습니다. 이렇게 설정해 두면, 사용자가 툴바에서 뭘 선택했든 다 무시하고 그 스토리는 항상 지정된 전역 변수값으로만 렌더링됩니다. 게다가 사용자가 헷갈리지 않게 해당 스토리 화면에서는 그 전역 변수를 조작하던 툴바 메뉴가 아예 클릭할 수 없게(disabled) 잠겨버리죠.

버튼 컴포넌트의 OnDark 스토리를 볼 때만 배경(backgrounds)을 무조건 'dark'로 고정해 버리는 예시입니다:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  globals: {
    // 👇 이렇게 쓰면 Button 컴포넌트의 '모든' 스토리가 'gray' 배경으로 고정됩니다.
    // backgrounds: { value: 'gray', grid: false },
  },
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const OnDark: Story = {
  globals: {
    // 👇 이 스토리 하나만 콕 집어서 배경을 'dark'로 고정시킵니다!
    backgrounds: { value: 'dark' },
  },
};

이제 OnDark 스토리에 들어가면 배경색은 강제로 'dark'가 되고, 상단의 배경색 변경 툴바 메뉴는 회색으로 변하면서 비활성화됩니다. 마우스를 올려보면 "이 스토리는 배경색이 고정되어 있어서 변경할 수 없다"는 친절한 툴팁 설명이 나올 거예요.

💡 주의: 특정 스토리의 globals 값을 강제로 덮어씌우는 기능은 아주 유용하지만 꼭 필요할 때만 써야 합니다. 사용자가 스토리북 툴바를 이리저리 만지면서 컴포넌트의 다양한 상태 조합(전역 변수 변경 + args 조작 등)을 자유롭게 탐색해 볼 수 있어야 하는데, 값을 고정해 버리면 그런 자유로운 탐색 기회를 뺏어버리게 되니까요.

고급 활용법 (Advanced usage)

지금까지 전역 변수 하나를 만들고 사용하는 기본적인 방법을 알아봤습니다.

이제 좀 더 실전적인 예시를 볼까요? 예를 들어, 아까 만들었던 locale(언어) 전역 변수를 활용할 수 있게 데코레이터(decorator)를 하나 만들어서 preview.js|ts에 연결해 보겠습니다. 테마(Theme)를 관리하는 데코레이터 예시지만, 언어나 다른 전역 변수를 다루는 원리도 이와 똑같습니다.

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
// (예시로 styled-components의 ThemeProvider를 가져옵니다)
import { ThemeProvider } from 'styled-components';
 
// 프로젝트에서 만들어둔 진짜 테마 파일들을 불러옵니다.
import { MyThemes } from '../my-theme-folder/my-theme-file';
 
const preview: Preview = {
  decorators: [
    (Story, context) => {
      // 툴바에서 사용자가 선택한 'theme' 글로벌 값을 가져와서 실제 테마 객체와 연결합니다.
      const theme = MyThemes[context.globals.theme];
      return (
        // 스토리(컴포넌트) 전체를 선택된 테마로 감싸서(wrap) 렌더링합니다!
        <<ThemeProvider theme={theme}>
          <Story />
        </ThemeProvider>
      );
    },
  ],
};
 
export default preview;

💡 다시 한번 강조하지만, 전역 변수는 말 그대로 전역(global)적인 녀석이라서 globalTypesinitialGlobals 설정은 오직 전역 설정 파일인 .storybook/preview.js|ts에서만 세팅할 수 있습니다!

애드온 내부에서 전역 변수값 업데이트하기 (Updating globals from within an addon)

만약 여러분이 직접 새로운 애드온을 만들고 있는데, 버튼을 누르면 전역 변수값이 탁! 바뀌면서 스토리북 화면이 짠! 하고 새로고침되게 만들고 싶으신가요?

storybook/manager-api 모듈이 제공하는 useGlobals() 훅을 쓰면 됩니다. 이 훅에서 두 번째 값으로 뱉어내는 updateGlobals 함수를 사용하면 원하는 전역 변수값을 입맛대로 바꿀 수 있죠.

가장 대표적인 예로, 여러분만의 멋진 툴바 애드온(toolbar addon)을 만들어서, 툴바 버튼을 누를 때마다 전역 변수값이 바뀌면서 화면이 리렌더링되게 하는 코드를 짜보겠습니다:

// your-addon-register-file.js (애드온 등록 파일)
import React, { useCallback } from 'react';
import { OutlineIcon } from '@storybook/icons';
import { useGlobals } from 'storybook/manager-api';
import { addons } from 'storybook/preview-api';
import { ToggleButton } from 'storybook/internal/components';
import { FORCE_RE_RENDER } from 'storybook/internal/core-events';
 
const ExampleToolbar = (props) => {
  // useGlobals() 훅으로 현재 전역 변수 상태와 업데이트 함수를 가져옵니다.
  const [globals, updateGlobals] = useGlobals();
 
  // 'my-param-key'라는 전역 변수값이 현재 true인지 확인합니다.
  const isActive = globals['my-param-key'] || false;
 
  // 툴바 버튼을 눌렀을 때 실행될 마법의 함수!
  const refreshAndUpdateGlobal = () => {
    // 1. 스토리북 전역 변수값을 반대로(true <-> false) 업데이트합니다.
    updateGlobals({
      ['my-param-key']: !isActive,
    });
    // 2. 스토리북 애드온 API의 통신 채널에 "화면 다시 렌더링해!"라는 이벤트(FORCE_RE_RENDER)를 쏴줍니다.
    addons.getChannel().emit(FORCE_RE_RENDER);
  };
 
  const toggleOutline = useCallback(() => refreshAndUpdateGlobal(), [isActive]);
 
  return (
    <ToggleButton
      key="Example"
      padding="small"
      variant="ghost"
      // 이 버튼이 눌린 상태인지 아닌지(active) 표시합니다.
      pressed={isActive}
      // 클릭 시 아까 만든 마법의 함수를 실행합니다.
      onClick={toggleOutline}
      ariaLabel="Addon feature"
      tooltip="Toggle addon feature"
    >
      <OutlineIcon />
    </ToggleButton>
  );
};
profile
프론트에_가까운_풀스택_개발자

0개의 댓글