본 문서는 Storybook: Writing stories in TypeScript 페이지를 자체 번역한 글입니다.
구글링과 파파고, ChatGPT를 돌려가며 그냥 제가 이해할 수 있을 정도로만 번역했습니다.
(왜냐면 당장 써야하기 때문에 마음이 급했어요.)
타입스크립트로 스토리 작성하기
TypeScript로 스토리를 작성하면 생산성이 올라가요. 컴포넌트의 props를 확인하기 위해 파일을 오갈 필요가 없으며, 코드 에디터가 필수 props의 누락을 경고하고, 앱 내에서 컴포넌트를 사용할 때처럼 props 값을 자동 완성해주기 때문이에요. 또한, Storybook은 컴포넌트 타입을 추론하여 Controls 테이블을 자동으로 생성해줍니다.
Storybook은 기본적으로 TypeScript를 지원하기 때문에 별도의 설정 없이 바로 시작할 수 있어요.
Meta
와StoryObj
를 활용한 스토리 타이핑
스토리를 작성할 때, 타입을 지정하면 유용한 두 가지 측면이 있어요. 첫 번째는 Meta 컴포넌트입니다. Meta 컴포넌트는 컴포넌트와 해당 스토리를 설명하고 구성하는 역할을 해요. CSF 파일에서는 기본 내보내기(default export)와 같아요. 그리고 두 번째는 스토리 자체입니다.
Storybook은 각각을 위한 유틸리티 타입인 Meta
와 StoryObj
를 제공해요.
다음은 이러한 타입을 사용하는 CSF 파일의 예시입니다:
// Replace your-renderer with the renderer you are using (e.g., react, vue3, etc.)
import type { Meta, StoryObj } from '@storybook/your-renderer';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
component: Button,
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Basic: Story = {};
export const Primary: Story = {
args: {
primary: true,
},
};
Props 타입의 매개변수
Meta
와 StoryObj
타입은 모두 제네릭이기 때문에, 컴포넌트 타입이나 props 타입을 선택적으로 제공할 수 있어요. (예: Meta에서 typeof Button 부분). 이렇게 하면 TypeScript가 잘못된 arg를 정의하는 것을 방지할 수 있으며, 모든 데코레이터, play 함수, loader들이 함수 인자에 대해 타입을 가지게 됩니다.
위 예제에서는 컴포넌트 타입을 전달했어요. props 타입을 전달하는 예제는 Typing custom args 섹션을 참고해 주세요.
satisfies
for better type safety더 나은 타입 안전성을 위한
satisfies
사용
TypeScript 4.9 이상을 사용하는 경우, 새로운 satisfies 연산자를 활용하여 더 엄격한 타입 검사를 수행할 수 있어요. 이제 필수 args가 누락된 경우에도 타입 에러를 받을 수 있으며, 단순히 잘못된 args뿐만 아니라 필수 args가 누락된 경우에도 타입 에러를 확인할 수 있답니다.
satisfies를 사용하여 스토리의 타입을 적용하면, 여러 스토리에서 play 함수를 공유할 때 타입 안전성을 유지하는 데 도움이 됩니다. satisfies를 사용하지 않으면 TypeScript는 play 함수가 정의되지 않았을 수 있다는 에러를 발생시킬 수 있어요. 그리고 satisfies 연산자를 사용하면 TypeScript가 play 함수가 정의되었는지 여부를 추론할 수 있습니다.
마지막으로, satisfies를 사용하면 typeof meta를 StoryObj 제네릭에 전달할 수 있어요. 이는 TypeScript에게 meta와 StoryObj 타입 간의 연결 관계를 알려주며, meta 타입에서 args 타입을 추론할 수 있게 해요. 즉, TypeScript는 args가 스토리와 meta 레벨 모두에서 정의될 수 있음을 이해하고, meta 레벨에서 필수 args가 정의되었지만 스토리 레벨에서는 정의되지 않은 경우에도 에러를 발생시키지 않아요.