์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

์Šคํ† ๋ฆฌ๋ถ ํŠœํ† ๋ฆฌ์–ผ

codevil 2023. 6. 8. 12:32

๐Ÿงธ ์„œ๋ก 

์ด์ „์— ์ง„ํ–‰ํ–ˆ๋˜ ํ”„๋กœ์ ํŠธ๋Š”(ํ˜„์žฌ๋„ ์ง„ํ–‰์ค‘...) CBD(Component Based Development)๋ฅผ ๊ธฐ์ค€์œผ๋กœ  ์ง„ํ–‰ํ•˜์˜€๋‹ค. ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜ ํ•˜๋‚˜ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ, layout์ด๋ผ๋Š” ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ๊ณณ์— ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ํ•˜๋‚˜ ํ•˜๋‚˜ ๊ทธ๋ฆฌ๋‹ค๋ณด๋‹ˆ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ์ด๋‚˜ ๊ทธ๋ฆฌ๋Š”๋ฐ ์žˆ์–ด UI์™€ ์ปดํฌ๋„ŒํŠธ์˜ ๋ถ„๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. 

Storybook is an open source tool for building UI components and pages in isolation. It streamlines UI development, testing, and documentation.

์ด๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์Šคํ† ๋ฆฌ๋ถ์„ ๊ณต๋ถ€ํ•˜๊ณ  ์ ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

๐Ÿฅ„์„ค์น˜

์Šคํ† ๋ฆฌ๋ถ ์„ค์น˜๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ปค๋งจ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

npx sb init

์ด ์ปค๋งจ๋“œ๊ฐ€ ๋๋‚˜๊ณ  ๋‚˜๋ฉด, package.json scripts์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ script๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค.

  "scripts": {
	...
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
    ...
  },

์šฐ๋ฆฌ๋Š” ์Šคํ† ๋ฆฌ๋ถ์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ package manager ์‹คํ–‰ ๋ช…๋ น์–ด์™€ storybook์„ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค(์˜ˆ์‹œ yarn storybook). inquirer๋ฅผ ๋”ฐ๋ผ์„œ localhost:6006(storybook default ์ฃผ์†Œ)๋กœ ์ด๋™ํ•˜๋ฉด ์Šคํ† ๋ฆฌ๋ถ์˜ ์˜ˆ์ œ๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ๋“ค์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

โ›ณ๏ธ ์ถ”๊ฐ€ ์„ค์ •

์ง€๊ธˆ ์Šคํ† ๋ฆฌ๋ถ์„ ์ ์šฉํ•  ํ”„๋กœ์ ํŠธ๋Š” Styled-components์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์ธ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค.  Styled-components๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” preview.ts(ํ˜น์€ preview.js)ํŒŒ์ผ์„ preview.tsx(preview.js ๋ผ๋ฉด jsx)๋กœ ์ˆ˜์ •ํ•ด์•ผํ•œ๋‹ค.

npx sb init๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•˜๋ฉด preview.ts๋กœ ํŒŒ์ผ์ด ์„ธํŒ…๋˜์–ด์žˆ์—ˆ๋‹ค.

ํŒŒ์ผ ์ด๋ฆ„์„ ์ˆ˜์ •ํ•˜์˜€๋‹ค๋ฉด preview.tsx์— ThemeProvider๋ฅผ ์ ์šฉํ•œ๋‹ค. ์ƒ˜ํ”Œ ์ฝ”๋“œ๋Š” https://storybook.js.org/docs/react/writing-stories/decorators ์ด ์ฃผ์†Œ์—์„œ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

import React from 'react';

import { Preview } from '@storybook/react';

import { ThemeProvider } from 'styled-components';

const preview: Preview = {
  decorators: [
    (Story) => (
      <ThemeProvider theme="default">
        {/* ๐Ÿ‘‡ Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </ThemeProvider>
    ),
  ],
};

export default preview;

๋ณธ์ธ์ด ์ ์šฉํ•œ ํ…Œ๋งˆ๊ฐ€ ์žˆ๋‹ค๋ฉด ์œ„์œผ ์ฝ”๋“œ์—์„œ theme={theme} ๊ณผ ๊ฐ™์ด ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

import React from 'react';

import { Preview } from '@storybook/react';

import { ThemeProvider } from 'styled-components';

import { theme} from "../src/constants";

const preview: Preview = {
  decorators: [
    (Story) => (
      <ThemeProvider theme={theme}>
        <Story />
      </ThemeProvider>
    ),
  ],
};

export default preview;

(์ง€๊ธˆ์€ alias๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— theme์˜ ์ฃผ์†Œ๋Š” ..์œผ๋กœ ์ž‘์„ฑ๋˜์–ด์žˆ๋‹ค)

 

๐ŸฅŠ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœํ•˜๊ธฐ

๐Ÿคน‍โ™€๏ธ ํŒŒ์ผ ๊ตฌ์กฐ

์œ„์— ์žˆ๋Š” ์ธ์Šคํƒ€๊ทธ๋žจ์˜ "์ƒ๊ฐ์„ ๊ณต์œ ํ•ด๋ณด์„ธ์š”" ๋งํ’์„ ์„ ์Šคํ† ๋ฆฌ๋ถ์„ ์ด์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด ๋ณด์ž. ์ด ์ปดํฌ๋„ŒํŠธ์˜ ์ด๋ฆ„์ด MyMemo๋ผ๊ณ  ํ•˜๋ฉด, ์ปดํฌ๋„ŒํŠธ์™€ ๊ด€๋ จ๋œ ํŒŒ์ผ์€

  • MyMemo.tsx
  • MyMemo.stories.tsx

๋‘ ๊ฐ€์ง€ ํŒŒ์ผ์ด ๊ธฐ๋ณธ์œผ๋กœ ํ•„์š”ํ•˜๋‹ค. ํ•˜์ง€๋งŒ, styled-components ๊ฐ™์€ ๊ฒฝ์šฐ ํ•จ์ˆ˜์ ์ธ ๊ธฐ๋Šฅ์˜ semantic์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ MyMemo.styles.tsxํŒŒ์ผ๊ณผ ํƒ€์ž…์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋ฅผ ๊ณ ๋ คํ•˜์—ฌ MyMemo.d.ts ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

  • MyMemo.tsx
  • MyMemo.stories.tsx
  • MyMemo.styles.tsx
  • MyMemo.d.ts

๐ŸŽฎ ์Šคํ† ๋ฆฌ๋ถ controls

์šฐ์„  ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ์—์„œ๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ด๋–ค ์‹์œผ๋กœ ๊ทธ๋ ค์ง€๋Š”๊ฐ€๋ฅผ ๋ณด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ

  • ๋กœ๊ทธ์ธ์ด ๋œ ์ผ€์ด์Šค
  • ๋กœ๊ทธ์ธ์ด ์•ˆ๋œ ์ผ€์ด์Šค

๋‘ ๊ฐ€์ง€ ์ผ€์ด์Šค๋กœ ๋ถ„๋ฆฌ๋ฅผ ํ•˜์—ฌ ์ง„ํ–‰์„ ํ•  ์˜ˆ์ •์ด๋‹ค.

๋ฆฌ์•กํŠธ์—์„œ๋Š”

  • state๋งˆ๋‹ค ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ปดํฌ๋„ŒํŠธ ๊ทธ๋ฆฌ๊ธฐ
  • ๋‹ค๋ฅธ props๋ฅผ ๋‚ด๋ ค์„œ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๊ทธ๋ฆฌ๊ธฐ

๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์กด์žฌํ•œ๋‹ค. state ๋งˆ๋‹ค ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ทธ๋ ค์ง€๋Š” ๋ฐฉ์‹์€ ๋‹ค๋ฅธ props๋กœ ๋‹ค๋ฅด๊ฒŒ ๊ทธ๋ ค์ง€๋Š” ๋ฐฉ์‹์˜ ์ผ€์ด์Šค๋ฅผ ํ•œ ๊ฐœ๋กœ ๊ทธ๋ฆฌ๋ฉด ๋˜‘๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— "๋‹ค๋ฅธ props๋ฅผ ๋‚ด๋ ค์„œ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๊ทธ๋ฆฌ๋Š” ๋ฐฉ์‹"์œผ๋กœ ์ง„ํ–‰ํ•˜๊ฒ ๋‹ค.

 

Step1. ์ปดํฌ๋„ŒํŠธ Props ์ •ํ•˜๊ธฐ

์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์ปดํฌ๋„ŒํŠธ์— ๋“ค์–ด์˜ค๋Š” ํƒ€์ž…์„ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค. ์ง€๊ธˆ ๋งŒ๋“ค ์ปดํฌ๋„ŒํŠธ๋Š” user๊ฐ€ undefined์ด๊ฑฐ๋‚˜ object๋ฅผ ๋งŒ๋“ค๋„๋ก ๊ตฌํ˜„์„ ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

//MyMemo.d.ts
interface MyMemoProps {
  user: { name: string } | undefined;
}

 

Step2. ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ๋“ค์–ด๊ฐˆ ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ์ „์— ์ปดํฌ๋„ŒํŠธ์˜ child์•ˆ์— ๋“ค์–ด๊ฐˆ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค. ์ง€๊ธˆ์€ ์šฐ์„  ๊ฐ„๋‹จํ•˜๊ฒŒ ๋กœ๊ทธ์ธ์˜ ์œ ๋ฌด์— ๋”ฐ๋ผ ์‰ฝ๊ฒŒ ๊ตฌ๋ถ„์„ ํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋นจ๊ฐ•๊ณผ ํŒŒ๋ž‘์œผ๋กœ ๊ฐ€์ง€๋Š” span์„ ๋งŒ๋“ค์—ˆ๋‹ค.

//MyMemo.styles.tsx
const ProfileIcon = styled.span`
  background-color: blue;
`;
const DefaultIcon = styled.span`
  background-color: red;
`;

 

Step3. props์— ๋”ฐ๋ฅธ ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

์Šคํ† ๋ฆฌ๋ถ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ธฐ ์ „์— user์˜ ๊ฐ’์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๊ทธ๋ ค์ง€๋„๋ก ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค.(์ผ€์ด์Šค๋งˆ๋‹ค ๋‚˜๋‰˜์–ด์ง€๊ฒŒ ๋จผ์ € ๋งŒ๋“ค๊ณ  ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด์„œ ๋”ฅํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๋„๋กํ•œ๋‹ค)

export function MyMemo(props: MyMemoProps) {
  const { user } = props;
  return <>{user ? <ProfileIcon>์•„์ด์ฝ˜</ProfileIcon> : <DefaultIcon >์•„์ด์ฝ˜</DefaultIcon >}</>;
}

 

Step4. Storybook ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

4.1  ๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธ ์„ค์ •ํ•˜๊ธฐ

์Šคํ† ๋ฆฌ๋ถ์—์„œ ๋งŒ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด๊ธฐ ์œ„ํ•˜์—ฌ [์ปดํฌ๋„ŒํŠธ์ด๋ฆ„].stories.tsx์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ปดํฌ๋„ŒํŠธ์™€, ์Šคํ† ๋ฆฌ๋ถ์—์„œ ์‚ฌ์šฉํ•  ์˜ต์…˜๋“ค์„ ๋„ฃ๋Š”๋‹ค.

์ถ”๊ฐ€์„ค๋ช… :

  • title: ์Šคํ† ๋ฆฌ๋ถ์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ์ด๋ฆ„
  • component: title๋กœ ๋ถ„๋ฅ˜ํ•œ ์Šคํ† ๋ฆฌ๋ถ ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉํ•  ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„
  • tags: ["autodocs"] ์Šคํ† ๋ฆฌ๋ถ์—์„œ ์—ฌ๋Ÿฌ ์ผ€์ด์Šค์™€ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด์ฃผ๋Š” ๋ฌธ์„œ๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ์ค„์ง€์— ๊ด€ํ•œ ์˜ต์…˜
  • parameters: { layout: "fullscreen" | "centered"}: ์Šคํ† ๋ฆฌ๋ถ ํŽ˜์ด์ง€์˜ ํ™”๋ฉด์— ๊ด€๋ จ๋œ ์„ค์ • 
const meta: Meta<typeof MyMemo> = {
  title: "MyMemo component",
  component: MyMemo,
  // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
  tags: ["autodocs"],
  parameters: {
    // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
    layout: "fullscreen",
  },
};
export default meta;

 

4.2 ์Šคํ† ๋ฆฌ๋ถ ์ผ€์ด์Šค ๋งŒ๋“ค๊ธฐ

export const LoggedIn: UserData = {
  args: {
    user: {
      name: "Mesher",
    },
  },
};

export const LoggedOut: UserData = { args: { user: undefined } };

 

์ „์ฒด ์ฝ”๋“œ

import type { Meta, StoryObj } from "@storybook/react";
import { MyMemo } from "./MyMemo";

type UserData = StoryObj<typeof MyMemo>;

const meta: Meta<typeof MyMemo> = {
  title: "MyMemo component",
  component: MyMemo,
  // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
  tags: ["autodocs"],
  parameters: {
    // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
    layout: "fullscreen",
  },
};
export default meta;
export const LoggedIn: UserData = {
  args: {
    user: {
      name: "Mesher",
    },
  },
};

export const LoggedOut: UserData = { args: { user: undefined } };

 

๐Ÿคพ‍โ™‚๏ธ ์Šคํ† ๋ฆฌ๋ถ Play Function

Step1. ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜

์Šคํ† ๋ฆฌ๋ถ Play Function์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์Šคํ† ๋ฆฌ๋ถ์—์„œ ์ œ๊ณตํ•˜๋Š” interaction ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•ด์•ผํ•œ๋‹ค.

//yarn
yarn add --dev @storybook/testing-library @storybook/jest @storybook/addon-interactions

//npm
npm install @storybook/testing-library @storybook/jest @storybook/addon-interactions --save-dev

//pnpm
pnpm add --save-dev @storybook/testing-library @storybook/jest @storybook/addon-interactions

 

Step2. addon ์ถ”๊ฐ€

.storybook/main.ts ํŒŒ์ผ์˜ addon props์— @storybook/addon-interactions๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผํ•œ๋‹ค.

// .storybook/main.ts

// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
import type { StorybookConfig } from '@storybook/your-framework';

const config: StorybookConfig = {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    // Other Storybook addons
    '@storybook/addon-interactions', // ์ด ๋ถ€๋ถ„์ด ํ•„์ˆ˜๋กœ ํ•„์š”ํ•˜๋‹ค.
  ],
};

export default config;

 

Step3. ์ปดํฌ๋„ŒํŠธ ์ž‘์„ฑ

Play Function์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜์ž. ์šฐ์„  ์ด ๊ธ€์€ ํŠœํ† ๋ฆฌ์–ผ์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋ ค์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ Interaction์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” count button์„ ์ด์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ Interaction์„ ์•Œ์•„๋ณด์ž.

//CountButton.tsx
import { useState } from "react";

export function CountButton() {
  const [count, setCount] = useState(0);
  const onClickHandler = () => setCount((count) => count + 1);
  return (
    <>
      <button onClick={onClickHandler}>{count}</button>
    </>
  );
}

 

Step4. ์Šคํ† ๋ฆฌ๋ถ ์ผ€์ด์Šค ์ž‘์„ฑ

//CountButton.stories.tsx

import type { Meta, StoryObj } from "@storybook/react";
import { CountButton } from "./CountButton";
import { fireEvent, userEvent, within } from "@storybook/testing-library";
type Story = StoryObj<typeof CountButton>;

const meta: Meta<typeof CountButton> = {
  title: "CountButton component",
  component: CountButton,
  tags: ["autodocs"],
  parameters: {
    layout: "fullscreen",
  },
};
export default meta;

export const CountButtonExample = { args: {} };
export const ClickOnceExample: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    await userEvent.click(canvas.getByRole("button"));
  },
};

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ˆ์ œ์—์„œ๋Š” CountButtonExample์„ ์ด์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฒดํ—˜ํ•ด ๋ณผ ์ˆ˜ ์žˆ๊ณ , ClickOnceExample์„ ์ด์šฉํ•˜์—ฌ ํ•œ๋ฒˆ ํด๋ฆญ์ด ๋˜์—ˆ์„ ๋•Œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์œ„์˜ ์˜ˆ์ œ๋ฅผ ์ด์šฉํ•˜๋ฉด

export const ClickExample: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    function sleep(ms: number) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }
    async function clicked() {
      await fireEvent.click(canvas.getByRole("button"));
      await sleep(50);
    }
    await Array(1000).fill(0).reduce(async (prev, current) => {
      const previousPromise = await prev.then();
      await func(current);
      return Promise.resolve(previousPromise)
      await clicked();
    }, Promise.resolve());   
  },
};

ํด๋ฆญ์„ 100๋ฒˆ์„ ํ•˜๊ธฐ ํž˜๋“  ๊ฒฝ์šฐ ์ด๋ฅผ ๋Œ€์‹ ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” UIํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜์žˆ๋Š” ์Šคํ† ๋ฆฌ๋ถํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํ•œ ๊ฐ€์ง€ ์ฃผ์˜ ํ•  ๊ฒƒ์ด ์žˆ๋‹ค๋ฉด, async์™€ await๋Š” forEach, map์™€ ์‚ฌ์šฉํ•ด๋„ ์•„๋ฌด๋Ÿฐ ํšจ๊ณผ๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์ž. ๋งŒ์ผ ๊ฐ™์€ ์ด๋ฒคํŠธ๋ฅผ ํ˜น์€ ์ˆœํ™˜ํ•˜๋ฉด์„œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด reduce์™€ reduce ๋‚ด๋ถ€์— ์žˆ๋Š” ๋ˆ„์ ๋œ ๊ฐ’์„ await์‹œ์ผœ์„œ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜๊ฐ€ ๋๋‚  ๋•Œ๋งˆ๋‹ค ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

๐Ÿ”š ๋งˆ๋ฌด๋ฆฌ

์ด ๊ธ€์—์„œ๋Š” ์Šคํ† ๋ฆฌ๋ถ์„ ์ด์šฉํ•˜์—ฌ ๋‘๊ฐ€์ง€๋ฅผ ์•Œ์•„๋ณด์•˜๋‹ค.

1. ์Šคํ† ๋ฆฌ๋ถ์„ ์ด์šฉํ•˜์—ฌ Props์— ๋”ฐ๋ผ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๊ทธ๋ ค์ง€๋Š”์ง€

2. ์Šคํ† ๋ฆฌ๋ถ์„ ์ด์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ์˜ UIํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ธฐ

ํ•œ ๋ฒˆ ์ •ํ•ด์ง„ ์ด๋ฒคํŠธ์™€ ์—๋Ÿฌ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ๋ณด๊ฑฐ๋‚˜ ์–ด๋–ค ์‹์œผ๋กœ ๊ทธ๋ ค์ง€๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํ† ๋ฆฌ๋ถ์„ ์ด์šฉํ•˜๋ฉด QA์‹œ๊ฐ„์„ ๋‹จ์ถ•์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ์˜ค๋Š˜์€ ํด๋ฆญ ์ด๋ฒคํŠธ๋งŒ ์˜ฌ๋ ค๋‘์—ˆ์ง€๋งŒ ์‹œ๊ฐ„์ด ๋˜๋Š”๋Œ€๋กœ ๋‹ค๋ฅธ ์ด๋ฒคํŠธ์— ๊ด€๋ จ๋œ ๊ธ€์„ ์ถ”๊ฐ€๋กœ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ด ๊ธ€์— ์ˆ˜์ •ํ•ด์„œ ๋„ฃ์–ด์•ผ๊ฒ ๋‹ค.

 

๋‚˜์ค‘์—๋Š” ์Šคํ† ๋ฆฌ๋ถ์˜ ์ด๋ฒคํŠธ๋ฅผ ์ž˜๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ...?

์ถœ์ฒ˜(https://storybook.js.org/docs/react/writing-tests/interaction-testing)