๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ“ Study/๐Ÿฉบ Testing

React ํ”„๋กœ์ ํŠธ์— Storybook ์ผ๋‹จ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ! with TS

๐Ÿ’กStorybook์— ๋Œ€ํ•œ ์ดํ•ด์™€ ํ•™์Šต๋ณด๋‹ค ๋น ๋ฅธ ์ ์šฉ์„ ๋ชฉ์ ์œผ๋กœ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค!
โ˜ ๏ธ Storybook ํ•™์Šต ์ค‘์— ์ž‘์„ฑํ•œ ๊ธ€์ด๋ฏ€๋กœ ๋‚ด์šฉ์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์ปดํฌ๋„ŒํŠธ์˜ props ๊ฐ’์„ ํŽธํ•˜๊ฒŒ ์ˆ˜์ •ํ•˜๋ฉด์„œ ์–ด๋–ป๊ฒŒ ๋ Œ๋”๋ง ๋˜๋Š”์ง€ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋Š” ์Šคํ† ๋ฆฌ๋ถ์€ ์จ๋ณด๋ฉด ์ •๋ง ํŽธํ•˜๊ณ  ์ข‹๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ๊ฐœ๋ฐœ์„ ํ•  ์ˆ˜ ์žˆ๊ณ  ๋˜ ์ผ์ข…์˜ ํ”„๋กœ์ ํŠธ ๋ฌธ์„œํ™” ์ž‘์—…๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ฒฝํ—˜์ƒ ์ฒ˜์Œ ์จ๋ณด๋ ค๊ณ  ํ• ๋•Œ ์Šคํ† ๋ฆฌ๋ถ์ด ์ •ํ™•ํžˆ ๋ญ”์ง€๋„ ๋ชจ๋ฅด๊ฒ ๊ณ  ์–ด๋–ป๊ฒŒ ์‹œ์ž‘ํ•ด์•ผ ํ• ์ง€ ๊ฐ์ด ์ž˜ ์•ˆ ์˜ค๋Š”๋ฐ ๋ฌด์—‡๋ณด๋‹ค ์Šคํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋”ฐ๋กœ ์ž‘์„ฑ์„ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ถ€๋‹ด๊ฐ์— ์„ ๋œป ์‹œ์ž‘ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์šด ๊ฑฐ ๊ฐ™๋‹ค.

ํ•˜์ง€๋งŒ ๋ง‰์ƒ ํ•œ๋ฒˆ ์ ์šฉํ•ด์„œ ์ด๊ฑฐ์ €๊ฑฐ ๊ฑด๋“œ๋ ค๋ณด๋ฉฐ ์–ด๋Š ์ •๋„ ์ ์‘ํ•˜๊ณ  ๋‚˜๋ฉด ๊ทธ๋ƒฅ ๊ณต์žฅ์ฒ˜๋Ÿผ ์ฐ์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค! ^-^)ใ…Ž

์•„์ง ์Šคํ† ๋ฆฌ๋ถ์˜ ๊ธฐ๋Šฅ์„ 10%๋„ ์ œ๋Œ€๋กœ ํ™œ์šฉ ์•ˆํ•˜๊ณ  ์žˆ๋Š”๋ฐ๋„ ๋„ˆ๋ฌด ์žฌ๋ฐŒ๊ณ  ํŽธํ•ด์„œ ์‹œ์ž‘ํ•˜๊ธฐ ๋ง‰๋ง‰ํ•œ ๋ถ„๋“ค์—๊ฒŒ ๋„์›€์ด ๋์œผ๋ฉด ํ•ด์„œ ์ง€๊ธˆ ๋‹น์žฅ ๊นŠ์€ ์ƒ๊ฐ์ด๋‚˜ ๊ณ ๋ฏผ ์—†์ด ๋ฐ”๋กœ ์ ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์ ์šฉ ๊ณผ์ •์„ ์ •๋ฆฌํ•ด๋ดค๋‹ค.

 

1. ์„ค์น˜

npx -p @storybook/cli sb init
npx sb init

์œ„์˜ ๋ช…๋ น์–ด ์ค‘ ํ•˜๋‚˜๋ฅผ ํ„ฐ๋ฏธ๋„ ์ฐฝ์— ์ž…๋ ฅํ•ด ์„ค์น˜ํ•œ๋‹ค.

์„ค์น˜ ์†Œ์š” ์‹œ๊ฐ„์ด ์กฐ๊ธˆ ์žˆ๋Š”๋ฐ ํ•œ๋ฒˆ์— ์„ค์น˜๋˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ค‘๊ฐ„์ค‘๊ฐ„ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์˜ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์‘๋‹ต์„ ์š”๊ตฌํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

์„ค์น˜๋˜๋Š”๊ฑธ ์ž˜ ๋ณด๋‹ค๊ฐ€ ๊ฐ‘์ž๊ธฐ ๋ฉˆ์ถ”๋ฉด ์œ ์‹ฌํžˆ ๋กœ๊ทธ๋ฅผ ์‚ดํŽด๋ณด์ž!

์ฐธ๊ณ ๋กœ ์Šคํ† ๋ฆฌ๋ถ ์„ค์น˜๋Š” CRA๋‚˜ ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ ์„ธํŒ…์„ ๋๋‚ด๊ณ  ํ•˜๋Š” ๊ฑธ ์ถ”์ฒœํ•œ๋‹ค.

ํ”„๋กœ์ ํŠธ ์„ธํŒ…์ด ๋๋‚˜์žˆ์œผ๋ฉด ์Šคํ† ๋ฆฌ๋ถ์ด ์•Œ์•„์„œ ํ”„๋กœ์ ํŠธ ํƒ€์ž…์„ ํƒ์ƒ‰ํ•˜๊ณ  ๊ทธ์— ๋งž์ถฐ์„œ ์„ค์น˜ํ•ด์ค€๋‹ค!

 

๐Ÿ’ฌ ์„ค์น˜ ์ค‘ ๋‚ด๊ฐ€ ๋ฐ›์•˜๋˜ ์งˆ๋ฌธ๋“ค

1. ์ฒ˜์Œ ์„ค์น˜ ํ•  ํŒจํ‚ค์ง€ ์ฒดํฌ ์งˆ๋ฌธ

Need to install the following packages:
       sb           // ๋˜๋Š” @storybook/cli
Ok to proceed? (y)  // y ๋ˆ„๋ฅด๋ฉด ์„ค์น˜ ์‹คํ–‰

2. Storybook ํ”„๋กœ์ ํŠธ ํƒ€์ž… ๋ฌผ์–ด๋ด„

๋งŒ์•ฝ ์ด ์งˆ๋ฌธ์„ ๋ฐ›์•˜๋‹ค๋ฉด ์ง€๊ธˆ ๋‚ด ๊ฒฝ๋กœ๊ฐ€ ์ œ๋Œ€๋กœ React ํ”„๋กœ์ ํŠธ ํด๋”๊ฐ€ ๋งž๋Š”์ง€ ํ•œ๋ฒˆ ์ฒดํฌํ•ด๋ณด์ž!

 

React ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ์— ์žˆ์œผ๋ฉด ๋งจ ์ฒ˜์Œ์— ์•Œ์•„์„œ ํ”„๋กœ์ ํŠธ ํƒ€์ž…์„ ํƒ์ƒ‰ํ•˜๊ณ  react ํƒ€์ž…์œผ๋กœ ์„ค์น˜๋ฅผ ํ•ด์ค€๋‹ค.

2. exlintPlugin migration

๐Ÿ”Ž found a 'eslintPlugin' migration:

We've detected you are not using our eslint-plugin.   
In order to have the best experience with Storybook and follow best practices, we advise you to install eslint-plugin-storybook.  
More info: https://github.com/storybookjs/eslint-plugin-storybook#readme

? Do you want to run the 'eslintPlugin' migration on your project? › (y/N)  // y ๋ˆ„๋ฅด์ž

 

๐Ÿ”” ์„ค์น˜ ์™„๋ฃŒ

To run your Storybook, type:

   npm run storybook 

For more information visit: https://storybook.js.org

ํ„ฐ๋ฏธ๋„์— ์œ„์˜ ๋กœ๊ทธ๊ฐ€ ๋œจ๋ฉด ์„ค์น˜ ์™„๋ฃŒ๋‹ค!

์Šคํ† ๋ฆฌ ๋ถ์ด ์„ค์น˜๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋๋‚˜๋ฉด ์„ค์น˜ํ•œ ๊ฒฝ๋กœ์— ์•„๋ž˜์˜ ํŒŒ์ผ๋“ค์ด ์ƒ์„ฑ๋œ๋‹ค!

์ด๊ฒŒ ๋ญ์ง€ ์‹ถ์€๋ฐ ๋ณ„๊ฑฐ ์—†๋‹ค.

.storybook์€ ์Šคํ† ๋ฆฌ๋ถ์˜ ํ™˜๊ฒฝ ์„ค์ • ํŒŒ์ผ์ด ๋“ค์–ด์žˆ๋Š” ํด๋”๊ณ 

stories๋Š” ์Šคํ† ๋ฆฌ๋ถ์˜ ๊ธฐ๋ณธ ์˜ˆ์ œ ์ฝ”๋“œ๋“ค์ด ๋“ค์–ด์žˆ๋Š” ํด๋”๋‹ค.

์Šคํ† ๋ฆฌ๋ถ์ด ์นœ์ ˆํ•˜๊ฒŒ ์ด๋Ÿฐ ์‹์œผ๋กœ ์ž‘์„ฑํ•˜๋ผ๊ณ  ๋‹ค ์•Œ๋ ค์ค€๋‹ค.

^-^)b ์Šคํ† ๋ฆฌ๋ถ ์ตœ๊ณ !

 

์–ด์จŒ๋“  ์ค‘์š”ํ•œ๊ฑด .storybook์ด๊ณ  stories๋Š” ์‹ค์งˆ์ ์œผ๋ก  ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค.

 

๋”ฐ๋ผ์„œ ํด๋” ์ฑ„๋กœ ์ง€์›Œ๋ฒ„๋ ค๋„ ๋˜....์ง€๋งŒ!!!

์ง€์šฐ๊ธฐ ์ „์— ์Šคํ† ๋ฆฌ๋ถ์ด ์ œ๋Œ€๋กœ ์„ค์น˜๋๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•ด๋ณด๊ณ  ์ง€์šฐ๋Š”๊ฑธ ์ถ”์ฒœํ•œ๋‹ค!

 

๐Ÿ˜‹ ์ž˜ ์„ค์น˜ ๋๋Š”์ง€ ์‹คํ–‰ํ•ด๋ณด๊ธฐ!

npm run storybook

์•„๊นŒ ์ „ ์„ค์น˜ ์™„๋ฃŒ ๋ฉ”์„ธ์ง€์—์„œ ์•Œ๋ ค์คฌ๋˜ ๊ทธ ๋ช…๋ น์–ด๋ฅผ ํ„ฐ๋ฏธ๋„์— ์ž…๋ ฅํ•ด๋ณธ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด ์Šคํ† ๋ฆฌ๋ถ์ด ๋ญ”๊ฐ€ ์œ™์œ™ํ•˜๋ฉด์„œ ์—ด์‹ฌํžˆ ์ผ์„ ํ•˜๋‹ค๊ฐ€ 

 

 

React ํ”„๋กœ์ ํŠธ ์‹คํ–‰ํ–ˆ์„ ๋•Œ์ฒ˜๋Ÿผ ํ‰! ๐ŸŽ‰ ํ•˜๊ณ  ์Šคํ† ๋ฆฌ๋ถ์ด ์ผœ์ง„๋‹ค.

๋งŒ์•ฝ ํ„ฐ๋ฏธ๋„์€ ๋ฉˆ์ท„๋Š”๋ฐ๋„ ์ฐฝ์ด ์•ˆ ๋œจ๋ฉด ๋‹นํ™ฉํ•˜์ง€ ๋ง๊ณ  ํ„ฐ๋ฏธ๋„์„ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ€์„œ

Storybook์— ํ• ๋‹น๋œ ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ์ฐพ์•„์„œ ์ˆ˜๋™์œผ๋กœ localhost:ํฌํŠธ๋ฒˆํ˜ธ ์ฃผ์†Œ๋กœ ์ ‘์†ํ•˜๋ฉด ๋œ๋‹ค!

์ฐธ๊ณ ๋กœ ์Šคํ† ๋ฆฌ๋ถ์„ ํ•œ๋ฒˆ ์‹คํ–‰ํ•˜๋ฉด ์ €์žฅํ•  ๋•Œ๋งˆ๋‹ค React ํ”„๋กœ์ ํŠธ์ฒ˜๋Ÿผ ์ž๋™์œผ๋กœ ์žฌ์‹คํ–‰๋œ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋Œ€๋กœ ๊ทธ๋ƒฅ ๊ณ„์† ์ผœ ๋‘๋Š” ๊ฑฐ ์ถ”์ฒœ!

 

2. ์Šคํ† ๋ฆฌ๋ถ ์ดˆ๊ธฐ ์„ค์ •

๐ŸŽ€ Global Style ๊ณผ ThemeProvider ์ ์šฉ

const root = createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <ThemeProvider theme={defaultTheme}>
      <GlobalStyle />
      <App />
    </ThemeProvider>
  </React.StrictMode>,
);

 

๋งŒ์•ฝ ํ˜„ ํ”„๋กœ์ ํŠธ์—์„œ GlobalStyle์ด๋‚˜ ThemeProvider๋ฅผ ์ด์šฉํ•ด Default Style์„ ์ ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์Šคํ† ๋ฆฌ๋ถ์—๋„ ์ ์šฉ์ด ๋˜๋„๋ก ๋”ฐ๋กœ ์„ค์ •์„ ํ•˜๋‚˜ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์–ด๋ ต๊ณ  ๋ณต์žกํ•œ ๊ฑด ์—†๊ณ  ๊ทธ๋ƒฅ ์•„๊นŒ ์„ค์น˜๋๋˜ ์Šคํ† ๋ฆฌ๋ถ์˜ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ จ ํด๋”์ธ .storybook ๋‚ด์˜ preview.js ํŒŒ์ผ๋กœ ๋“ค์–ด๊ฐ€ ThemProvider, GlobalStyle, Default Style์„ import ํ•˜๊ณ  ๋งจ ์•„๋ž˜์— ์ด ์ฝ”๋“œ๋ฅผ ๋ณต๋ถ™ ํ•ด์„œ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

export const decorators = [
  (Story) => (
    <ThemeProvider theme={defaultTheme}>
      <GlobalStyle />
      <Story />
    </ThemeProvider>
  ),
];

์ด๋Ÿฐ ์‹์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค!!!!

 

decorators๋Š” ๋Œ€์ถฉ ์Šคํ† ๋ฆฌ๋ถ์—์„œ ๋ Œ๋”๋ง ํ•  ๋•Œ ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ ์™ธ๋กœ ์ถ”๊ฐ€์ ์ธ ์Šคํƒ€์ผ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์ด๊ฑธ preview.js์— ์ ์šฉํ•˜๋ฉด ์•ž์œผ๋กœ ์Šคํ† ๋ฆฌ๋ถ์œผ๋กœ ๋„์šธ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์—๊ฒŒ Global decorators๋ฅผ ๋‹ฌ์•„์ฃผ๋Š” ๊ฑฐ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

์กฐ๊ธˆ ๋” ์ •ํ™•ํ•œ ๋‚ด์šฉ์€ ๊ณต์‹ ์‚ฌ์ดํŠธ์˜ Decorators ๊ธ€์„ ์ฐธ๊ณ !

 

๐Ÿงญ ์ ˆ๋Œ€ ๊ฒฝ๋กœ ์„ค์ •

์–ธ์ œ๋‚˜ ๊ณจ์น˜ ์•„ํ”„๊ฒŒ ํ•˜๋Š” ๊ฒฝ๋กœ ์„ค์ •!

์Šคํ† ๋ฆฌ๋ถ์—์„œ๋„ ์ ˆ๋Œ€๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋”ฐ๋กœ ์„ค์ •์„ ํ•ด์ค˜์•ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค!

๊ทผ๋ฐ ์‚ฌ์‹ค ๋‚˜๋Š” ๋”ฑํžˆ ๋ญ ์„ค์ • ์•ˆ ํ•ด์คฌ๋Š”๋ฐ ์Šคํ† ๋ฆฌ ์ฝ”๋“œ ๋‚ด์—์„œ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์ž˜ ์ ์šฉ์ด ๋œ๋‹ค...ใ…Ž-ใ…Ž)?

๋‚ด๊ฐ€ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฑด๊ฐ€ ์‹ถ๊ธด ํ•œ๋ฐ...

 

๊ทธ๋ž˜์„œ ๋งŒ์•ฝ ์„ค์ •ํ•ด์ฃผ๋Š” ๊ฒŒ ๊ท€์ฐฎ๋‹ค๋ฉด ์ผ๋‹จ ์ด ์„ค์ •์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์ž‘์—…์„ ๋จผ์ € ํ•ด๋ณด๋‹ค ๊ฒฝ๋กœ๋กœ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ๊ทธ๋•Œ ์‹œ๋„ํ•ด๋ด๋„ ์ข‹์„ ๊ฑฐ ๊ฐ™๋‹ค. ์•„๋‹ˆ๋ฉด ์ฒ˜์Œ์—” ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ๋จผ์ € ํ•ด๋ณด๋Š” ๊ฒƒ๋„ ๋‚˜์˜์ง€ ์•Š์„ ์ง€๋„...!

 

1. tsconfig์— ๊ฒฝ๋กœ ์„ค์ •์„ ํ•œ๋‹ค.

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["components/*"],
    }
  }
}

2. '.storybook' ํด๋” ๋‚ด์˜ main.js ํŒŒ์ผ์—์„œ ์„ค์ •ํ•ด์ค€๋‹ค.

์ดˆ๊ธฐ main.js๋ฅผ ๋ณด๋ฉด ์ด๋ ‡๊ฒŒ ๋˜์–ด ์žˆ๋Š”๋ฐ!

๋จผ์ € path ๋ชจ๋“ˆ์„ import ํ•ด์ฃผ๊ณ  core ์•„๋ž˜์— ์ด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค!

const path = require('path');

...

core: {
    builder: '@storybook/builder-webpack5',
},
/* ์š”๊ฑฐ ์ถ”๊ฐ€! */
webpackFinal: async config => {
  config.resolve.modules = [path.resolve(__dirname, '..'), 'node_modules', 'styles'];
  config.resolve.alias = {
    ...config.resolve.alias,
    '@components': path.resolve(__dirname, '../src/components')
  };
  return config;
 },

๋” ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ๋กœ๊ฐ€ ์žˆ๋‹ค๋ฉด config.resolve.alias ์•„๋ž˜์— '์„ค์ •ํ•˜๋ ค๋Š” ๊ฒฝ๋กœ ๋ณ„์นญ' : ์‹ค์ œ ๊ฒฝ๋กœ ํ˜•ํƒœ'๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ path ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๋ฐ ๋ฌด์Šจ TsconfigPathPlugin์„ ์‚ฌ์šฉํ•ด์„œ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ณ , ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค๊ณ  ํ•˜๋Š” ๊ฑฐ ๊ฐ™๋‹ค.

  webpackFinal: async (config) => {
    config.resolve.modules = [...(config.resolve.modules || []), path.resolve(__dirname, '../src')];
    return config;
  },

์ด๊ฑฐ ์ €๊ฑฐ ํ•ด๋ณด๊ณ  ๋ณธ์ธ ํ”„๋กœ์ ํŠธ์— ๋งž๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋  ๊ฑฐ ๊ฐ™๋‹ค.

 

3. ์Šคํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

๋“œ๋””์–ด ์‹ค์ „์ด๋‹ค!

ํ•˜์ง€๋งŒ TypeScript๋กœ ์ž‘์—…์„ ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ํฌ๊ฒŒ ๊ฑฑ์ •ํ• ๊ฒŒ ๋”ฑํžˆ ์—†๋‹ค!

JavaScript ๋ฅผ ํ•  ๋•Œ๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์— ์ผ์ผ์ด ํƒ€์ž…์„ ์ง€์ •ํ•ด์ค˜์•ผ ์ œ๋Œ€๋กœ ์Šคํ† ๋ฆฌ ๋ถ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ

์ด๋ฏธ TypeScript๋กœ ํƒ€์ž…์„ ๋‹ค ์ง€์ •ํ•ด๋†จ์œผ๋ฉด ๊ทธ ๊ณผ์ •์„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์–ด์„œ ์•„์ฃผ ์ข‹๋‹ค!

 

๐Ÿ—‚ ์Šคํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ํŒŒ์ผ ์œ„์น˜

src ์•„๋ž˜์—๋งŒ ์žˆ์œผ๋ฉด ๋”ฑํžˆ ์ƒ๊ด€์—†๋‹ค!

์ฒ˜์Œ ์Šคํ† ๋ฆฌ๋ถ์„ ์„ค์น˜ํ–ˆ์„ ๋•Œ ์ƒ๊ฒผ๋˜ src ํด๋” ์•„๋ž˜์˜ stories ํด๋”๋ฅผ ์žฌํ™œ์šฉํ•ด๋„ ๋˜๊ณ  ์•„๋‹ˆ๋ฉด ๊ทธ๋ƒฅ ์ž‘์„ฑํ•œ ์ปดํฌ๋„ŒํŠธ .tsx ํŒŒ์ผ๊ณผ ํ•จ๊ป˜ ๋‘ฌ๋„ ๋œ๋‹ค.

์ค‘์š”ํ•œ ๊ฑด ํŒŒ์ผ์˜ ์ด๋ฆ„์— .stories ๊ฐ€ ๋“ค์–ด๊ฐ€๋Š๋ƒ๋‹ค.

์—ฌ๊ธฐ์„œ ์Šคํ† ๋ฆฌ๋ถ์˜ ํ™˜๊ฒฐ ์„ค์ • ํŒŒ์ผ main.js๋ฅผ ๋‹ค์‹œ ์‚ดํŽด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์Šคํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ํŒŒ์ผ ๋ช…์— ๋Œ€ํ•œ ์„ค์ •๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. (๋ฌผ๋ก  ์›ํ•˜๋Š”๋Œ€๋กœ ์ˆ˜์ •๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค!)

๋‚˜๋Š” ๋”ฐ๋กœ ์Šคํ† ๋ฆฌ ํด๋”๋ฅผ ๋งŒ๋“ค์–ด ๋‘์ง€ ์•Š๊ณ  ์•„๋ž˜์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค!

ํ•œ๊บผ๋ฒˆ์— ๋ชจ์•„๋‘๋Š” ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ํŽธํ•œ ๊ฑฐ ๊ฐ™๋‹ค ^-^)b

๊ทผ๋ฐ ๋‚˜์ค‘์— ์Šคํ† ๋ฆฌ ํŒŒ์ผ๋“ค์„ ๋‹ค ์—†์• ์•ผ ํ•˜๊ฑฐ๋‚˜ ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ๊ทธ๋ƒฅ ํ•œ ๊ณณ์— ๋ชจ์•„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ๋‚˜์„ ๊ฒƒ ๊ฐ™๊ธฐ๋„ ํ•˜๋‹ค!

 

๐Ÿ“ ์Šคํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ! with ํ…œํ”Œ๋ฆฟ ์ฝ”๋“œ

์–ด๋ ค์šธ ๊ฑฐ ํ•˜๋‚˜ ์—†๋‹ค!

๋‚˜๋Š” ์™„์ „ ๊ฐ„๋‹จํ•œ ํ…œํ”Œ๋ฆฟ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋‘๊ณ  ์ด๊ฑธ ๋ณต๋ถ™ ํ•ด์„œ ๊ณต์žฅ์ฒ˜๋Ÿผ ์ฐ์–ด๋‚ด๊ณ  ์žˆ๋‹คใ…Žใ…Ž!

import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ์ปดํฌ๋„ŒํŠธ from '์ปดํฌ๋„ŒํŠธ.tsx ๊ฒฝ๋กœ';

export default {
  title: '์นดํ…Œ๊ณ ๋ฆฌ์ด๋ฆ„/์ปดํฌ๋„ŒํŠธ์ด๋ฆ„',
  component: ์ปดํฌ๋„ŒํŠธ,
} as ComponentMeta<typeof ์ปดํฌ๋„ŒํŠธ>;

const Template: ComponentStory<typeof ์ปดํฌ๋„ŒํŠธ> = (args) => <์ปดํฌ๋„ŒํŠธ {...args} />;

export const ์ปค์Šคํ…€ = Template.bind({});
์ปค์Šคํ…€.args = {
	๊ฐ’์„ ์ง€์ •ํ•˜๊ณ  ์‹ถ์€ ์†์„ฑ : ์†์„ฑ๊ฐ’,
};
์ปค์Šคํ…€.storyName = '์Šคํ† ๋ฆฌ๋ถ์— ํ‘œ์‹œํ•  ์ด๋ฆ„';

export const Default: ComponentStory<typeof ์ปดํฌ๋„ŒํŠธ> = (args) => <์ปดํฌ๋„ŒํŠธ {...args} />;

์œ„์˜ ์ฝ”๋“œ์—์„œ ํ•œ๊ธ€๋กœ ๋˜์–ด ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•ด์„œ ์ž‘์„ฑํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ์Šคํ† ๋ฆฌ๋ถ์— ๊ทธ๋ƒฅ ๋œฌ๋‹ค! ^-^)b

 

์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ์“ฐ๋Š” ๊ฑด์ง€ ์ข€ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ฉด!

์˜ˆ๋ฅผ ๋“ค์–ด ์œ„์™€ ๊ฐ™์€ props ์†์„ฑ์„ ๊ฐ€์ง€๋Š” WeekList๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ!

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณต๋ถ™ ํ•ด์„œ ํ•œ๊ธ€๋กœ ๋œ ๋ถ€๋ถ„์„ ์ˆ˜์ •์„ ํ•˜๊ณ  ์™ผ์ชฝ์ฒ˜๋Ÿผ ์ž‘์„ฑ์„ ํ•˜๋ฉด ์˜ค๋ฅธ์ชฝ์ฒ˜๋Ÿผ ๋ฟ…! ๋œฌ๋‹ค.

^-^) ใ…Žใ…Žใ…Žใ…Ž์ฐธ ์‰ฝ๋‹คใ…Žใ…Žใ…Žใ…Žใ…Ž

์ฐธ๊ณ ๋กœ ์Šคํ† ๋ฆฌ๋ถ ํ•˜๋‹จ์˜ Controls์— ๋œจ๋Š” ์†์„ฑ๋“ค์€ ์ „๋ถ€ weekList.tsx ์—์„œ ์„ค์ •ํ•œ weekList ์˜ props ํƒ€์ž…์—์„œ ์ž๋™์œผ๋กœ ๊ฐ€์ ธ์™€์ง„ ๊ฑฐ๋‹ค. ๋งŒ์•ฝ ์ด๊ฒŒ TS๊ฐ€ ์•„๋‹ˆ๊ณ  JavaScript ์˜€๋‹ค๋ฉด...propsTypes ๋กœ ํƒ€์ž…์„ ๋‹ค ์ง€์ •์„ ํ•ด์ค˜์•ผ ์ œ๋Œ€๋กœ ๋œฌ๋‹ค...! ใ…  -ใ… )!

 

์œ„์˜ ์ฝ”๋“œ์— ๋Œ€ํ•ด ์ข€ ๋” ์„ค๋ช…์„ ํ•ด๋ณด์ž๋ฉด

์ด๋Ÿฐ ๊ตฌ์กฐ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค!

๋นจ๊ฐ„์ƒ‰์œผ๋กœ ์ฒดํฌํ•œ title์—์„œ ์Šคํ† ๋ฆฌ๋ถ์— ์†Œ์†๋  ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ง€์ •ํ•˜๋Š”๋ฐ / ๋กœ ๊ตฌ๋ถ„ํ•ด์„œ ๋Œ€๋ถ„๋ฅ˜, ์†Œ๋ถ„๋ฅ˜๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

(์ฆ‰ title์— Calender/ ๋ผ๊ณ  ์“ด ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ œ์ผ ํฐ ์นดํ…Œ๊ณ ๋ฆฌ์ธ Calender์— ํฌํ•จ๋œ๋‹ค!)

 

๊ทธ๋ฆฌ๊ณ  ๊ทธ ์•„๋ž˜์—๋Š” ์ž‘์„ฑํ•œ ์Šคํ† ๋ฆฌ ์ฝ”๋“œ์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ํ‘œ์‹œ๊ฐ€ ๋˜๋Š”๋ฐ

๋งจ ์•„๋ž˜์— ๋ณด๋ผ์ƒ‰์œผ๋กœ ์ฒดํฌํ•œ ์•„๋ฌด๋Ÿฐ ์†์„ฑ์„ ์ง€์ •ํ•˜์ง€ ์•Š์€ ์Šคํ† ๋ฆฌ๊ฐ€ Default๋กœ ์ถ”๊ฐ€๋˜๊ณ 

๊ทธ ์ค‘๊ฐ„์— Template๋กœ ์ƒ์„ฑํ•œ ์Šคํ† ๋ฆฌ ๊ฐ์ฒด? WeekListEng๊ฐ€ args์— ์„ค์ •ํ•œ ์†์„ฑ ๊ฐ’์ด ์ ์šฉ๋œ ์ƒํƒœ๋กœ,

storyName์— ์„ค์ •ํ•œ ์ด๋ฆ„์œผ๋กœ ๋œฌ๋‹ค!

 

ํ•„์ˆ˜ ์ฝ”๋“œ๋Š” ๋”ฑ ์ด ๋ถ€๋ถ„์ด๊ณ  

import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import WeekList from 'components/calender/components/WeekList';
import weekData from 'components/calender/constants/weekData';

export default {
  title: 'Calender/WeekList',
  component: WeekList,
} as ComponentMeta<typeof WeekList>;

export const Default: ComponentStory<typeof WeekList> = (args) => <WeekList {...args} />;

 

์—ฌ๊ธฐ์„œ ๋˜ ๋‹ค๋ฅธ ์Šคํ† ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์œ„์˜ WeekListEng๋ฅผ ๋ณต์‚ฌํ•ด์„œ Template๋กœ ์Šคํ† ๋ฆฌ ๊ฐ์ฒด๋“ค์„ ์ƒ์„ฑํ•ด์„œ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค! ์•„๋ž˜์ฒ˜๋Ÿผ!

const Template: ComponentStory<typeof WeekList> = (args) => <WeekList {...args} />;

export const WeekListEng = Template.bind({});
WeekListEng.args = {
  data: weekData.eng,
};
WeekListEng.storyName = 'WeekList(Eng)';

/* ์Šคํ† ๋ฆฌ ์ƒˆ๋กœ ์ถ”๊ฐ€ */
export const WeekListJp = Template.bind({});
WeekListJp.args = {
  data: weekData.jp,
};
WeekListJp.storyName = 'WeekList(Jp)';

 

์™€์•„์•„์•™!! ์ถ”๊ฐ€๋๋‹ค!!! ^3^)!!!!

 

๋‚˜๋Š” ํ˜„์žฌ ์ฝ”๋“œ์Šค์ฟผ๋“œ์—์„œ ์ง„ํ–‰ ์ค‘์ธ 3์ฃผ ๋ฏธ์…˜์—์„œ ์ด ์ •๋„๊นŒ์ง€๋งŒ ํ•ด์„œ ์Šคํ† ๋ฆฌ๋ถ์„ ํ™œ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

 

์Šคํ† ๋ฆฌ๋ถ์— ๋Œ€ํ•ด์„œ ์ข€ ๋” ์ฐพ์•„๋ณด๋ฉด ์Šคํ† ๋ฆฌ๋ถ์— ๋œจ๋Š” Controls ๋“ค์˜ ํƒ€์ž…์„ ํŽธํ•œ ๊ฑธ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜๋„ ์žˆ๊ณ 

decorators ์†์„ฑ์„ ์‚ฌ์šฉํ•ด ์Šคํ† ๋ฆฌ๋ถ ๋‚ด์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ข€ ๋” ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ์ปค์Šคํ…€ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์ผ์ข…์˜ ์ต์Šคํ…์…˜์ธ addon์„ ์ถ”๊ฐ€ํ•ด ์†Œ์Šค ์ฝ”๋“œ๋„ ๊ฐ™์ด ๋„์šฐ๊ฑฐ๋‚˜, Readme๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๊ฑฐ๋‚˜ ๋“ฑ ๋ฉ‹์ง€๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๊ธด ํ•˜์ง€๋งŒ...!

๋ฏธ์…˜ ํ•˜๊ธฐ๋„ ๋ฐ”์˜๊ณ  ์•„์ง ๋ฏธ์…˜ ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ๊ฐ€ ํฌ์ง€ ์•Š์•„์„œ ์ด ์ด์ƒ์˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ํ•„์š”๊นŒ์ง€๋Š” ์—†์„ ๊ฑฐ ๊ฐ™๋‹ค.

 

์ด ์ •๋„๋งŒ ํ•ด๋„ ์ข‹์€ ๊ฒŒ ์†์„ฑ๊ฐ’์„ ์Šคํ† ๋ฆฌ๋ถ์—์„œ ์ด๊ฑฐ์ €๊ฑฐ ์ˆ˜์ •ํ•ด๊ฐ€๋ฉฐ ์‹ค์ œ๋กœ ์ž˜ ์ ์šฉ์ด ๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•˜๋Š”๊ฑด ๋ฌผ๋ก ์ด๊ณ 

์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งจ ์ฒ˜์Œ ๋งŒ๋“ค๊ณ  ๋‚˜์„œ ์Šคํƒ€์ผ์ด ์ œ๋Œ€๋กœ ๋๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด App ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋”ฐ๋กœ ํ…œํ”Œ๋ฆฟ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‹คํ–‰ํ•ด๋ณด๊ฑฐ๋‚˜ ํ•  ํ•„์š” ์—†์ด ๋ฐ”๋กœ๋ฐ”๋กœ ์ €์žฅํ•˜๊ณ  ํ™•์ธํ•˜๊ณ  ์ €์žฅํ•˜๊ณ  ํ™•์ธํ•˜๊ณ  ํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธํ•˜๋‹ค!

๊ทธ๋ฆฌ๊ณ  ๋งŒ์•ฝ ์†์„ฑ ๊ฐ’์ด ๋งŽ๊ณ , ๊ทธ ์†์„ฑ์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ์˜ ๋””์ž์ธ์ด ํœ™ํœ™ ๋ฐ”๋€Œ์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

ํ•ด๋‹น ๋””์ž์ธ์„ ๋งŒ๋“ค๋ผ๋ฉด ์ •ํ™•ํžˆ ์–ด๋–ค ์†์„ฑ์ด ์–ด๋–ค ๊ฐ’์ด์–ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์Šคํ† ๋ฆฌ๋ถ์„ ๋ณด๊ณ  ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋„ˆ๋ฌด ํŽธํ•˜๋‹ค!

 

๋ฌด์—‡๋ณด๋‹ค ์ œ์ผ ์ตœ๊ณ ๋ผ ์ƒ๊ฐํ•˜๋Š” ์žฅ์ ์€...!

 

โœง*.โ—Ÿ( หŠ แ—จ ห‹ )โ—ž.*โœง

๋‹ค ๋งŒ๋“ค๊ณ  ๋ณด๋ฉด ๋„˜ ๋ฟŒ๋“ฏํ•จ!!!

 

๊ฑฐ๊ธฐ๋‹ค ์ด๋ ‡๊ฒŒ ๋งŒ๋“  ์Šคํ† ๋ฆฌ๋ถ์„ ๋ฐฐํฌ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค!!!!!

๋ฐฐํฌ์— ๋Œ€ํ•œ ๋‚ด์šฉ์€...!

๋”ฐ๋กœ ๊ธ€์„ ํŒŒ๋Š” ๊ฒŒ ๋‚ซ๊ฒ ๋‹คใ…Žใ…Ž!  -> ๋”ฐ๋กœ ํŒ ๋‹ค! 

์•„๋ฌดํŠผ ์Šคํ† ๋ฆฌ๋ถ ๋„ˆ๋ฌด ์ข‹๋‹น!!!! ^0^)!ใ…Žใ…Žใ…Žใ…Žใ…Ž

๋‹ค๋“ค ์ธ„๋ผ์ด!!!

 


๐Ÿ’– ์ฐธ๊ณ 

 

Storybook Tutorials

Learn how to develop UIs with components and design systems. Our in-depth frontend guides are created by Storybook maintainers and peer-reviewed by the open source community.

storybook.js.org

 

React๋ฅผ ์œ„ํ•œ Storybook ํŠœํ† ๋ฆฌ์–ผ

Storybook์„ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— ์„ค์น˜ํ•ด๋ด…์‹œ๋‹ค

storybook.js.org