-
Notifications
You must be signed in to change notification settings - Fork 1
StoryBook
์ฐธ๊ณ ๋งํฌ
- velopert๋ธ๋ก๊ทธ
- ๊ณต์๋ฌธ์
- Say Hello to Storybook: ์คํ ๋ฆฌ๋ถ์ ํตํ React UI ์ปดํฌ๋ํธ ๊ฐ๋ฐ
ํ์ด์ง ๋จ์์ ๊ฐ๋ฐ์ด ์ด๋ฃจ์ด์ง๋ ๊ณผ๊ฑฐ์ ๋ฌ๋ฆฌ ์์ฆ์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์ฃผ๋ก ์ปดํฌ๋ํธ ๋จ์๋ก ์ด๋ฃจ์ด์ง๋ค. ์๋ ์ปดํฌ๋ํธ๋ ์ธ๋ถ ์ํ์ ์ํฅ์ ๋ฐ์ง์๋ ๋ ๋ฆฝ๋ ๊ฐ์ฒด๋ก์, ๊ณ ๋ฆฝ๋ ํ๊ฒฝ์์๋ ์์ ๋ง์ ์คํ์ผ๊ณผ ์ํ๋ฅผ ๊ฐ์ง ์ ์์ด์ผ ํ๋ค. React์ ์ปดํฌ๋ํธ ์ ์์ ๋ฐ๋ฅด๋ฉด, ์ปดํฌ๋ํธ๊ฐ UI๋ฅผ ๋ ๋ฆฝ์ ์ด๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋จ์๋ก ๋ถ๋ฆฌํ๊ณ ๊ฐ ๋จ์๋ฅผ ๊ณ ๋ฆฝํด์ ์๊ฐํ ์ ์๊ฒ ํด์ค๋ค๊ณ ์ค๋ช ํ๋ค.
์ด๋ฐ ์ ์์ ๋ฐ๋ผ์ ์ปดํฌ๋ํธ ๋จ์์ ๊ฐ๋ฐ์ container(๋ก์ง๊ณผ state) ์ presenter(container๊ฐ ๋๊ฒจ์ฃผ๋ props์ ๋ฐ๋ผ ์ค์ ๋ก ๋ณด์ฌ์ง๋ ๋ถ๋ถ)์ ๊ตฌ์ฑ์ผ๋ก ์ด๋ฃจ์ด์ง๊ฒ ๋์๋๋ฐ ์ด๋ฐ ๋ฐฉ์์ ๊ฐ๋ฐ์๊ฐ ์จ์ ํ ๋ทฐ์ ์ง์คํ๊ธฐ ์ด๋ ต๊ฒ ํ๋ค.
Storybook์ ์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋, ์ปดํฌ๋ํธ ๋จ์์ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ง์ํ๋ ๋๊ตฌ๋ค. ์์์ ๋งํ presenter๋ถ๋ถ์ ๋ ๋ฆฝ์ ์ผ๋ก ๋ฌถ์ด์ค์ผ๋ก์จ ๋ทฐ๋ฅผ ๊ฐ๋ฐํ ๋ ๊ณ ๋ฆฝ๋ ํ๊ฒฝ์ ์ ๊ณตํด์ ๊ด์ฌ์ฌ๋ฅผ ์์กด์ฑ๊ณผ ํ๊ฒฝ์ผ๋ก๋ถํฐ ๋ถ๋ฆฌ์์ผ ์ค๋ค.
๋ฆฌ์กํธ ๊ธฐ์ค
- CRA x
mkdir ํด๋์ด๋ฆ
cd ํด๋์ด๋ฆ
yarn init -y # ๋๋ npm init -y
npx -p @storybook/cli sb init --type react
- CRA o
create-react-app ์ผ๋ก ๋ง๋ ํด๋์์
npx -p @storybook/cli sb init --type react_scripts
- ์์ฑ๋ ๋๋ ํ ๋ฆฌ ๋ชจ์ต
- main.js : ์คํ ๋ฆฌ๋ฅผ ์ด๋์๋ถํฐ ๋ถ๋ฌ ์ฌ์ง ์ ํ์๋ค.
module.exports = {
stories: ['../stories/**/*.stories.js'],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
};
- srcํด๋๋ฅผ ์์ฑํ๊ณ storiesํด๋๋ฅผ src์์ผ๋ก ์ฎ๊ธด ํ ์ฝ๋๋ฅผ ์๋์ ๊ฐ์ด ๋ฐ๊พผ๋ค.
stories: ['../src/**/*.stories.js']
- ๋ฐ๋ ๋๋ ํ ๋ฆฌ ๋ชจ์ต
- ์คํ ๋ช ๋ น์ด
npm run storybook / yarn storybook
- ๊ฐ๋จํ Hello ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๊ธฐ ์ํด srcํด๋ ์๋ Helloํด๋๋ฅผ ๋ง๋ค๊ณ Hello.jsํ์ผ์ ๋ง๋ ๋ค.
- src/Hello/Hello.js
import React from 'react';
const Hello = ({ name, big }) => {
if (big) {
return <h1>์๋
ํ์ธ์, {name}!</h1>;
//big์ด๋ผ๋ props๊ฐ ์ฃผ์ด์ง๋ฉด h1์ ๋๋๋ง
}
return <p>์๋
ํ์ธ์, {name}!</p>;
//์๋๋ฉด p ๋๋๋ง
};
export default Hello;
- ์คํ ๋ฆฌ๋ฅผ ๋ง๋ ๋ค. ์คํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ํ์ฅ์๋ .stories.js์ด๋ค.
์ฌ๊ธฐ์๋ ๊ฐ๋จํ ์์ ๋ผ์ ์คํ ๋ฆฌ๊ฐ ํ๋์ ์ปดํฌ๋ํธ ๋จ์๋ก ๋ง๋ค์๋๋ฐ ์คํ ๋ฆฌ๋ ํ์ด์ง ๋จ์๊ฐ ๋ ์๋ ์๋ค. ๊ทธ๊ฒ์ ๊ฐ๋ฐ์์ ์ ํ์ด๋ค.
- src/Hello/Hello.stores.js
import React from 'react';
import Hello from './Hello';
export default {
title: 'components|basic/Hello',
// title : ์คํ ๋ฆฌ๋ถ์์ ๋ณด์ฌ์ง ๊ทธ๋ฃน๊ณผ ๊ฒฝ๋ก๋ฅผ ๋ช
์
component: Hello
// component : ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ ๋ฌธ์ํ ํ ์ง ๋ช
์
};
export const standard = () => <Hello name="Storybook" />;
export const big = () => <Hello name="Storybook" big />;
- title: export default ๋ฅผ ํตํ์ฌ ๊ฐ์ฒด๋ฅผ ๋ด๋ณด๋ผ ๋, title ๊ฐ์ ์คํ ๋ฆฌ๋ถ์์ ๋ณด์ฌ์ง๋ ๊ทธ๋ฃน๊ณผ ๊ฒฝ๋ก๋ฅผ ๋ช ์ํ๋ค.
- ๊ฒฐ๊ณผ ํ๋ฉด
https://storybook.js.org/addons/ addon์ ๋ํ ๊ณต์๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์๋ ๊ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค!
์ปดํฌ๋ํธ์ props ๋ฅผ ์คํ ๋ฆฌ๋ถ ํ๋ฉด์์ ๋ฐ๊ฟ์ ๋ฐ๋ก ๋ฐ์์์ผ์ค ์ ์๋ ์ ๋์จ
- ์ค์น ๋ฐฉ๋ฒ
- ๋ช ๋ น์ด ์คํ
yarn add --dev @storybook/addon-knobs
๋๋ npm install --save-dev @storybook/addon-knobs
- .storybookํด๋์ main.js์ addons์
'@storybook/addon-knobs/register'
์ถ๊ฐํ๋ค.
-
withKnob๋ผ๋ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋์จ์ ์ ์ฉ
- src/Hello/Hello.stories.js
import React from 'react'; import Hello from './Hello'; import { withKnobs } from '@storybook/addon-knobs'; export default { title: 'components|basic/Hello', // ์คํ ๋ฆฌ๋ถ์์ ๋ณด์ฌ์ง ๊ทธ ๋ฃน๊ณผ ๊ฒฝ๋ก๋ฅผ ๋ช ์ component: Hello, // ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ ๋ฌธ์ํ ํ ์ง ๋ช ์ decorators: [withKnobs] // ์ ๋์จ ์ ์ฉ //export default๋ก ๋ด๋ณด๋ด๋ ๊ฐ์ฒด ์์ decorators ๋ผ๋ ๋ฐฐ์ด์ ๋ง๋ค์ด์ ๊ทธ ์์ withKnobs ๋ฅผ ๋ฃ์ด์ค๋ค. }; export const standard = () => <Hello name="Storybook" />; export const big = () => <Hello name="Storybook" big />;
-
default ๋ผ๋ ์ด๋ฆ์ผ๋ก Knobs๋ฅผ ์ฌ์ฉํ๋ ์๋ก์ด ์คํ ๋ฆฌ ๋ง๋ค๊ธฐ
- src/Hello/Hello.stories.js
(...) export default { title: 'components|basic/Hello', // ์คํ ๋ฆฌ๋ถ์์ ๋ณด์ฌ์ง ๊ทธ๋ฃน๊ณผ ๊ฒฝ๋ก๋ฅผ ๋ช ์ component: Hello, // ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ ๋ฌธ์ํ ํ ์ง ๋ช ์ decorators: [withKnobs] // ์ ๋์จ ์ ์ฉ }; export const hello = () => { // knobs ๋ง๋ค๊ธฐ const big = boolean('big', false); const name = text('name', 'Storybook'); return <Hello name={name} big={big} />; }; hello.story = { name: 'Default' }; (...)
- ๊ฒฐ๊ณผ
- ์๋ Knobsํจ๋์์ big์ name์ ๋ฐ๊ฟ์ state๋ฅผ ์ง์ ๋ณ๊ฒฝํ ์ ์๋ค!
-
Knobs ๋ฅผ ์ฌ์ฉ ํ ๋ ํ์ํ ์ฃผ์ ์ธ์
- Knobs ์ ์ด๋ฆ
- ๊ธฐ๋ณธ๊ฐ
- GROUP ID (optional)
- ์์
const big = boolean('big', false, 'Group 1');
์ปดํฌ๋ํธ๋ฅผ ํตํ์ฌ ํน์ ํจ์๊ฐ ํธ์ถ๋์ ๋ ์ด๋ค ํจ์๊ฐ ํธ์ถ๋๋์ง, ๊ทธ๋ฆฌ๊ณ ํจ์์ ์ด๋ค ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฃ์ด์ ํธ์ถํ๋์ง์ ๋ํ ์ ๋ณด๋ฅผ ํ์ธ ํ ์ ์๊ฒ ํ๋ ์ ๋์จ
- ์ก์ ์ ๋ง๋ค ๋๋ **action('์ก์ ์ด๋ฆ')**์ด๋ผ ํ๋ค.
- src/Hello/Hello.js
(...)
const Hello = ({ name, big, onHello, onBye }) => {
return (
<div>
{big ? <h1>์๋
ํ์ธ์, {name}!</h1> : <p>์๋
ํ์ธ์, {name}!</p>}
<div>
<button onClick={onHello}>Hello</button>
<button onClick={onBye}>Bye</button>
</div>
</div>
);
};
(...)
- src/Hello/Hello.js
(...)
import { action } from '@storybook/addon-actions';
export default {
title: 'components|basic/Hello', // ์คํ ๋ฆฌ๋ถ์์ ๋ณด์ฌ์ง ๊ทธ๋ฃน๊ณผ ๊ฒฝ๋ก๋ฅผ ๋ช
์
component: Hello, // ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ ๋ฌธ์ํ ํ ์ง ๋ช
์
decorators: [withKnobs] // ์ ๋์จ ์ ์ฉ
};
export const hello = () => {
// knobs ๋ง๋ค๊ธฐ
const big = boolean('big', false);
const name = text('name', 'Storybook');
return (
<Hello
name={name}
big={big}
onHello={action('onHello')}
onBye={action('onBye')}
/>
);
};
(...)
- ๊ฒฐ๊ณผ
story source๋ฅผ ํจ๋์์ ๋ณผ์ ์๋ค.
- ์ค์น ๋ฐฉ๋ฒ
- ๋ช ๋ น์ด ์คํ
yarn add --dev @storybook/addon-storysource
๋๋ npm install --save-dev @storybook/addon-storysource
- .storybookํด๋์ main.js์ addons์
'@storybook/addon-storysource'
์ถ๊ฐํ๋ค.
- ๊ฒฐ๊ณผ
์์ด ๋ง์์ ๊ณ์ ์ ๋ฐ์ดํธ ํ๊ฒ ์ต๋๋คใ ใ