Story 작성
Story란?
Story는 UI 컴포넌트의 렌더링 된 상태를 캡처합니다. 인자 집합이 주어지면 컴포넌트 상태를 반환하는 함수입니다.

Story 구성
Storybook은 컴포넌트와 그 하위 스토리의 2가지 기본 단계로 구성되어 있습니다. 스토리는 컴포넌트에 대한 개별 이야기입니다. 필요한 만큼의 스토리를 컴포넌트 별로 작성할 수 있습니다.
컴포넌트
├── 스토리
├── 스토리
├── ...
└── 스토리
Story 파일 생성
Story는 컴포넌트 파일이 위치한 디렉토리 안에 작성합니다. 이 파일은 개발용이며 프로덕션 번들에 포함되지 않습니다. Story를 구성할 컴포넌트를 작성한 후, 컴포넌트 파일과 같은 위치에 Story 파일을 추가합니다.
components/
└─ StyleInput/
├─ StoryInput.scss
├─ StyleInput.js
└─ StyleInput.stories.js # Story 파일
Story 파일 포멧
컴포넌트 스토리 포멧(CSF)은 Story를 구성하는 객체를 말하며, 작성할 Story의 정보를 작성합니다.
속성
설명
Storybook 앱 사이드바에 표시되는 컴포넌트 이름
Story를 작성 할 컴포넌트 설정 (컴포넌트 설명, props
추출)
모든 Story에 공통 적용할 전달 인자 설정
각 Story 인자(args)의 행동(behaviour) 방식 설정
Story를 감싸는 렌더링 함수 (Story 보강, 렌더링 세부 정보 수집 등)
Story에 대한 정적 메타 데이터 정의 (다양한 애드온 구성 제공)
Storybook에서 Story를 내보낼 때 렌더링에서 제외 설정 (정규 표현 식)
컴포넌트 스토리 포멧(CSF)은 "기본(default) 내보내기"로 내보내야 합니다.
export default {
title: '시스템/그룹/컴포넌트 이름',
component: Component,
// args: {},
// argTypes: {}
// decorators: []
// ...
}
Story 작성(정의)
컴포넌트의 Story(CSF)는 "지정 된 이름(named) 내보내기"를 사용하여 컴포넌트 Story를 정의합니다. Story 이름은 TitleCase로 작성하는 것이 권장됩니다.
export const Story = () => <Button secondary>스토리 버튼</Buton>
Storybook에 표시되는 Story 이름을 변경해야 한다면? storyName
속성을 사용할 수 있습니다.
StoryButton.storyName = 'Secondary Button'
멀티 Story 작성
Story는 컴포넌트를 렌더링 하는 방법을 설명하는 함수입니다. 컴포넌트 마다 여러 개의 Story를 가질 수 있습니다. Story를 만드는 가장 간단한 방법은 인자가 다른 컴포넌트를 여러 번 렌더링하는 것입니다.
export const Primary = () => <Button children="프라이머리 버튼" />
export const Secondary = () => <Button secondary children="세컨더리 버튼" />
export const Tertiary = () => <Button tertiary children="터시어리 버튼" />
이 방법은 작성이 간단하지만, 많은 Story를 관리해야 할 경우 유지보수가 어렵습니다.
템플릿, 인자 활용
하나 이상 컴포넌트의 스토리를 만들 경우 Template
변수에 컴포넌트 복사본을 할당하는 것이 편리합니다. 이 패턴을 Story에 도입하면 작성 또는 유지보수 해야 할 코드 양이 줄어듭니다.
// Story 템플릿
const Template = (args) => <Button {...args} />
// 템플릿 복사본
export const Primary = Template.bind({})
export const Secondary = () => Template.bind({})
export const Tertiary = () => Template.bind({})
// 복사한 각 템플릿(Copyed Story)에 인자 설정
Primary.args = { children: "프라이머리 버튼" }
Secondary.args = { secondary: true, children: "세컨더리 버튼" }
Tertiary.args = { tertiary: true, children: "터시어리 버튼" }
인자(arguments)는 줄여서 args
를 사용하며, Storybook을 다시 시작하지 않고도 Controls addon으로 컴포넌트를 실시간으로 수정할 수 있습니다. 값이 변하면 컴포넌트도 실시간 업데이트 됩니다.
Primary.args에서 의문이 들 수 있습니다. args는 JavaScript 함수의 표준 속성이 아니기 때문이죠. 이는 Storybook이 컴포넌트를 렌더링 하는 과정에서 Primary에 args를 전달하므로 정상 작동하는 것입니다.
아래 코드는 Story 작성 예시입니다.
// Story를 구성할 컴포넌트 파일 불러오기
import StoryInput from './StoryInput'
/* ------------------------------------------------------------------- */
export default {
// 컴포넌트 설명을 입력하면 Storybook에 카테고리 되어 표시됩니다.
title: 'FormControl/StoryInput',
// 컴포넌트 설정
component: StoryInput,
// 전달인자 공통 설정
args: {
label: '이메일',
type: 'email',
placeholder: 'yamoo9@euid.dev',
},
// 전달 인자 유형 설정
argTypes: {
backgroundColor: { control: 'color' },
disabled: { control: 'boolean' },
},
}
// 컴포넌트 템플릿
// 함수의 복사본을 만드는 표준 JavaScript 기법
const Template = (args) => <StoryInput {...args} />
// sm 사이즈 컴포넌트
export const SmSize = Template.bind({})
SmSize.storyName = 'Small'
SmSize.args = {
id: 'sm-size-kwdj1',
size: 'sm',
}
SmSize.parameters = {
viewport: {
defaultViewport: 'iphonex',
},
}
// md 사이즈 컴포넌트
export const MdSize = Template.bind({})
MdSize.storyName = 'Medium'
MdSize.args = {
id: 'md-size-kwdj5',
size: 'md',
}
MdSize.parameters = {
viewport: {
defaultViewport: 'iphonexr',
},
}
// lg 사이즈 컴포넌트
export const LgSize = Template.bind({})
LgSize.storyName = 'Large'
LgSize.args = {
id: 'lg-size-kwdj8',
size: 'lg',
}
Storybook 구동
storybook 구동 명령을 사용해 Storybook을 웹 브라우저에서 확인할 수 있습니다.
npm run storybook
Light 모드

Dark 모드 (별도 설정 필요)

Storybook 스니펫
Storybook 컴포넌트 스니펫을 사용하면 손쉽게 컴포넌트 Story를 구성할 수 있습니다.
import $1 from './$1'
const storyConfig = {
// 컴포넌트 설명
title: '${2:시스템}/${3:그룹}/$1',
// 컴포넌트 설정
component: $1,
// 전달 인자 설정
// args: {
// 설정 예시
// type: 'email',
// },
// 전달 인자 유형 설정
// argTypes: {
// 컬러 피커 컨트롤 설정 예시
// backgroundColor: { control: 'color' },
// }
}
export default storyConfig
// 컴포넌트 템플릿
const Template = (args) => <$1 {...args} />
// 예제 컴포넌트 생성
export const Example = Template.bind({})
// 예제 컴포넌트 props 설정
Example.args = {
// prop 속성 설정
}
매개변수 활용
매개변수는 Story에 대한 정적 메타 데이터를 정의하는 Storybook의 방법입니다. Story의 매개변수를 사용하여 Story 또는 Story 그룹 레벨에서 다양한 애드온에 구성을 제공 할 수 있습니다. 예를 들어 앱의 다른 컴포넌트와 다른 배경에서 컴포넌트를 테스트 하고 싶다면? 다음과 같이 CSF를 설정합니다. (컴포넌트 레의 매개변수 설정)
export default {
title: 'Button',
component: Button,
//👇 Story 매개 변수 설정
parameters: {
backgrounds: {
values: [
{ name: 'darkred', value: '#340000' },
{ name: 'storypink', value: '#fb6597' }
],
},
},
};

데코레이터 활용
데코레이터는 Story를 렌더링 할 때 임의의 마크업으로 컴포넌트를 감싸는 메커니즘입니다. 예를 들어 테마 또는 레이아웃 래퍼(wrapper)가 필요할 수 있습니다. 또는 UI에 특정 컨텍스트 혹은 데이터 공급자(provider)가 필요할 수 있습니다.
간단한 예는 컴포넌트의 Story에 스타일 래퍼를 추가하는 것입니다. 다음과 같이 Story를 감싸는 데코레이터를 작성하면 Storybook 뷰포트에 스타일이 반영된 래퍼 요소가 렌더링 됩니다.
export default {
title: 'Button',
component: Button,
decorators: [
(Story) => (
<div style={{ margin: '40px' }}>
<Story />
</div>
),
],
};
멀티 컴포넌트 Story
디자인 시스템 또는 컴포넌트 라이브러리를 빌드 할 때 함께 작동하도록 디자인 된 2개 이상 컴포너트의 Story를 작성하려면 상위 컴포넌트와 하위 컴포넌트 모두 불러와 사용하고, 컴포넌트 작동 상황 별 Story를 렌더링 하는 것이 좋습니다.
import List from './List';
import ListItem from './ListItem';
export default {
component: List,
title: 'List',
};
// 하위 컴포넌트를 포함하지 않은 상위 컴포넌트 Story
export const Empty = (args) => <List {...args} />;
// 1개의 하위 컴포넌트를 포함한 상위 컴포넌트 Story
export const OneItem = (args) => (
<List {...args}>
<ListItem />
</List>
);
// 1개 이상 하위 컴포넌트를 포함한 상위 컴포넌트 Story
export const ManyItems = (args) => (
<List {...args}>
<ListItem />
<ListItem />
<ListItem />
</List>
);
Last updated
Was this helpful?