Jieunny์˜ ๋ธ”๋กœ๊ทธ

S3) Unit 3. [React] Custom Component ๋ณธ๋ฌธ

CodeStates/learning contents

S3) Unit 3. [React] Custom Component

Jieunny 2023. 2. 20. 16:47

๐Ÿ“ฃ CDD

โœ”๏ธ Component-Driven Development(CDD)
โžฐ ๋ถ€ํ’ˆ ๋‹จ์œ„๋กœ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋‚˜๊ฐ€๋Š” ๊ฐœ๋ฐœ
โžฐ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“ฃ CSS in JS

โžฐ ๋‹ค์–‘ํ•œ ๋””๋ฐ”์ด์Šค๋“ค์˜ ๋“ฑ์žฅ์œผ๋กœ ์›น์‚ฌ์ดํŠธ๋“ค์ด ๋‹ค์–‘ํ•œ ๋””์Šคํ”Œ๋ ˆ์ด๋ฅผ ์ปค๋ฒ„ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— CSS๋Š” ๋” ๋ณต์žกํ•ด์ง€๊ฒŒ ๋˜์—ˆ๋‹ค.
โžฐ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CSS ์ „์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ๋“ฑ์žฅํ—€๋‹ค.

โœ”๏ธ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ : CSS๊ฐ€ ๊ตฌ์กฐ์ ์œผ๋กœ ์ž‘์„ฑ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋„์›€์„ ์ฃผ๋Š” ๋„๊ตฌ
โžฐ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ์ž์ฒด๋งŒ์œผ๋กœ๋Š” ์›น ์„œ๋ฒ„๊ฐ€ ์ธ์ง€ํ•˜๊ธฐ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ์ „์ฒ˜๋ฆฌ๊ธฐ์— ๋งž๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ณ , ์ปดํŒŒ์ผ์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์‹ค์ œ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” CSS ๋ฌธ์„œ๋กœ ๋ณ€ํ™˜์ด ๋œ๋‹ค.

โœ”๏ธ SASS(Syntactically Awesome Style Sheets)
โžฐ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ์ค‘ ๊ฐ€์žฅ ์œ ๋ช…ํ•˜๋ฉฐ, CSS๋ฅผ ํ™•์žฅํ•ด ์ฃผ๋Š” ์Šคํฌ๋ฆฝํŒ… ์–ธ์–ด์ด๋‹ค.
โžฐ CSS๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์–ธ์–ด๋กœ์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์ฒ˜๋Ÿผ ํŠน์ • ์†์„ฑ์˜ ๊ฐ’์„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•˜์—ฌ ํ•„์š”ํ•œ ๊ณณ์— ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ์˜ ์„ ์–ธ์œผ๋กœ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.
โžฐ SASS๋Š” SCSS ์ฝ”๋“œ๋ฅผ ์ฝ์–ด์„œ ์ „์ฒ˜๋ฆฌํ•œ ๋‹ค์Œ ์ปดํŒŒ์ผํ•ด์„œ ์ „์—ญ CSS ๋ฒˆ๋“ค ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ฃผ๋Š” '์ „์ฒ˜๋ฆฌ๊ธฐ' ์—ญํ• ์„ ํ•œ๋‹ค.
โžฐ ํ•˜์ง€๋งŒ ์žฅ์ ๋ณด๋‹ค ๋‹จ์ ์„ ๋” ๋งŽ์ด ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋œ๋‹ค.

โœ”๏ธ BEM, OOCSS, SMACSS
โžฐ SASS์˜ ๋ฌธ์ œ๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•œ CSS ๋ฐฉ๋ฒ•๋ก 
โžฐ '์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ', '์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐํ™”', '์ฝ”๋“œ์˜ ํ™•์žฅ์„ฑ', '์ฝ”๋“œ์˜ ์˜ˆ์ธก์„ฑ' ์„ ๊ณตํ†ต์œผ๋กœ ์ง€ํ–ฅํ•œ๋‹ค.

โœ”๏ธ BEM
โžฐ Block, Element, Modifier๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ํด๋ž˜์Šค๋ช…์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•
โžฐ Block, Element, Modifier์€ ๊ฐ๊ฐ -์™€ _๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค.
โžฐ ํด๋ž˜์Šค๋ช…์€ BEM ๋ฐฉ์‹์˜ ์ด๋ฆ„์„ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ฐ˜๋ณตํ•˜์—ฌ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ฉฐ, ๋” ์ผ๊ด€๋œ ์ฝ”๋”ฉ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.
โžฐ ํ•˜์ง€๋งŒ ํด๋ž˜์Šค๋ช… ์„ ํƒ์ž๊ฐ€ ์žฅํ™ฉํ•ด์ง€๊ณ , ๊ธด ํด๋ž˜์Šค๋ช… ๋•Œ๋ฌธ์— ๋งˆํฌ์—…์ด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ปค์ง€๋Š” ๋“ฑ์˜ ๋‹จ์ ์ด ์žˆ๋‹ค.

๐Ÿšจ SASS์™€ BEM๋„ ๊ณ ์น˜์ง€ ๋ชปํ–ˆ๋˜ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ
1. ์–ธ์–ด ๋กœ์ง ์ƒ์— ์ง„์ •ํ•œ ์บก์Šํ™”์˜ ๊ฐœ๋…์ด ์—†๋‹ค.
2. ์ด๋กœ ์ธํ•ด ๊ฐœ๋ฐœ์ž๋“ค์ด ์œ ์ผํ•œ ํด๋ž˜์Šค๋ช…์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ์— ์˜์กดํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

โœ”๏ธ CSS-in-JS
โžฐ CSS๋„ ์ปดํฌ๋„ŒํŠธ ์˜์—ญ์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•

โœ”๏ธ Styled-Component
โžฐ CSS-in-JS์˜ ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•
โžฐ ๊ธฐ๋Šฅ์  ํ˜น์€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋“ค๋กœ๋ถ€ํ„ฐ UI๋ฅผ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ฃผ ๋‹จ์ˆœํ•œ ํŒจํ„ด์„ ์ œ๊ณตํ•œ๋‹ค.

๐Ÿ“ฃ CSS ๋ฐฉ๋ฒ•๋ก ๋“ค์˜ ํŠน์ง•๊ณผ ์žฅ๋‹จ์ 


๐Ÿ“ฃ Styled Components

โœ”๏ธ React ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ CSS๋ฅผ ์ปดํฌ๋„ŒํŠธํ™”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
โžฐ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด CSS๋„ ์‰ฝ๊ฒŒ JS์•ˆ์— ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค.
โžฐ HTML + JS + CSS ๊นŒ์ง€ ๋ฌถ์–ด์„œ ํ•˜๋‚˜์˜ JS ํŒŒ์ผ ์•ˆ์—์„œ ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๐Ÿ“ฃ Styled Components ์„ค์น˜ํ•˜๊ธฐ

โœ”๏ธ ์„ค์น˜

npm install --save styled-components


โœ”๏ธ package.json์— ์ฝ”๋“œ ์ถ”๊ฐ€
โžฐ ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ Styled Components๊ฐ€ ์„ค์น˜๋˜์–ด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ค„์—ฌ์ค€๋‹ค.

{
  "resolutions": {
    "styled-components": "^5"
  }
}


โœ”๏ธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

import styled from "styled-components"

๐Ÿ“ฃ Styled Components ๋ฌธ๋ฒ•

1๏ธโƒฃ ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

โžฐ ๋”ฐ์˜ดํ‘œ๊ฐ€ ์•„๋‹Œ ๋ฐฑํ‹ฑ์„ ์‚ฌ์šฉํ•˜๋Š” Templete Literals ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

const BlueButton = styled.button`
    background-color: blue;
    color: white;
`;

โžฐ ๋ฐฐ๊ฒฝ์ƒ‰์ด ํŒŒ๋ž€์ƒ‰, ๊ธ€์”จ ์ƒ‰์ƒ์ด ํ•˜์–€์ƒ‰์ธ ๋ฒ„ํŠผ์ด ์ƒ์„ฑ๋œ๋‹ค.

2๏ธโƒฃ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ™œ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

const BigBlueButton = styled(BlueButton)`
    padding: 10px;
    margin-top: 10px;
`;

โžฐ ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ™œ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
โžฐ 2๋ฒˆ์˜ ํŒŒ๋ž€ ๋ฒ„ํŠผ์— padding, margin ์†์„ฑ์ด ์ถ”๊ฐ€ ๋œ ๋ฒ„ํŠผ์ด ์ƒ์„ฑ๋œ๋‹ค.

3๏ธโƒฃ Props ํ™œ์šฉํ•˜๊ธฐ

โžฐ Styled Component๋กœ ๋งŒ๋“  ์ปดํฌ๋„ŒํŠธ๋„ React ์ปดํฌ๋„ŒํŠธ์ฒ˜๋Ÿผ props๋กœ ๋‚ด๋ ค์ค„ ์ˆ˜ ์žˆ๋‹ค.
โžฐ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ๋ฒ• '${}' ์„ ์‚ฌ์šฉํ•ด์„œ JS ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

1) Props๋กœ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋งํ•˜๊ธฐ

const Button = styled.button`
    background: ${(props) => props.skyblue? "skyblue" : "white"}
`;

โžฐ <Button> ์ปดํฌ๋„ŒํŠธ์— skyblue๋ผ๋Š” props๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์žˆ์œผ๋ฉด ๋ฐฐ๊ฒฝ์ƒ‰์„ skyblue๋กœ, ์—†์œผ๋ฉด white๋กœ ์ง€์ •ํ•ด์ฃผ๋Š” ์ฝ”๋“œ

<Button skyblue>Button1</Button>
// ๋ฐฐ๊ฒฝ์ƒ‰์ด ์Šค์นด์ด๋ธ”๋ฃจ์ธ ๋ฒ„ํŠผ

<Button>Button2</Button>
// ๋ฐฐ๊ฒฝ์ƒ‰์ด ํ™”์ดํŠธ์ธ ๋ฒ„ํŠผ


2) Props ๊ฐ’์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ

const Button = styled.button`
    background: ${(props) => props.color? props.color: "white"}
`;

<Button>Button1</Button>
<Button color="orange">Button2</Button>
<Button color="tomato">Button3</Button>

โžฐ props.color ๊ฐ€ ์—†๋‹ค๋ฉด white๋ฅผ, ์žˆ๋‹ค๋ฉด ๊ทธ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ์Šคํƒ€์ผ ์†์„ฑ ๊ฐ’์„ ๋ฆฌํ„ดํ•ด์ฃผ๊ณ  ์žˆ๋‹ค.
โžฐ ๊ผญ ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋งŒ ์‚ฌ์šฉํ•  ํ•„์š”๋Š” ์—†๋‹ค -> JS ์ฝ”๋“œ๋ผ๋ฉด ๋ฌด์—‡์ด๋“  ์‚ฌ์šฉ ๊ฐ€๋Šฅ

4๏ธโƒฃ ์ „์—ญ ์Šคํƒ€์ผ ์„ค์ •ํ•˜๊ธฐ

import { createGlobalStyle } from 'styled-components";

const GlobalStyle = createGlobalStyle`
    button {
        padding: 5px;
        margin: 2px;
        border-radius: 5px;
    }
`
function App() {
    return (
        <>
            <GlobalStyle />
            <Button>์ „์—ญ ์Šคํƒ€์ผ ์ ์šฉํ•˜๊ธฐ</Button>
        </>
    );
}

โžฐ <GlobalStyle> ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์— ์‚ฌ์šฉํ•ด์ฃผ๋ฉด ์ „์—ญ์— <GlobalStyle> ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์ด ์ ์šฉ๋œ๋‹ค.

๐Ÿ“ฃ ์‹ค์Šต

import styled from "styled-components";

      const Button = styled.button`
      padding: 1rem;
      font-size: 2rem;
      background: powderblue;
      border-radius: 1rem;
      transition: 0.5s;
      &:hover{
      // hover ํšจ๊ณผ
        background: cornflowerblue;
        color: white;
        transition: 0.5s;
      }
      `;


export default function App() {
  return (
    <Button>Practice!</Button>
  );
}

๐Ÿ“ฃ ์ปดํฌ๋„ŒํŠธ UI ๊ฐœ๋ฐœ์„ ์œ„ํ•œ Storybook

โœ”๏ธ CDD๊ฐ€ ํŠธ๋ Œ๋“œ๋กœ ์ž๋ฆฌ ์žก๊ฒŒ ๋˜๋ฉด์„œ ์ด๋ฅผ ์ง€์›ํ•˜๋Š” ๋„๊ตฌ์ธ Component Explorer๊ฐ€ ๋“ฑ์žฅํ–ˆ๋‹ค.
โœ”๏ธ Storybook : Component Explorer ์˜ UI ๊ฐœ๋ฐœ ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜

๐Ÿ“ฃ Storybook์ด ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ”๏ธ UI ๊ฐœ๋ฐœ, ์ฆ‰ CDD๋ฅผ ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ
โžฐ ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋”ฐ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌ์„ฑํ•ด ์ฃผ์–ด ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋‹ค.
โžฐ ์žฌ์‚ฌ์šฉ์„ฑ์„ ํ™•๋Œ€ํ•˜๊ธฐ ์œ„ํ•ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฌธ์„œํ™”ํ•˜๊ณ , ์ž๋™์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‹œ๊ฐํ™”ํ•˜์—ฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ์ƒํƒœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
โžฐ ๋ฒ„๊ทธ ์‚ฌ์ „ ๋ฐฉ์ง€, ํ…Œ์ŠคํŠธ ๋ฐ ๊ฐœ๋ฐœ ์†๋„ ํ–ฅ์ƒ์˜ ์žฅ์ ์ด ์žˆ๋‹ค.

๐Ÿ“ฃ Storybook๊ณผ ๊ฐ™์€ UI ๊ฐœ๋ฐœ ๋„๊ตฌ๋ฅผ ์™œ ์‚ฌ์šฉํ• ๊นŒ์š”?

โœ”๏ธ Storybook์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋…๋ฆฝ์ ์ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜๋ฉฐ, ๊ฐœ๋ฐœ์ž๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๊ตฌ์• ๋ฐ›์ง€ ์•Š๊ณ  UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง‘์ค‘์ ์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.
โœ”๏ธ Storybook ์˜ ์ฃผ์š” ๊ธฐ๋Šฅ
โžฐ UI ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์นดํƒˆ๋กœ๊ทธํ™” ํ•˜๊ธฐ
โžฐ ์ปดํฌ๋„ŒํŠธ ๋ณ€ํ™”๋ฅผ Stories๋กœ ์ €์žฅํ•˜๊ธฐ
โžฐ ํ•ซ ๋ชจ๋“ˆ ์žฌ ๋กœ๋”ฉ๊ณผ ๊ฐ™์€ ๊ฐœ๋ฐœ ํˆด ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๊ธฐ
โžฐ ๋ฆฌ์•กํŠธ๋ฅผ ํฌํ•จํ•œ ๋‹ค์–‘ํ•œ ๋ทฐ ๋ ˆ์ด์–ด ์ง€์›ํ•˜๊ธฐ

๐Ÿ“ฃ Storybook ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋ฌธ์„œํ™”ํ•ด์„œ ๊ด€๋ฆฌํ•˜๊ธฐ

โžฐ ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ์•ˆ์— Storybook ์„ค์น˜ํ•˜๊ธฐ

npx storybook init
// package.json์„ ๋ณด๊ณ  ์‚ฌ์šฉ ์ค‘์ธ ํ”„๋ก ํŠธ์—”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋งž๋Š” Storybook ์‚ฌ์šฉํ™˜๊ฒฝ์„ ์•Œ์•„์„œ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ช…๋ น์–ด

โžฐ /.storybook ํด๋”์™€ /src/stories ํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  /.storybook ํด๋”์—๋Š” Storybook ๊ด€๋ จ ์„ค์ • ํŒŒ์ผ์ด, /src/stories ํด๋”์—๋Š” Storybook ์˜ˆ์‹œ ํŒŒ์ผ๋“ค์ด ๋“ค์–ด์žˆ๋‹ค.

โžฐ ํ„ฐ๋ฏธ๋„์—์„œ Storybook ์‹คํ–‰ํ•˜๊ธฐ

npm run storybook

โžฐ localhost:6006 ์œผ๋กœ ์ ‘๊ทผํ•˜์—ฌ Storybook์„ ์‹คํ–‰์‹œํ‚จ๋‹ค.
โžฐ /src/stories ํด๋” ์•ˆ์— ์žˆ๋˜, Storybook์—์„œ ๋งŒ๋“ค์–ด๋†“์€ ์˜ˆ์‹œ ์Šคํ† ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

โžฐ ์Šคํ† ๋ฆฌ ์ž‘์„ฑ -> src ํด๋” ์•ˆ์— Title.js ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ์ปดํฌ๋„ŒํŠธ export ํ•˜๊ธฐ

import React from "react";

// title์€ h1 ์š”์†Œ์˜ textContent, textColor์€ ๊ธ€์ž์ƒ‰์ด ๋˜๋Š” props์ž…๋‹ˆ๋‹ค.
const Title = ({title, textColor}) => (
<h1 style={{color: textColor}}>{title}</h1>
);

export default Title;


โžฐ ๊ฐ™์€ ์œ„์น˜์ธ src ํด๋” ์•ˆ์— Title.stories.js ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ ๋‹ค.

// ์•ž์—์„œ ์ž‘์„ฑํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
import Title from "./Title";

// title : ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์œผ๋กœ, '/'๋ฅผ ๋„ฃ์–ด ์นดํ…Œ๊ณ ๋ฆฌํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
//         ์ดํ›„ ์˜ˆ์‹œ์—์„œ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
// component : ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์Šคํ† ๋ฆฌ๋กœ ๋งŒ๋“ค ๊ฒƒ์ธ์ง€ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
// argTypes : ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ์ „๋‹ฌ์ธ์ž์˜ ์ข…๋ฅ˜์™€ ํƒ€์ž…์„ ์ •ํ•ด์ค๋‹ˆ๋‹ค.
//            ์ง€๊ธˆ์€ title, textColor์ด๋ผ๋Š” ์ „๋‹ฌ์ธ์ž์— text ํƒ€์ž…์ด ํ•„์š”ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
export default {
    title: "Practice/Title", 
    component: Title,
    argTypes: {
        title: { control: "text" },
        textColor: {control: "text"}
    }
}

// ํ…œํ”Œ๋ฆฟ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. ์ด ํ…œํ”Œ๋ฆฟ์—์„œ๋Š”
// Title ์ปดํฌ๋„ŒํŠธ๊ฐ€ args๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ props๋กœ ๋‚ด๋ ค์ค๋‹ˆ๋‹ค.
const Template = (args) => <Title {...args} />

// Storybook์—์„œ ํ™•์ธํ•˜๊ณ  ์‹ถ์€ ์ปดํฌ๋„ŒํŠธ๋Š” export const๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
// ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์—ฌ Storybook์— ๋„ฃ์–ด์ค„ ์Šคํ† ๋ฆฌ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
// Template.bins({}); ๋Š” ์ •ํ•ด์ง„ ๋ฌธ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
export const RedTitle = Template.bind({});

// ๋งŒ๋“ค์–ด์ค€ ์Šคํ† ๋ฆฌ์˜ ์ „๋‹ฌ์ธ์ž๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.
RedTitle.args= {
    title: "Red Title",
    textColor: "red"
}

// ์Šคํ† ๋ฆฌ๋ฅผ ํ•˜๋‚˜ ๋” ๋งŒ๋“ญ๋‹ˆ๋‹ค.
export const BlueTitle = Template.bind({});

// ์Šคํ† ๋ฆฌ์˜ ์ „๋‹ฌ์ธ์ž๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.
BlueTitle.args= {
    title: "Blue Title",
    textColor: "blue"
}

โžฐ Storybook ์„ค์ • ํŒŒ์ผ์— ์˜ํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ๊ณผ ๋˜‘๊ฐ™์€ ํŒŒ์ผ ์ด๋ฆ„์— .stories๋ฅผ ๋ถ™์—ฌ ํŒŒ์ผ์„ ๋งŒ๋“ค๋ฉด ์•Œ์•„์„œ ์Šคํ† ๋ฆฌ๋กœ ์ธ์‹ํ•œ๋‹ค.


๐Ÿ“ฃ useRef

โœ”๏ธ DOM ์—˜๋ฆฌ๋จผํŠธ์˜ ์ฃผ์†Œ๊ฐ’์„ ํ™œ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ React๋กœ ๋ชจ๋“  ๊ฐœ๋ฐœ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•  ์ˆ˜ ์—†๋‹ค.
โžฐ focus
โžฐ text selection
โžฐ media playback
โžฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
โžฐ DOM ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ

โžฐ ์œ„์™€ ๊ฐ™์€ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์—์„œ useRef๋กœ DOM ๋…ธ๋“œ, ์—˜๋ฆฌ๋จผํŠธ, ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ์ฃผ์†Œ๊ฐ’์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>);
}

โžฐ ์ด ์ฃผ์†Œ๊ฐ’์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋˜๋”๋ผ๋„ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.

๐Ÿšจ useRef๋Š” ๋ฆฌ์•กํŠธ์˜ ํŠน์ง•์ธ ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™๊ณผ ๋ฐฐ์น˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚จ์šฉํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค!