프론트엔드에서는 UI 작업을 할 때에 재사용성이 높은 디자인들을 컴포넌트로 구성하는 작업이 필수적이다.
컴포넌트 작업을 여러 사람이 하다보면, 내가 가져다 사용해야 할 컴포넌트 이름이 뭐고 어디에 있지?? 이렇게 혼란이 올 때가 많다. 또한 컴포넌트를 코드를 직접 변경해가며 변화를 확인하는 것은 매우 귀찮다.
이를 방지하기 위해 모든 컴포넌트들을 한눈에 파악하고, 코드 변경없이 컴포넌트들의 변화를 한번에 확인할 수 있는 Storybook이라는 도구가 있다.
npx storybook init
프로젝트에서 위 명령어를 터미널에 입력하면 스토리북 설치를 진행할 수 있다.
설치가 완료되기 전 Storybook에서 제공하는 ESLint 를 설치 여부를 묻는 단계가 있다.
ESLint 는 내제되어있는 Linter가 없는 자바스크립트로 협업할 때 필수적인 도구이다. ESLint 를 통해 문법 및 스타일적 오류, 적절하지 않은 구조 등을 잡아냄으로써 일관성 있는 코드를 짤 수 있게 도와준다.
Storybook ESLint 역시 스토리북을 사용하는 과정에서 발생할 수 있는 에러를 미리 표시해주는 도구이므로 나는 설치를 진행하였다.
설치 후 적용하기 위해 ESLint 설정 관련 파일인 .eslintrc.js 파일을 루트에 생성해준 뒤, extends 에
{
...
"extends": ["plugin:storybook/recommended"]
...
}
위와 같이 설정해주면 *.stories.* 와 같은 구조의 파일을 Storybook ESLint 가 감지할 수 있다.
설치를 마쳤다면 루트에 .storybook 폴더가 생성되어있고, 그 안에 main.ts, preview.ts 파일을 볼 수 있을 것이다. CRA 와 Next.js 에서 내용 차이가 살짝 있으나 자동으로 생성해주고, 추가로 필요한 내용을 추가해주면 된다.
import type { StorybookConfig } from "@storybook/react-webpack5";
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-webpack5",
options: {
builder: {
useSWC: true,
},
},
},
docs: {
autodocs: "tag",
},
staticDirs: ["..\\public"],
};
export default config;
Next.js 를 사용한다면 StorybookConfig 를 @storybook/nextjs 에서 내려줄 것이다.
- stories 에는 스토리북이 stories를 찾을 경로를 지정해주면 된다. 본인이 stories를 사용할 경로가 디폴트 경로에 포함되어있지 않다면 해당 부분에 추가해주면 된다.
- addons 에는 스토리북 측에서 우리들이 더 편리하고 유용하게 사용할 수 있도록 추가적인 기능을 덧붙인 것이라고 보면 된다. 아직은 디폴트 세팅에서 건드리지 않았는데 필요하다면 찾아서 추가해주면 될 것 같다.
- framework 에는 말그대로 사용하는 프레임워크를 정의해주는 부분이다. CRA 에서는 위에처럼 설정되어있을 것이고, Next.js 에서는 @storybook/nextjs 로 설정하면 된다. SWC 는 Speedy Web Compiler의 약자로 이를 설정하여 매우 빠른 컴파일러를 사용할 수 있다고 한다.
- docs 는 디폴트로 "tag" 값을 가지고 있다. true/false 의 boolean 값을 통해 전역적으로 docs 를 사용할 지 지정할 수 있고, "tag" 값을 사용하면 tag 요소를 사용하여 컴포넌트별로 story를 내보낼 때 각각 docs 의 생성여부를 지정할 수 있다.
- staticDirs 에 Storybook 에서 감지해야 하는 정적 파일들이 있는 경로를 지정해줄 수 있다. 이미지 및 svg 파일들의 경로를 지정해줄 때 사용하면 유용하다.
addon-ally
내가 듣는 강의에서 강사님이 추천해주신 addon이 하나 있다.
이 addon을 사용하면 접근성 테스트를 진행할 수 있다고 한다.
yarn add -D @storybook/addon-a11y@7.6.16
명령어로 addon을 설치해준 후, addons 속성에
"@storybook/addon-a11y"
를 추가해주면 된다.
나는 스토리북 v7을 사용하고 있는데 addon-ally v8을 사용하면 storybook/icons 관련 에러가 발생해서 버전 7로 설치해주었다.
그러면
위처럼 컴포넌트에 대한 접근성 테스트까지 해주는 것을 확인할 수 있다.
이처럼 main.ts 파일의 내용을 살펴보았다. 그럼 preview.ts 파일은 대체 무엇일까?
import type { Preview } from "@storybook/react";
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
해당 파일에서는 모든 Story 에 적용될 global 포맷을 설정할 수 있다. 디폴트로 parameters 에 actions 와 controls 가 설정되어있다.
- actions 에서는 이벤트 핸들러를 액션으로 인식하여 Storybook에서 해당 액션을 보여줄 때 사용된다. argTypesRegex 에 정의된대로 on으로 시작하는 다음 글자가 대문자인 모든 이벤트 핸들러를 액션으로 인식한다.
- controls 에서는 개발자가 코드를 변경할 필요 없이 해당 속성을 동적으로 손쉽게 control 할 수 있게 도와준다. 예를들어 matchers 에 color 가 있고, 이를 통해 모든 Story에서 색상을 코드변경 없이 동적으로 변경할 수 있도록 도와준다.
나는 tailwind를 사용하였고, 이를 사용하기 위해서는 기본적으로
@tailwind base;
@tailwind components;
@tailwind utilities;
위와같은 내용의 CSS폴더 경로를 preview.ts 파일에 추가로 import 해줘야 한다.
이제 본격적으로 컴포넌트를 Story로 생성하는 과정에 대해 알아보자!! 기본적으로 stories 파일이 생성되어있을텐데 나는 우선 모두 지워주었다.
위는 내가 Storybook 사용시에 활용하는 구조이다. 예를들어 Button 컴포넌트를 만든다고 하면 Button.tsx 와 같은 경로에 stories 파일을 만들어서 각 컴포넌트의 Story를 쉽게 찾을 수 있도록 해준다.
import { twMerge } from "tailwind-merge";
export interface ButtonProps {
variant?: "fill" | "line";
label: string;
disabled?: boolean;
onClick?: () => void;
className?: string;
}
export default function Button({
variant = "fill",
label = "버튼 라벨",
disabled = false,
onClick,
className,
}: ButtonProps) {
return (
<button
className={twMerge(
`w-[33.5rem] h-[4.8rem] rounded-[10px] text-white bg-blue-400 disabled:bg-gray-400`,
variant === "line" &&
"h-[4rem] bg-white text-gray-400 border-[1.125px] border-gray-400",
className
)}
onClick={onClick}
disabled={disabled}
>
{label}
</button>
);
}
위는 Button 컴포넌트를 정의하는 기본적인 tailwindCSS를 활용한 코드이다.
variant 속성은 'fill' 과 'line' 으로 구분되며, disabled 는 boolean 타입을 가지고있다.
import Button from "./Button";
import type { Meta, StoryObj } from "@storybook/react";
const meta = {
title: "Common/UI/Button/Button",
component: Button,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {
label: {
control: "text",
description: "버튼에 들어갈 텍스트 지정",
},
variant: {
control: "radio",
description: "버튼 타입 fill or line",
},
disabled: {
description: "버튼 클릭 허용 여부",
},
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Fill: Story = {
args: {
variant: "fill",
label: "Fill 버튼",
},
};
export const Line: Story = {
args: {
variant: "line",
label: "Line 버튼",
},
};
위는 Button 컴포넌트를 Story로 생성하는 코드이다.
- 위에서는 meta라는 변수에 여러 속성을 정의하였다. title 을 통해 Storybook에서 해당 Story가 생성될 경로를 지정한다. 다음으로 component를 정의해주고, parameters와 tags 를 통해 docs가 자동생성되게 한 뒤 Story가 docs 내에서 보여질 위치를 지정해준다. 마지막으로 argTypes를 통해 Story 생성 시 사용될 args에 추가적인 정보를 지정해줄 수 있다. 위에서는 각 args의 description만 지정해주었는데 문서를 보면 훨씬 더 많은 내용이 담겨있다. (https://storybook.js.org/docs/api/arg-types)
- 생성한 meta 변수가 해당 컴포넌트의 타입과 맞는지 satisfies 연산자를 통해 확인해주고, 맞지 않으면 에러를 뿜어낸다. satisfies 연산자는 타입을 강력히 지정해주는 as 연산자보다 더 유용하게 사용될 수 있다고 한다. 다음 블로그에 잘 정리되어 있다.(https://mycodings.fly.dev/blog/2023-07-14-understanding-typescript-satisfies-operator)
- 그 후 meta 변수를 Storybook에서 제공하는 StoryObj의 제네릭으로 주입하여 type을 선언해준다. 해당 type을 이용하여 여러 Story를 생성할 수 있다.
위와 같이 Story를 생성한 후 Storybook을 실행시켜보자.
npm run storybook
위 명령어를 통해 실행시켜보면
위와 같이 title에 지정한 경로에 Story가 생성된 것을 확인할 수 있다.
위와 같이 코드 변경 없이도 속성값을 변화시켜가며 한눈에 컴포넌트 변화를 파악할 수 있다.
Storybook은 Chromatic을 통해 쉽게 배포할 수 있다.(https://storybook.js.org/tutorials/intro-to-storybook/react/ko/deploy/)
이처럼 UI 작업에 큰 효율을 가져다줄 수 있는 Storybook에 대해 간략히 알아보았다.
이러한 디자인 시스템들을 익혀나가는 것은 프론트엔드 개발자에게 매우 중요한 요소라고 생각이 들었다!
'FrontEnd > React' 카테고리의 다른 글
React-Transition-Group 으로 애니메이션 주입하기 (0) | 2024.02.18 |
---|---|
ref 속성 파헤치기 (4) | 2024.02.10 |
Vite + Yarn berry 프로젝트 배포하기 (0) | 2023.12.24 |
Vite + Yarn Berry 구축기 - 2 (4) | 2023.12.07 |
Vite + Yarn Berry 구축기 - 1 (4) | 2023.12.06 |