Storybook을 이용하여 Atomic Design 시작해보기
Storybook이 뭔가요?
간단하게 말하면 Storybook은 오픈소스 UI 테스터 툴입니다. 즉, UI 컴포넌트를 제작하면서 바로바로 확인해볼 수 있도록 도와주는 툴입니다.
아마 Storybook을 쓰기 전의 저와 비슷한 생각을 가진 사람이라면, 바로바로 페이지를 만들면서 확인할 수 있는 걸 굳이 테스터까지 사용해가며 구현할 필요가 있을까?라는 의문을 가질 수도 있을 것 같습니다. 그래서 등장하게 되는 것이 Atomic Design입니다.
Atomic Design
(Atomic Design 개념을 소개한 Brad Frost의 책. 무료로 읽어볼 수 있습니다.)
Page를 기본 단위로 생각해가며 디자인, 개발을 해왔던 것과 달리, Atomic Design은 UI의 공통적인 요소를 가장 작은 단위인 atom부터 molecule, organism, template, page 순으로 점점 조립해나가는 개념입니다.
간단하게 예를 들면 최근 제가 작업했던 프로젝트의 채팅창 모습인데요, 여기서 붉은 색이 atom, 초록색이 molecule, 노란색이 organism 정도로 생각해볼 수 있습니다. 가장 작은 단위의 UI 컴포넌트인 atom, 이런 atom을 몇 가지 조합하여 나오는 재활용이 가능한 묶음인 molecule, 그리고 이 molecule이 여럿 모여 의미를 갖는 묶음이 organism이라고 볼 수 있습니다. 이런 organism을 모아서 하나의 화면을 구성할 수 있는 템플릿이 나오고, 이 템플릿을 이용해 구성하는 것들이 결국 페이지가 되는 셈입니다.
즉, 페이지별로 따로 작업을 하는 것이 아니라, 공통되는 작은 요소부터 독립적으로 만든 뒤 합쳐가며 제작을 하는 디자인 패턴이라고 생각할 수 있습니다.
물론, 저 또한 Atomic Design과 관련된 모든 글을 읽은 것은 아니고, 접한 지 얼마 되지 않아 정확한 정보는 아닐 수 있으나, 중요한 것은 재활용이 가능한 공통 컴포넌트를 만드는다는 개념이 아닌가 싶습니다.
그래서 Storybook은 이런 독립적인 컴포넌트를 제작할 때 더욱 유용합니다. 페이지를 만들기 전에도 UI를 미리 확인할 수 있고, UI의 props에 따라 달라지는 형태도 일괄적으로 확인이 가능하기 때문입니다.
React + TypeScript + Storybook
제가 Storybook을 사용한 환경은 TypeScript 기반의 React 프로젝트입니다. 각각의 버전은 다음과 같습니다.
"@storybook/react": "^6.1.11",
"typescript": "^4.1.3",
"react": "^17.0.1",
"@types/react": "^17.0.0",
감사하게도 Storybook은 프로젝트의 루트 디렉토리에서 단 한 번의 명령어만으로도 자동으로 프로젝트의 환경을 감지하여 Storybook을 사용할 수 있는 세팅을 해줍니다. 관련된 튜토리얼은 아래 링크에서도 자세하게 소개하고 있습니다.
명령어는 다음과 같습니다. 루트 디렉토리로 진입 후
npx sb init
위의 명령어를 입력하면 다음과 같은 것들이 프로젝트에 추가됩니다.
- 📦 필요한 의존성 모듈 설치
- 🛠 Storybook 실행을 위한 스크립트 추가
- 🛠 Storybook 기본 설정 추가
- 📝 바로 사용할 수 있게끔 가이드라인이 되어줄 boilerplate 추가
이렇게 자동으로 세팅이 완료되고나면
npm run storybook
명령어를 통해 바로 Storybook을 실행할 수 있습니다. Storybook은 웹 브라우저를 통해 확인하실 수 있으며, localhost 6006 포트로 진입하시면 됩니다.
Story 만들기
스토리는 UI가 렌더된 모습을 미리 확인할 수 있게 해 줍니다. 즉, Storybook을 실제로 실행해보면 나오는 각각의 항목을 작성한다고 이해하시면 됩니다.
Storybook은 생성되는 boilerplate만 봐도 기본적인 사용법은 바로 숙지가 될 만큼 명확하게 작성되어있습니다. 만들어지는 boilerplate는 모두 stories라는 폴더 안에 .stories.tsx로 끝나는 파일들인데, 그중 Button.stories.tsx의 코드는 이렇습니다.
import React from 'react';
// also exported from '@storybook/react' if you can deal with breaking changes in 6.1
import { Story, Meta } from '@storybook/react/types-6-0';
import { Button, ButtonProps } from './Button';
export default {
title: 'Example/Button',
component: Button,
argTypes: {
backgroundColor: { control: 'color' },
},
} as Meta;
const Template: Story<ButtonProps> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
export const Large = Template.bind({});
Large.args = {
size: 'large',
label: 'Button',
};
export const Small = Template.bind({});
Small.args = {
size: 'small',
label: 'Button',
};
스토리가 포함해야 하는 것들은 대략 다음과 같습니다.
- 필요한 모듈, 컴포넌트, 타입 불러오기
- 스토리의 제목 (Example/Button이면 스토리북 내 섹션 구분이 Example, 스토리 이름이 Button입니다. 디렉토리처럼 명시한다고 생각하시면 됩니다.)
- argTypes: Storybook에서 자유롭게 조절하고 싶은 인자 (만약 버튼 컴포넌트가 색을 자유롭게 넣을 수 있는 구조라면 미리 명시하지 않고 Storybook에서 자유롭게 선택해가며 테스트할 수 있습니다)
- 템플릿: 말 그대로 사용할 컴포넌트로 템플릿을 만듭니다. 직접 만든 컴포넌트를 불러와 위에 적힌 부분에서 컴포넌트, 타입 부분만 교체하면 됩니다.
- Storybook에서 보여줄 스토리의 여러 하위 스토리들: 여기서 자유롭게 인자를 지정하여 여러 가지 버전을 만들어 확인할 수 있습니다.
이렇게 boilerplate를 보고 직접 만든 컴포넌트 또한 스토리 파일을 계속 생성하여 바로바로 테스트해볼 수 있습니다. 예를 들어, 버튼이라면 예시 코드처럼 Primary, Secondary 같은 변화나, 활성화 비활성화에 따른 모습을 굳이 페이지에서 변경해가며 확인하지 않고도 인자 값만 변경하여 등록해놓으면 확인이 간단합니다.
마치며
굳이 Atomic Design까지 요하지 않는 간단한 페이지 작업이라면 Storybook이 오히려 과하다고 느껴질 수도 있다고는 생각합니다. 그러나 조금이라도 디자인 시스템이나 Atomic Design을 적용하는 프로젝트라면 테스트터로서의 장점도 있고, 무엇보다 협업할 때 다른 사람이 만든 컴포넌트를 확인하기가 간단한 것 같습니다. 개인적으로는 사용하면서 장점이 훨씬 많다고 느꼈고, 덕분에 Atomic Design을 적용하기가 훨씬 수월해져서 상당히 만족스럽습니다. 이미 잘 알려진 도구이지만, 혹 사용을 망설이시는 분들이 있다면 적극적으로 추천을 해봅니다.