[S3U3] React Custom Component

๐Ÿ‘ฝยท2024๋…„ 4์›” 29์ผ
0
post-thumbnail

CH1. Component Driven Development

๐Ÿ“Œ CDD

๐Ÿ”ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ชจ๋“ˆ ๋‹จ์œ„๋กœ ๊ฐœ๋ฐœํ•˜์—ฌ UI ๊ตฌ์ถ•์— ๋„๋‹ฌํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฐ ์„ค๊ณ„ ๋ฐฉ๋ฒ•๋ก .
๐Ÿ”ธ ๊ธฐ๋ณธ์ ์ธ ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ UI ๋ทฐ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ ์ง„์ ์œผ๋กœ ์กฐ๋ฆฝ(๊ฒฐํ•ฉ)ํ•ด๊ฐ€๋Š” ์ƒํ–ฅ์  ์„ฑํ–ฅ์„ ๋”.

  • ๊ธฐ์กด CSS๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” Color๊ฐ’ ์ฐพ๊ธฐ, tag ๋‹ซ๊ธฐ ๋“ฑ ๋ฒˆ๊ฑฐ๋กœ์šด ์ž‘์—…์ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์š”๊ตฌ๋˜๊ณ  ์žˆ์Œ.
  • ๋ฟ๋งŒ์•„๋‹ˆ๋ผ ํด๋ž˜์Šค ์ƒ์†๊ณผ ๊ฐ™์€ ์ผ๋กœ ์ ์  CSS ๋ฌธ์„œ๋Š” ์–‘์ด ๋งŽ์•„์ง€๊ณ  ์ด๋กœ ์ธํ•ด ์œ ์ง€๊ด€๋ฆฌ์— ๋งŽ์€ ์˜ํ–ฅ์„ ๋ผ์นจ.
  • ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋“ค์„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฐœ๋…(๋ณ€์ˆ˜, ํ•จ์ˆ˜, ์ƒ์† ๋“ฑ)์„ ํ™œ์šฉํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ.

๐Ÿ“Œ CSS in JS

CSS Preprocessor (CSS ์ „์ฒ˜๋ฆฌ๊ธฐ)

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

Syntactically Awesome Style Sheets (SASS)
๐Ÿ”ธ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ์ค‘ ํ•˜๋‚˜๋กœ CSS๋ฅผ ํ™•์žฅํ•ด์ฃผ๋Š” ์Šคํฌ๋ฆฝํŒ… ์–ธ์–ด.
๐Ÿ”ธ SCSS ์ฝ”๋“œ๋ฅผ ์ฝ์–ด์„œ ์ „์ฒ˜๋ฆฌํ•œ ๋‹ค์Œ ์ปดํŒŒ์ผํ•ด์„œ ์ „์—ญ CSS ๋ฒˆ๋“ค ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ์ฃผ๋Š” ์ „์ฒ˜๋ฆฌ๊ธฐ(preprocessor)์˜ ์—ญํ• 

๐Ÿ”ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์ฒ˜๋Ÿผ ํŠน์ • ์†์„ฑ(ex. color, margin, width ๋“ฑ)์˜ ๊ฐ’(ex. #ffffff, 25rem, 100px ๋“ฑ)์„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•˜์—ฌ ํ•„์š”ํ•œ ๊ณณ์— ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋ฅผ ์ ์šฉํ•˜๊ฑฐ๋‚˜ ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ์˜ ์„ ์–ธ์œผ๋กœ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ๋Š” ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง.

  • ๊ทธ๋Ÿฌ๋‚˜ CSS์˜ ์šฉ๋Ÿ‰์ด ์ปค์ง€๊ณ  ์ปดํŒŒ์ผ์„ ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Œ.
  • CSS ์ „์ฒ˜๋ฆฌ๊ธฐ์˜ ๋ฌธ์ œ๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด BEM, OOCSS, SMACSS ๊ฐ™์€ CSS ๋ฐฉ๋ฒ•๋ก ์ด ๋‚˜ํƒ€๋‚จ.

CSS ๋ฐฉ๋ฒ•๋ก 

๐Ÿ”ธ ์ข€ ๋” ํšจ์œจ์ ์œผ๋กœ CSS๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก CSS๋„ค์ด๋ฐ, ์ž‘์„ฑ ๋ฐฉ๋ฒ• ๋“ฑ์— ๊ทœ์น™์„ ๋‘์–ด ์ง€ํ‚ค๋Š” ๊ฒƒ์„ ๋งํ•จ.

BEM (Block Element Modifier)

  • Block--Element__Modifier๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ํด๋ž˜์Šค๋ช…์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•
    • Block : ๋ฌธ๋‹จ ์ „์ฒด์— ์ ์šฉ๋œ element๋‚˜ element๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ์ปจํ…Œ์ด๋„ˆ. ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋…๋ฆฝ์ ์ธ ํŽ˜์ด์ง€ ๊ตฌ์„ฑ ์š”์†Œ. ๋ธ”๋ก์€ ์žฌ์‚ฌ์šฉ๊ณผ ์ค‘์ฒฉ์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์—ฌ๋ฐฑ, ์œ„์น˜ ๋“ฑ์— ์˜ํ–ฅ๋ฐ›์ง€ ์•Š๋„๋ก ํ•ด์•ผํ•จ.
    • Element : Block ์•ˆ์—์„œ ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ. ๋‹จ๋…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ธ”๋ก์˜ ๋ถ€๋ถ„์ ์ธ ๊ตฌ์„ฑ์š”์†Œ๋กœ ๋ธ”๋ก์ฒ˜๋Ÿผ ์ค‘์ฒฉํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ ๋ธ”๋ก์— ์˜์กด์ ์ธ ์ด๋ฆ„์„ ๊ฐ€์ง.
    • Modifier : ์‚ฌ์ด์ฆˆ, Boolean ๊ฐ’์ฒ˜๋Ÿผ ๋ธ”๋ก์ด๋‚˜ ์š”์†Œ๋กœ ์•Œ ์ˆ˜ ์—†๋Š” ๋ชจ์–‘, ์ƒํƒœ, ํ–‰๋™์„ ์ •์˜
  • ์†Œ๋ฌธ์ž, ์ˆซ์ž๋งŒ์„ ์ด์šฉํ•ด ์ž‘๋ช…, ๊ธฐ๋ณธ์ ์œผ๋กœ ID๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  class๋งŒ์„ ์‚ฌ์šฉํ•จ.

๐Ÿ”ธ BEM, OOCSS, SMACSS ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ์ด ์žˆ์œผ๋‚˜ ๊ฒฐ๊ตญ ์„ธ ๋ฐฉ๋ฒ•๋ก  ๋ชจ๋‘ ๊ฐ™์€ ์ง€ํ–ฅ์ ์„ ๊ฐ€์ง.

  • ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ
  • ์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐํ™”(์œ ์ง€ ๋ณด์ˆ˜ ์šฉ์ด)
  • ์ฝ”๋“œ์˜ ํ™•์žฅ์„ฑ
  • ์ฝ”๋“œ์˜ ์˜ˆ์ธก์„ฑ(ํด๋ž˜์Šค ๋ช…์œผ๋กœ ์˜๋ฏธ ์˜ˆ์ธก)

๐Ÿ”ธ ๋‹จ์ 

  • ๊ธธ์–ด์ง€๊ณ  ๋ณต์žกํ•ด์ง€๋Š” ํด๋ž˜์Šค๋ช…
  • ๊ธด ํด๋ž˜์Šค๋ช…์œผ๋กœ ์ธํ•œ ๊ธธ์–ด์ง€๋Š” ๋งˆํฌ์—…
  • ์žฌ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ™•์žฅ
  • ์ง„์ •ํ•œ ์บก์Аํ™” ๊ฐœ๋…์˜ ๋ถ€์žฌ
    • ์บก์Аํ™”(Encapsulation) : ๊ฐ์ฒด์˜ ์†์„ฑ, ํ–‰์œ„๋ฅผ ๋ฌถ๊ณ , ์‹ค์ œ ๊ตฌํ˜„ ๋‚ด์šฉ ์ผ๋ถ€๋ฅผ ์™ธ๋ถ€์— ๊ฐ์ถ”์–ด ์€๋‹‰ํ•˜๋Š” ๊ฐœ๋….

๐Ÿ”ธ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CSS๋ฅผ ์ปดํฌ๋„ŒํŠธ ์˜์—ญ์œผ๋กœ ๋ถˆ๋Ÿฌ๋“ค์ด๊ธฐ ์œ„ํ•œ CSS-in-JS๊ฐ€ ๋‚˜ํƒ€๋‚จ.

๐Ÿ”ธ CSS-in-JS์—๋Š” ๋Œ€ํ‘œ์ ์œผ๋กœ Styled-Component๊ฐ€ ์žˆ์Œ.

CH2. CDD ๊ฐœ๋ฐœ๋„๊ตฌ

๐Ÿ“Œ Styled Components

๐Ÿ”ธ CSS๋ฅผ ์ปดํฌ๋„ŒํŠธํ™” ์‹œํ‚ด์œผ๋กœ์จ ๊ตฌ์กฐ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” React์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ.

๐Ÿ”ธ ๊ธฐ๋Šฅ์ (Functional) ํ˜น์€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋“ค๋กœ๋ถ€ํ„ฐ UI๋ฅผ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ฃผ ๋‹จ์ˆœํ•œ ํŒจํ„ด์„ ์ œ๊ณตํ•จ.

๐Ÿ”ธ HTML + JS + CSS๊นŒ์ง€ ๋ฌถ์–ด์„œ ํ•˜๋‚˜์˜ JSํŒŒ์ผ ์•ˆ์—์„œ ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์Œ.

๐Ÿ”ธ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ.

  • class, id ์ด๋ฆ„์„ ๋”ฐ๋กœ ์ง“์ง€ ์•Š์•„๋„ ๋จ.
  • ์ปดํฌ๋„ŒํŠธ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜์—ฌ ํƒœ๊ทธ๋ณ„๋กœ ์›ํ•˜๋Š” ๋ถ€๋ถ„์„ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ์Œ.
  • ์ค‘๋ณต๋˜๋Š” ์Šคํƒ€์ผ์€ ๊ฒน์น˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„๋งŒ ์ถ”๊ฐ€ํ•˜์—ฌ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ.
  • JS ๊ตฌ๋ฌธ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Œ (props, ์‚ผํ•ญ์—ฐ์‚ฐ์ž ๋“ฑ)

Styled Components ์„ค์น˜ํ•˜๊ธฐ

1. ํ„ฐ๋ฏธ๋„์— ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์„ค์น˜ํ•˜๊ธฐ
# with npm
$ npm install --save styled-components@latest

# with yarn
$ yarn add styled-components

2. JS ํŒŒ์ผ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
import styled from "styled-components"

๐Ÿ”ธ ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ Styled Components๊ฐ€ ์„ค์น˜๋˜์–ด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ package.json์— ์ž‘์„ฑ.

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

Styled Components ๋ฌธ๋ฒ•

1. ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

๐Ÿ”ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธ ํ›„, styled.ํƒœ๊ทธ์ข…๋ฅ˜๋ฅผ ํ• ๋‹นํ•˜๊ณ , ๋ฐฑํ‹ฑ ์•ˆ์— ๊ธฐ์กด CSS ๋ฌธ๋ฒ•์„ ์ž‘์„ฑ.
๐Ÿ”ธ ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌํ„ด๋ฌธ์•ˆ์— ์ž‘์„ฑํ•ด์ฃผ๋ฉด ์Šคํƒ€์ผ์ด ์ ์šฉ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋จ.

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

๐Ÿ”ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธํ•˜๊ณ  styled()์— ์žฌํ™œ์šฉํ•  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ „๋‹ฌํ•ด ์ค€ ๋‹ค์Œ, ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ์Šคํƒ€์ผ ์†์„ฑ ์ž‘์„ฑ.

3. Props ํ™œ์šฉํ•˜๊ธฐ

๐Ÿ”ธ props ๊ฐ’์— ๋”ฐ๋ผ์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋ง ๊ฐ€๋Šฅ
๐Ÿ”ธ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ๋ฒ•(${ })์„ ์‚ฌ์šฉํ•˜์—ฌ JavaScript ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉ. props๋ฅผ ๋ฐ›์•„์˜ค๋ ค๋ฉด props๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉ.

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

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

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

    ๐Ÿ”ธ props์˜ ๊ฐ’์„ ํ†ต์งธ๋กœ ํ™œ์šฉํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์— ํ™œ์šฉ.
    ๐Ÿ”ธ props.color๊ฐ€ ์—†๋‹ค๋ฉด white๋ฅผ, props.color๊ฐ€ ์žˆ๋‹ค๋ฉด props.color์˜ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€์„œ ์Šคํƒ€์ผ ์†์„ฑ ๊ฐ’์œผ๋กœ ๋ฆฌํ„ด. ๊ทธ ๊ฒฐ๊ณผ color๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ฐ›์€ props์˜ ๊ฐ’์œผ๋กœ ๋ฐฐ๊ฒฝ์ƒ‰์ด ์ง€์ •๋จ.
4. ์ „์—ญ ์Šคํƒ€์ผ ์„ค์ •ํ•˜๊ธฐ

๐Ÿ”ธ ์ „์—ญ ์Šคํƒ€์ผ์„ ์„ค์ •ํ•˜๋ ค๋ฉด, Styled Components์—์„œ createGlobalStyle ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ด.

import { createGlobalStyle } from "styled-components";

๐Ÿ”ธ ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด CSS ํŒŒ์ผ์—์„œ ์ž‘์„ฑํ•˜๋“ฏ ์„ค์ •ํ•ด ์ฃผ๊ณ  ์‹ถ์€ ์Šคํƒ€์ผ์„ ์ž‘์„ฑํ•˜๊ณ , ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ <GlobalStyle> ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•ด ์ฃผ๋ฉด ์ „์—ญ์— <GlobalStyle> ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์ด ์ ์šฉ๋จ.

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

CH2. Storybook

๐Ÿ”ธ CDD ๋ฐฉ๋ฒ•์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ๊ฐ UI ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์‹œ๊ฐ์ ์œผ๋กœ ์‰ฝ๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ๋„๊ตฌ.

๐Ÿ”ธ ๋งŒ๋“ค์–ด ๋‘” ๊ฐ UI ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์นดํƒˆ๋กœ๊ทธ์ฒ˜๋Ÿผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ๊ฐ props๋ฅผ ์„ค์ •ํ•ด๋ณด๋ฉฐ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ์Œ.

๐Ÿ”ธ UN์˜ Storybook ํŽ˜์ด์ง€

Storybook ํŠน์ง•

๐Ÿ”ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฌธ์„œํ™”ํ•˜์—ฌ ํšŒ์‚ฌ์˜ UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์‚ฌ์šฉ(์žฌ์‚ฌ์šฉ์„ฑ ์šฉ์ด)

  • ๋˜ํ•œ, ์™ธ๋ถ€ ๊ณต๊ฐœ์šฉ ๋””์ž์ธ ์‹œ์Šคํ…œ(Design System)์„ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ ํ”Œ๋žซํผ์œผ๋กœ ์‚ฌ์šฉ.

๐Ÿ”ธ ์ž๋™์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‹œ๊ฐํ™”ํ•˜์—ฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•  ์ˆ˜ ์žˆ์Œ. (์ด๋ฅผ ํ†ตํ•ด ๋ฒ„๊ทธ๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์คŒ)

๐Ÿ”ธ ํ…Œ์ŠคํŠธ ๋ฐ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ํ–ฅ์ƒํ•˜๋Š” ์žฅ์ ์ด ์žˆ์œผ๋ฉฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋˜ํ•œ ์˜์กด์„ฑ์„ ๊ฑฑ์ •ํ•˜์ง€ ์•Š๊ณ  ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ์Œ.

๐Ÿ”ธ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋…๋ฆฝ์ ์ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜์–ด, ๊ฐœ๋ฐœ์ž๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๊ตฌ์• ๋ฐ›์ง€ ์•Š๊ณ  UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง‘์ค‘์ ์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์Œ.

Storybook์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

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

Storybook ์„ค์น˜ & ์‹คํ–‰

๐Ÿ”ธ React๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•. CRA์„ ํ†ตํ•ด React ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•˜๊ณ , ์ƒ์„ฑ๋œ ํด๋” ์•ˆ ํ„ฐ๋ฏธ๋„์— ์•„๋ž˜์˜ ๋ช…๋ น์–ด ์ž…๋ ฅ.

// ์„ค์น˜ํ•˜๊ธฐ
npx storybook@latest init

๐Ÿ”ธ package.json์„ ๋ณด๊ณ  ์‚ฌ์šฉ ์ค‘์ธ ํ”„๋ก ํŠธ์—”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋งž๋Š” Storybook ์‚ฌ์šฉ ํ™˜๊ฒฝ์„ ์•Œ์•„์„œ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ๊ผญ React๊ฐ€ ์•„๋‹ˆ๋”๋ผ๋„
๋‹ค์–‘ํ•œ ํ”„๋ก ํŠธ์—”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

๐Ÿ”ธ Storybook ์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด, ์•„๋ž˜์˜ ํด๋”๊ฐ€ ์ƒ์„ฑ๋จ.

  • /.storybook : Storybook ๊ด€๋ จ ์„ค์ • ํŒŒ์ผ
  • /src/stories : Storybook ์˜ˆ์‹œ ํŒŒ์ผ๋“ค

๐Ÿ”ธ npm run storybook : localhost:6006์œผ๋กœ ์ ‘๊ทผํ•˜์—ฌ Storybook์„ ์‹คํ–‰.

Storybook ์ž‘์„ฑ

1. ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ Storybook๋งŒ๋“ค๊ธฐ

๐Ÿ”ธ Storybook์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ์ด ๋  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋จผ์ € ์ž‘์„ฑ.
๐Ÿ”ธ ์•„๋ž˜์™€ ๊ฐ™์ด src ํด๋”์— Button.js ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๊ธฐ๋ณธ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ฌ.

import React from "react";

const Button = ({title, color}) => (
<button style={{color: color}}>{title}</button>
);

export default Button;

๐Ÿ”ธ ๊ฐ™์€ src ํด๋” ์•ˆ์— Button.stories.js ํŒŒ์ผ์„ ๋งŒ๋“ฌ (/.storybook ์•ˆ์— ์žˆ๋Š” storkbook ์„ค์ • ํŒŒ์ผ์— ์˜ํ•ด ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ๋ช….stories.js๋กœ ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ์Šคํ† ๋ฆฌ๋กœ ์ธ์‹๋จ.

// Button.stories.js
import Title from "./Button";

export default {
    title: "UI Components/Button", // storybook ๋ฌธ์„œ์˜ ์นดํ…Œ๊ณ ๋ฆฌ
    component: Button, //์Šคํ† ๋ฆฌ๋กœ ๋งŒ๋“ค ์ปดํฌ๋„ŒํŠธ
  //์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ์ „๋‹ฌ์ธ์ž์˜ ์ข…๋ฅ˜์™€ ํƒ€์ž…
    argTypes: {
        title: { control: "text" }, // control ํŒจ๋„์—์„œ ์ง์ ‘ ํ…์ŠคํŠธ ์ž…๋ ฅ ๊ฐ€๋Šฅ
        color: { control: "text"}, // control ํŒจ๋„์—์„œ ์ง์ ‘ ์ปฌ๋Ÿฌ ์ž…๋ ฅ ๊ฐ€๋Šฅ
        size: { control: { type:'radio', options : ['big', 'normal', 'small'] }}, // ์ปจํŠธ๋กค ํŒจ๋„์—์„œ radioํƒ€์ž… ๋ฒ„ํŠผ์œผ๋กœ big, normal, small์˜ ์˜ต์…˜์„ ์„ ํƒ ๊ฐ€๋Šฅ
    }
}

//ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ: Button ์ปดํฌ๋„ŒํŠธ๊ฐ€ args๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ props๋กœ ๋‚ด๋ ค์ค€๋‹ค.
const Template = (args) => <Button {...args} />
//๋งŒ์•ฝ ์Šคํ† ๋ฆฌ๋ถ์—์„œ ์ธ์ž๋ฅผ ์ง์ ‘ ๋ฐ›์œผ๋ ค๋ฉด ์œ„์˜ Template๋ฅผ ๊ทธ๋Œ€๋กœ exportํ•œ๋‹ค.

//์Šคํ† ๋ฆฌ ์ƒ์„ฑ: ์Šคํ† ๋ฆฌ๋Š” export const๋กœ ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”๊ฐ€ํ•œ๋‹ค.
export const RedButton = Template.bind({}); //๋’ค์˜ ์‚ฌํ•ญ์€ ๊ฑฐ์˜ ์ •ํ•ด์ง„ ๋ฌธ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ข‹๋‹ค.

// ๋งŒ๋“ค์–ด์ค€ ์Šคํ† ๋ฆฌ์— ์ธ์ž๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
RedButton.args= {
    title: "Red Button",
    color: "#ff0000"
}

export const StorybookButton = (args) =>{
    return <Button {...args} />
}

๐Ÿ”ธ ์Šคํ† ๋ฆฌ๋ถ์„ ์‹คํ–‰ํ•˜๋ฉด ๋งŒ๋“  ์Šคํ† ๋ฆฌ๋ฅผ ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ.

CH4. useRef

๐Ÿ”ธ React๋ฅผ ๋‹ค๋ฃฐ๋•Œ DOM์„ ์ง์ ‘ ์กฐ์ž‘ํ•˜๋Š” ๊ฒƒ์€ ์ง€์–‘ํ•ด์•ผํ•จ.
๐Ÿ”ธ ํ•˜์ง€๋งŒ ์•„๋ž˜ ์˜ˆ์‹œ์™€ ๊ฐ™์ด DOM ์—˜๋ฆฌ๋จผํŠธ์˜ ์ฃผ์†Œ๊ฐ’์„ ํ™œ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์กด์žฌ. ์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ Hook ํ•จ์ˆ˜ useRef.

๐Ÿ”ธ useRef๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ

  • focus : ํ˜„์žฌ input์—์„œ ์—”ํ„ฐํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋‹ค์Œ input์„ ํฌ์ปค์‹ฑ์‹œํ‚ฌ ๋•Œ. (input ์š”์†Œ๋ฅผ ํด๋ฆญํ•˜์ง€ ์•Š๊ณ  ํฌ์ปค์Šค๋ฅผ ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ)
  • media playback : ์‚ฝ์ž…๋œ ๋™์˜์ƒ์˜ ์žฌ์ƒ, ์ผ์‹œ์ •์ง€๋ฅผ ๋‹ค๋ฅธ ๋ฒ„ํŠผ์œผ๋กœ ์ œ์–ดํ•˜๊ณ ์ž ํ•  ๋•Œ.
  • ๊ทธ ์™ธ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ, text selection ๋“ฑ DOM์˜ ์ฃผ์†Œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ.
  • d3.js, greensock ๋“ฑ DOM ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉํ•  ๋•Œ.

๐Ÿ”ธ React๋Š” ์ด๋Ÿฐ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์—์„œ useRef๋กœ DOM ๋…ธ๋“œ, ์—˜๋ฆฌ๋จผํŠธ, ๊ทธ๋ฆฌ๊ณ  React ์ปดํฌ๋„ŒํŠธ ์ฃผ์†Œ๊ฐ’์„ ์ฐธ์กฐ๊ฐ€๋Šฅ.

๐Ÿ”ธ ์•„๋ž˜ ์˜ˆ์‹œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜๋ฉด ์ฃผ์†Œ๊ฐ’์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

const ์ฃผ์†Œ๊ฐ’(DOM)์„ ํ• ๋‹นํ•  ์ž„์˜์˜ ๋ณ€์ˆ˜ el = useRef(์ฐธ์กฐ์ž๋ฃŒํ˜•)

return (
	<div>
    	<input ref={el} type="text />
        // React์—์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ref๋ผ๋Š” ์†์„ฑ์— ๋ณ€์ˆ˜๋ฅผ ํ• ๋‹นํ•˜๋ฉด, ๊ทธ ๋ณ€์ˆ˜์—๋Š” ์—˜๋ฆฌ๋จผํŠธ์˜ ์ฃผ์†Œ๊ฐ€ ๋‹ด๊น€.
        // ํ–ฅํ›„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ด ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Œ.
    </div>);

๐Ÿ”ธ ์ด ์ฃผ์†Œ๊ฐ’์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ re-render ๋˜๋”๋ผ๋„ ๋ฐ”๋€Œ์ง€ ์•Š์Œ. ์ด๋ฅผ ์ด์šฉํ•ด ์ œํ•œ๋œ ์ƒํ™ฉ์—์„œ useRef์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

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

๐Ÿ”ธ ์ œ์‹œ๋œ ์ƒํ™ฉ ์ œ์™ธํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ React ๋ฌธ๋ฒ•์„ ๋ฒ—์–ด๋‚˜ useRef๋ฅผ ๋‚จ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ถ€์ ์ ˆํ•˜๊ณ , React์˜ ํŠน์ง•์ด์ž ์žฅ์ ์ธ ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™๊ณผ ๋ฐฐ์น˜๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์กฐ์‹ฌํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•จ.

์ฐธ๊ณ  ์‚ฌ์ดํŠธ

๐Ÿ”ธ styled-components
๐Ÿ”ธ Storybook

profile
์ฝ”๋ฆฐ์ด๐Ÿ‘ฝ

0๊ฐœ์˜ ๋Œ“๊ธ€