react 컴포넌트는 재사용하여 생산성을 향상시켜준다
근데 그 컴포넌트 정보를 팀원에게 공유하자니 생각보다 이게 설명하기 힘들고 귀찮다
Next.js에서는 그런 당신을 위해 준비했습니다! 내가 말고 어느 대단하신분들이 만든 라이브러리! storybook!
컴포넌트 별로 문서화하고 테스트 해볼 수 있는 라이브러리라고 보면 될 것 같다
api 도 스웨거로 문서화하고 테스트해보듯이, 컴포넌트도 문서화로 쉽게 정리하고 조작하도록 돕는 것이다!
사용 세팅방법은 아래와 같다
리액트 앱(혹은 next.js) 설치 후 아래의 코드 실행하자
npx sb init --builder webpack5
그럼 설치 후 아래와 같이 묻는다
Do you want to run the 'eslintPlugin' migration on your project? 에 y를 눌러 yes 를 해주자 그럼 다시 주르르 설치된다
설치 중에 요런 에러가 뜰 것이다
https://github.com/storybookjs/eslint-plugin-storybook
스토리북 사용법이 정의된 스토리북 공식 깃헙이다
아래 캡쳐부분을 보자
eslint 플러그인에 위의 extends 를 추가해주면 된다고 한다. 응 그렇단다...
요 파일에 들어가서 위에서 추가하란대로 기존 extends 에 아래와 같이 추가해줬다
npm도 7버전으로 마이그레이션 할거냐고 묻길래 y를 눌러서 "넹 해주시죠~"
스토리북 설치가 끝났다
npm run stroybook
혹은
yarn run storybook
으로 스토리북을 실행해보면 다음과 같은 창이 뜬다
왼쪽의 사이드 탭에 몇개의 데모 스토리(기본 제공 컴포넌트)를 제공한다
왼쪽의 탭에서 button 을 눌러보자
저 파란 버튼을 클릭하면 아래탭의 actions 에서 이벤트를 볼 수 있고,
controls 탭에서는 버튼 컴포넌트의 텍스트나 색, 크기 등의 css 를 수정해볼 수 있다
상단의 Docs를 눌러보면 아래와 같은 형식으로 보여주는데 뭐 코드를 볼 수 있다는 점 외엔 크게 다른점은 모르겠다
여기서도 컴포넌트를 확인 및 테스트가 가능하다
이제 스토리북 웹페이지를 닫고, 폴더구조를 확인해보자
.storybook 폴더와 stories 폴더가 생겼다
.storybook 폴더는 스토리 전역 설정용 폴더이며
story 폴더엔 방금 막 써본 Botton 이란 데모 스토리도 tsx, css 파일로 보인다
Button.tsx 를 눌러보자
스토리북 페이지에서 값을 넣어봤던 것들이 여기서 Props 로 갖는 것들이었다 두둥!
즉, 컴포넌트의 Props 를 스토리북에서 대입해보며 테스트가 가능하다는 말이다
근데 스토리 폴더를 이렇게 따로 두어 관리하는 것보다, 각 컴포넌트와 같은 경로에서 스타일 파일과 함께 두는 것이 찾고 관리하기 쉽기 때문에, stories 폴더는 지워버리고 .storybook 폴더에서 main.js 를 아래와 같이 수정해주자
위처럼 설정을 바꾸면 최상위 경로에 components 폴더를 만들어서, pages 폴더와 함께 사용하면 된다
components 폴더에 Button 폴더를 만들고 버튼 컴포넌트를 Button.tsx 에 구현, Button.stories.tsx 에는 구현한 버튼을 스토리 북에서 테스트 해 볼 수 있도록 추가로 설정해주자
만들어본 예시를 보며 이해하자
//Button.tsx
import { css, SerializedStyles } from "@emotion/react";
import styled from "@emotion/styled";
import { MouseEvent } from "react";
// 컬러 표를 타입으로 정해 해당 타입에서만 고르도록 하자
export type Color = "primary" | "secondary" | "danger" | "warning";
// color props 가 undefined 일 경우가 있으므로 ? 를 붙여주자
// onClick의 이벤트를 props 로 받을 수 있는데, 이벤트로 리턴되는 것은 없다고 타입에서 정해주자
export type Props = {
children: string;
color?: Color;
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
};
// Button 컴포넌트에서 color property 의 값을 받아서 스타일을 바꿔주기 위한 함수
// emotion 을 사용, css`` css 리터럴로 리턴해준다
// 이때의 리턴된 css 리터럴의 타입은 SerializedStyles 로 해주자. 직렬화된 스타일 이란 뜻이다
// color props 가 undefined 일 경우, 인자를 받지 않을 수 있으니 ?를 붙여주자
export const getColors = (color?: Color): SerializedStyles => {
switch (color) {
case "primary":
return css`
background: #6d5dfc;
color: #e4ebf5e6;
`;
case "secondary":
return css`
color: #5e5c64e6;
`;
case "danger":
return css`
background: #dc3545e6;
color: #e4ebf5e6;
`;
case "warning":
return css`
background: #ffca2ce6;
color: #5e5c64e6;
`;
default:
return css``;
}
};
// color, children, onClick 을 props 로 받아 사용할 것이므로 타입을 지정해주자
export const Button = styled.button<Props>`
all: unset;
display: flex;
justify-content: center;
align-items: center;
justify-self: center;
user-select: none;
cursor: pointer;
font-size: 1.6rem;
width: 15rem;
height: 4rem;
border-radius: 1rem;
transition: all 0.4s ease;
${({ color }) => getColors(color)} // props 로 받은 color 값으로 css 값을 동적으로 변경한다
box-shadow: 0.5vmin 0.5vmin 1vmin #c8d0e7, -0.5vmin -0.5vmin 1vmin #ffffff;
&:hover {
opacity: 0.9;
}
&:active {
box-shadow: 0.5vmin 0.5vmin 1vmin #c8d0e7 inset, // inset 은 그림자를 안으로 들어가게 해준다
-0.5vmin -0.5vmin 1vmin #ffffff inset;
}
`;
// color 가 undefined 일 경우, 기본으로 사용할 color props 를 정해주자
Button.defaultProps = {
color: "primary",
};
// Button.stories.tsx
import React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import { Button } from "./Button";
// export default 로 보내는 객체는 스토리북에 탭에 사용할 내용이다
// title 에는 스토리북에서 사용할 컴포넌트의 이름을 정해주고, 하위 토글이라면 / 로 들여쓰기처럼 사용가능하다
// component 에는 실제로 연결할 컴포넌트를 넣어주자
// 이 객체는 Button의 타입을 인자로 받는 ComponentMeta 타입으로 간주한다
export default {
title: "Controls/Button",
component: Button,
} as ComponentMeta<typeof Button>;
// template 는 받은 인자들로 속성을 갖는 Button 엘리먼트를 리턴해준다
// template 의 타입은 Button 의 타입을 인자로 갖는 ComponentStory 이다
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
// Template 에 빈객체에 엮어서 빈 속성의 BasicButton 을 만들어주면
// 해당 BasicButton는 사이드 탭의 토글에서 클릭하여 테스트 가능한 컴포넌트로 보여진다
// Button.tsx 에서 만든 버튼을 보며 조작해 볼 수 있다
// args 속성 값으로 객체를 추가하여, 테스트 사항을 추가 할 수 있어보인다
// 아래에서 children 은 button 의 children 과 같이 해당 요소에 보이는 텍스트이므로 수정이 가능하다
export const BasicButton = Template.bind({});
BasicButton.args = {
children: "Button",
};
이제 yarn storybook 으로 버튼 컴포넌트를 테스트해볼 수 있게 되었다 와아
아직 막 배운터라 효용은 잘 모르겠지만 말이다...
댓글