Unit3 - [React] Custom Component

๊ฐ•์„ฑ์ผยท2023๋…„ 6์›” 16์ผ
0
post-thumbnail

โœ… TIL


์˜ค๋Š˜์€ ์ „์— ๋‹ค๋ค„๋ดค์—ˆ๋˜ Styled-Components ์— ๋Œ€ํ•ด ๋ฐฐ์šด๋‹ค.
์ด๊ฒƒ์ด CDD์ด๋ฉฐ, ๋˜ ๋‹ค๋ฅธ ํˆด์ธ Storybook ์ด๋ผ๋Š” ๊ฒƒ์ด ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ฒ˜์Œ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค.

๋จผ์ € ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์ด ๋“ฑ์žฅํ•˜๊ฒŒ ๋œ ๋ฐฐ๊ฒฝ์ธ Component Driven Development(CDD)์™€ CSS-in-JS์— ๋Œ€ํ•ด์„œ ๋‹ค๋ค„๋ณด๊ฒ ๋‹ค.



Component Driven Development


CDD

ํŽ˜์ด์ง€๊ฐ€ ๋ชจ๋‘ ์™„์„ฑ๋˜์—ˆ๋Š”๋ฐ, ๋‹ค๋ฅธ ํŽ˜์ด์ง€์— ์ ์šฉ๋˜๋Š” ๋ฒ„ํŠผ์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ๊ธฐํš์•ˆ์ด ๋„์ฐฉํ–ˆ๋‹ค.

์–ด? ๊ทธ๋Ÿฐ๋ฐ ๊ธฐํš์ž๊ฐ€ ์š”์ฒญํ•œ ์‚ฌํ•ญ์—์„œ ๋ฒ„ํŠผ์— ๋Œ€ํ•œ ๊ธฐํš์ด ์ด์ „์— ์š”์ฒญ๋ฐ›์•˜๋˜ ๋ฒ„ํŠผ์„ ๋˜‘๊ฐ™์ด ์‚ฌ์šฉํ•˜๋„๋ก ์š”์ฒญํ–ˆ๋‹ค๋ฉด?
๋””์ž์ด๋„ˆ์™€ ๊ฐœ๋ฐœ์ž๋Š” ์ด ๋ถ€๋ถ„์„ ๋ชจ๋‘ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๊ฑธ๊นŒ?

๋‹ต์€ ์•„๋‹ˆ๋‹ค.
๋””์ž์ธ๊ณผ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋ถ€ํ„ฐ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฏธ๋ฆฌ ๋””์ž์ธํ•˜๊ณ  ๊ฐœ๋ฐœํ•˜๋ฉด ์ด๋Ÿฐ ๊ณ ๋ฏผ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๊ณ ๋ฏผ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•์ด ๋ฐ”๋กœ Component Driven Development (CDD) ์ด๋‹ค.

๋ ˆ๊ณ ์ฒ˜๋Ÿผ ์กฐ๋ฆฝํ•ด ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๋ถ€ํ’ˆ ๋‹จ์œ„๋กœ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋‚˜๊ฐ€๋Š” ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.



CSS in JS

์ธํ„ฐ๋„ท์ด ๋งŒ๋“ค์–ด์ง„ ์ดํ›„ ๊ธฐ์ˆ ์˜ ๋ฐœ๋‹ฌ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ์ž๋“ค์€ ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ(๋””๋ฐ”์ด์Šค)์—์„œ ์ธํ„ฐ๋„ท์„ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.
์ด์— ๋”ฐ๋ผ ๊ฐœ๋ฐœ์ž๋“ค์˜ CSS ์ž‘์„ฑ ๋ฐฉ์‹๋„ ๊พธ์ค€ํžˆ ์ง„ํ™”ํ•ด ์˜ค๊ณ  ์žˆ๋‹ค.

ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ๋‚˜ ๋ณต์žก๋„๊ฐ€ ์ ์  ์ปค์ง€๊ณ  ํ•จ๊ป˜ ์ž‘์—…ํ•ด์•ผ ํ•  ํŒ€์› ์ˆ˜๋„ ๋งŽ์•„์ง์—
๋”ฐ๋ผ CSS๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ผ๊ด€๋œ ํŒจํ„ด์ด ์—†๋‹ค๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ๊ฐ€์žฅ ํฐ ๊ฑธ๋ฆผ๋Œ์ด ๋˜์—ˆ๋‹ค.

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

์ด๋Ÿฌํ•œ ๋ฌธ์ œ์ ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CSS ์ „์ฒ˜๋ฆฌ๊ธฐ(CSS Preprocessor)๋ผ๋Š” ๊ฐœ๋…์ด ๋“ฑ์žฅํ–ˆ๋‹ค.

CSS ์ „์ฒ˜๋ฆฌ๊ธฐ(CSS Preprocessor)๋ž€ CSS๊ฐ€ ๊ตฌ์กฐ์ ์œผ๋กœ ์ž‘์„ฑ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋„์›€์„ ์ฃผ๋Š” ๋„๊ตฌ์ด๋‹ค.

ํ•˜์ง€๋งŒ ์ด CSS ์ „์ฒ˜๋ฆฌ๊ธฐ(CSS Preprocessor) ์ž์ฒด๋งŒ์œผ๋กœ๋Š” ์›น ์„œ๋ฒ„๊ฐ€ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์—
๊ฐ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ์— ๋งž๋Š” Compiler๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ณ  ์ปดํŒŒ์ผ์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์‹ค์ œ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” CSS ๋ฌธ์„œ๋กœ ๋ณ€ํ™˜์ด ๋œ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด CSS ํŒŒ์ผ๋“ค์„ ์ž˜ ๊ตฌ์กฐํ™”ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ณ , ์ตœ์†Œํ•œ CSS ํŒŒ์ผ์„ ์ž‘์€ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์ƒ๊ฒผ๋‹ค.

CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ์ค‘์—์„œ ๊ฐ€์žฅ ์œ ๋ช…ํ•œ SASS๋Š”
Syntactically Awesome Style Sheets์˜ ์•ฝ์ž๋กœ CSS๋ฅผ ํ™•์žฅํ•ด ์ฃผ๋Š” ์Šคํฌ๋ฆฝํŒ… ์–ธ์–ด์ด๋‹ค.

์ฆ‰, CSS๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์–ธ์–ด๋กœ์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์ฒ˜๋Ÿผ ํŠน์ • ์†์„ฑ์˜ ๊ฐ’์„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•˜์—ฌ ํ•„์š”ํ•œ ๊ณณ์— ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋ฅผ ์ ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ , ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ์˜ ์„ ์–ธ์œผ๋กœ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ๋Š” ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ๊ฐ€์กŒ๋‹ค.

๊ทธ๋ž˜์„œ SASS๋Š” SCSS ์ฝ”๋“œ๋ฅผ ์ฝ์–ด์„œ ์ „์ฒ˜๋ฆฌํ•œ ๋‹ค์Œ ์ปดํŒŒ์ผํ•ด์„œ
์ „์—ญ CSS ๋ฒˆ๋“ค ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ์ฃผ๋Š” ์ „์ฒ˜๋ฆฌ๊ธฐ(preprocessor)์˜ ์—ญํ• ์„ ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„์„œ SASS๊ฐ€ โ€˜CSS์˜ ๊ตฌ์กฐํ™”โ€™๋ฅผ
ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ์˜ ์žฅ์ ๋ณด๋‹ค ๋‹ค๋ฅธ ๋ฌธ์ œ๋“ค์„ ๋” ๋งŽ์ด ๋งŒ๋“ค์–ด๋‚ธ๋‹ค๋Š” ๊ฒƒ์ด ๋ฐํ˜€์กŒ๋‹ค.

๊ฒฐ๊ตญ ์šฐ๋ฆฌ๋Š” ์ „์ฒ˜๋ฆฌ๊ธฐ(preprocessor)๊ฐ€ ๋‚ด๋ถ€์—์„œ ์–ด๋–ค ์ž‘์—…์„ ํ•˜๋Š”์ง€๋Š” ์•Œ์ง€ ๋ชปํ•œ ์ฑ„, ์Šคํƒ€์ผ์ด ๊ฒน์น˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ์ˆœํžˆ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๋Š” ๊ฒƒ์— ์˜์ง€ํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ๊ทธ ๊ฒฐ๊ณผ ์ปดํŒŒ์ผ๋œ CSS์˜ ์šฉ๋Ÿ‰์€ ์–ด๋งˆ์–ด๋งˆํ•˜๊ฒŒ ์ปค์ง€๊ฒŒ ๋˜์—ˆ๋‹ค.

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

๋ฐฉ๋ฒ•๋ก ์˜ ๊ณตํ†ต ์ง€ํ–ฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

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

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

๋Œ€ํ‘œ์ ์ธ CSS ๋ฐฉ๋ฒ•๋ก ์œผ๋กœ๋Š” BEM์ด ์žˆ๋‹ค.

BEM์ด๋ž€ Block, Element, Modifier๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ํด๋ž˜์Šค๋ช…์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋ฉฐ,
Block, Element, Modifier ๊ฐ๊ฐ์€ โ€”์™€ __๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค.

ํด๋ž˜์Šค๋ช…์€ BEM ๋ฐฉ์‹์˜ ์ด๋ฆ„์„ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ฐ˜๋ณตํ•˜์—ฌ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ฉฐ
HTML/CSS/SASS ํŒŒ์ผ์—์„œ๋„ ๋” ์ผ๊ด€๋œ ์ฝ”๋”ฉ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•๋ก ๋“ค์—์„œ๋„ ๋ฌธ์ œ์ ์ด ๋ฐœ์ƒํ•˜๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค. ํด๋ž˜์Šค๋ช… ์„ ํƒ์ž๊ฐ€ ์žฅํ™ฉํ•ด์ง€๊ณ , ์ด๋Ÿฐ ๊ธด ํด๋ž˜์Šค๋ช… ๋•Œ๋ฌธ์— ๋งˆํฌ์—…์ด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ปค์ง€๋ฉฐ, ์žฌ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ™•์žฅํ•ด์•ผ๋งŒ ํ–ˆ๋‹ค.

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

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๊ฐœ๋ฐœ ๋ฐฉํ–ฅ์ด ์ง„ํ™”ํ•˜๋ฉด์„œ ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„์˜ ๊ฐœ๋ฐœ์€ ์บก์Šํ™”์˜ ์ค‘์š”์„ฑ์„ ๋ถˆ๋Ÿฌ์™”๋‹ค.
ํ•˜์ง€๋งŒ CSS๋Š” ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜์˜ ๋ฐฉ์‹์„ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ์ ์ด ํ•œ ๋ฒˆ๋„ ์—†์—ˆ๋‹ค.

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

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

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


CSS ๋ฐฉ๋ฒ•๋ก ๋“ค์˜ ํŠน์ง•, ์žฅ๋‹จ์  overview



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


Styled Components


CSS ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ถˆํŽธํ•จ์„ ๋Š๊ปด๋ณธ ์  ์žˆ๋Š”๊ฐ€?

  • class, id ์ด๋ฆ„์„ ์ง“๋Š๋ผ ๊ณ ๋ฏผํ•œ ์ ์ด ์žˆ๋‹ค.
  • CSS ํŒŒ์ผ ์•ˆ์—์„œ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋ถ€๋ถ„์„ ์ฐพ๊ธฐ ํž˜๋“ค์—ˆ๋‹ค.
  • CSS ํŒŒ์ผ์ด ๋„ˆ๋ฌด ๊ธธ์–ด์ ธ์„œ ํŒŒ์ผ์„ ์ชผ๊ฐœ์„œ ๊ด€๋ฆฌํ•ด ๋ณธ ์ ์ด ์žˆ๋‹ค.
  • ์Šคํƒ€์ผ ์†์„ฑ์ด ๊ฒน์ณ์„œ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š์€ ์ ์ด ์žˆ๋‹ค.

์ด ์™ธ์—๋„ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์–ด๋ ค์›€์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ๋ถˆํŽธํ•จ์„ CSS๋ฅผ ์ปดํฌ๋„ŒํŠธํ™” ์‹œํ‚ด์œผ๋กœ์จ ํ•ด๊ฒฐํ•ด ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค.
๋ฐ”๋กœ React ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Styled Components๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

Styled Components๋Š” ์•ž์„œ ๋ฐฐ์šด CSS in JS๋ผ๋Š” ๊ฐœ๋…์ด ๋Œ€๋‘๋˜๋ฉด์„œ ๋‚˜์˜จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

๊ธฐ์กด์— HTML, CSS, JS ํŒŒ์ผ๋กœ ์ชผ๊ฐœ์„œ ๊ฐœ๋ฐœํ•˜๋˜ ๋ฐฉ๋ฒ•์—์„œ, React ๋“ฑ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋“ฑ์žฅ์œผ๋กœ
์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ๊ฐœ๋ฐœ์ด ์ฃผ๋ฅ˜๊ฐ€ ๋˜์—ˆ์ง€๋งŒ, CSS๋Š” ๊ทธ๋ ‡์ง€ ๋ชปํ–ˆ๋‹ค๋Š” ์ ์—์„œ ์ถœ๋ฐœํ•œ ๊ฐœ๋…์ด๋‹ค.

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

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

// Styled Components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜
# with npm
$ npm install --save styled-components@latest

# with yarn
$ yarn add styled-components

// ๊ทธ ๋‹ค์Œ Styled Components๋ฅผ ์‚ฌ์šฉํ•  ํŒŒ์ผ๋กœ ๋ถˆ๋Ÿฌ์™€์ฃผ๋ฉด ์‚ฌ์šฉ ์ค€๋น„๋Š” ์™„๋ฃŒ!
import styled from "styled-components"


Styled Components ๋ฌธ๋ฒ•

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

Styled Components๋Š” ES6์˜ Templete Literals ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ฆ‰, ๋”ฐ์˜ดํ‘œ๊ฐ€ ์•„๋‹Œ ๋ฐฑ ํ‹ฑ(`)์„ ์‚ฌ์šฉํ•œ๋‹ค.

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

import styled from "styled-components";

//Styled Components๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ 
const BlueButton = styled.button`
  background-color: blue;
  color: white;
`;

export default function App() {
  // React ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋“ฏ์ด ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  return <BlueButton>Blue Button</BlueButton>;
}

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

์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ™œ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.

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

import styled from "styled-components";

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

//๋งŒ๋“ค์–ด์ง„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ™œ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const BigBlueButton = styled(BlueButton)`
  padding: 10px;
  margin-top: 10px;
`;

//์žฌํ™œ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ™œ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
const BigRedButton = styled(BigBlueButton)`
  background-color: red;
`;

export default function App() {
  return (
    <>
      <BlueButton>Blue Button</BlueButton>
      <br />
      <BigBlueButton>Big Blue Button</BigBlueButton>
      <br />
      <BigRedButton>Big Red Button</BigRedButton>
    </>
  );
}

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

Styled Component๋กœ ๋งŒ๋“  ์ปดํฌ๋„ŒํŠธ๋„ React ์ปดํฌ๋„ŒํŠธ์ฒ˜๋Ÿผ props๋ฅผ ๋‚ด๋ ค์ค„ ์ˆ˜ ์žˆ๋‹ค.
๋‚ด๋ ค์ค€ props ๊ฐ’์— ๋”ฐ๋ผ์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋ง ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

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

โš™๏ธ 1. Props๋กœ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋งํ•˜๊ธฐ

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

Button1์˜ ๊ฒฝ์šฐ๋Š” skyblue๋ผ๋Š” props๊ฐ€ ์žˆ์–ด ๋ฐฐ๊ฒฝ์ƒ‰์ด skyblue๋กœ ์ง€์ •๋๊ณ ,
Button2์˜ ๊ฒฝ์šฐ๋Š” props๊ฐ€ ์•„์˜ˆ ์—†์–ด ๋ฐฐ๊ฒฝ์ƒ‰์ด white๋กœ ์ง€์ •๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

import styled from "styled-components";
import GlobalStyle from "./GlobalStyle";
//๋ฐ›์•„์˜จ prop์— ๋”ฐ๋ผ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
const Button1 = styled.button`
  background: ${(props) => (props.skyblue ? "skyblue" : "white")};
`;

export default function App() {
  return (
    <>
      <GlobalStyle />
      <Button1>Button1</Button1>
      <Button1 skyblue>Button1</Button1>
    </>
  );
}

โš™๏ธ 2. Props ๊ฐ’์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ

3๋ฒˆ๊ณผ ๋น„์Šทํ•˜๊ฒŒ, props์˜ ๊ฐ’์„ ํ†ต์งธ๋กœ ํ™œ์šฉํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

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

๊ผญ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

JavaScript ์ฝ”๋“œ๋ผ๋ฉด ๋ฌด์—‡์ด๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์›ํ•˜๋Š” ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ์ˆ˜ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

import styled from "styled-components";
import GlobalStyle from "./GlobalStyle";

//๋ฐ›์•„์˜จ prop ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•ด ๋ Œ๋”๋งํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค
const Button1 = styled.button`
  background: ${(props) => (props.color ? props.color : "white")};
`;
//๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ๋„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const Button2 = styled.button`
  background: ${(props) => props.color || "white"};
`;

export default function App() {
  return (
    <>
      <GlobalStyle />
      <Button1>Button1</Button1>
      <Button1 color="orange">Button1</Button1>
      <Button1 color="tomato">Button1</Button1>
      <br />
      <Button2>Button2</Button2>
      <Button2 color="pink">Button2</Button2>
      <Button2 color="turquoise">Button2</Button2>
    </>
  );
}

4. ์ „์—ญ ์Šคํƒ€์ผ ์„ค์ •ํ•˜๊ธฐ

์Šคํƒ€์ผ์„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ์ข‹์ง€๋งŒ, ์ „์—ญ์— ์Šคํƒ€์ผ์„ ์„ค์ •ํ•˜๊ณ  ์‹ถ์„ ๋• ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์ข‹์„๊นŒ?
๋ฌผ๋ก  Styled Components๋Š” ์ด๋Ÿฐ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•œ ์ปดํฌ๋„ŒํŠธ๋„ ์ค€๋น„ํ•ด ๋†“์•˜๋‹ค.

์šฐ์„  ์ „์—ญ ์Šคํƒ€์ผ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด Styled Components์—์„œ createGlobalStyle ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
๊ทธ๋‹ค์Œ ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด CSS ํŒŒ์ผ์—์„œ ์ž‘์„ฑํ•˜๋“ฏ ์„ค์ •ํ•ด ์ฃผ๊ณ  ์‹ถ์€ ์Šคํƒ€์ผ์„ ์ž‘์„ฑํ•œ๋‹ค.

import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
	button {
		padding : 5px;
    margin : 2px;
    border-radius : 5px;
	}
`

function App() {
	return (
		<>
			<GlobalStyle />
			<Button>์ „์—ญ ์Šคํƒ€์ผ ์ ์šฉํ•˜๊ธฐ</Button>
		</>
	);
}


์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ GlobalStyle ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•ด ์ฃผ๋ฉด, ์ „์—ญ์— GlobalStyle ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์ด ์ ์šฉ๋œ๋‹ค.



Styled Components ๊ด€๋ จ ์ต์Šคํ…์…˜ ์†Œ๊ฐœ

Styled Components๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, CSS ์ฝ”๋“œ๋ฅผ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•ด์„œ ์ž‘์„ฑํ•˜๊ฒŒ ๋œ๋‹ค.

์ฆ‰, CSS ์ฝ”๋“œ๋ฅผ ๋ฌธ์ž์—ด๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ž˜ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ VSCode์—์„œ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ,
CSS ํŒŒ์ผ์—์„œ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ž๋™ ์™„์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

์ด๋•Œ ๋„์›€์ด ๋˜๋Š” VSCode ์ต์Šคํ…์…˜์ด ์žˆ๋‹ค. ๋ฐ”๋กœ VSCode-Styled-Components๋ผ๋Š” ์ต์Šคํ…์…˜์ด๋‹ค.

VSCode ์ขŒ์ธก์˜ Extensions ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ ํ›„, Vscode-Styled-Components๋ฅผ ๊ฒ€์ƒ‰ํ–ˆ์„ ๋•Œ๋‚˜์˜ค๋Š” ์ฒซ ๋ฒˆ์งธ ์ต์Šคํ…์…˜์„ ์„ค์น˜ํ•˜๋ฉด ๋œ๋‹ค.



Storybook


Storybook์€ UI ๊ฐœ๋ฐœ ์ฆ‰, Component Driven Development๋ฅผ ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ด๋‹ค.

๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋”ฐ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌ์„ฑํ•ด ์ฃผ์–ด ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ณต์žกํ•œ ๊ฐœ๋ฐœ ์Šคํƒ์„ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๊ฐ•์ œ ์ด๋™ํ•˜๊ฑฐ๋‚˜,
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํƒ์ƒ‰ํ•  ํ•„์š” ์—†์ด ์ „์ฒด UI๋ฅผ ํ•œ๋ˆˆ์— ๋ณด๊ณ  ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

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

Storybook์—์„œ ์ง€์›ํ•˜๋Š” ์ฃผ์š” ๊ธฐ๋Šฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

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

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

๋จผ์ € ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ ํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด, ํด๋” ์•ˆ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ Storybook์„ ์„ค์น˜ํ•œ๋‹ค.

npx storybook@latest init

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

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

์ด์ œ ํ„ฐ๋ฏธ๋„ ์ฐฝ์— ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ Storybook์„ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

npm run storybook

๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด React๊ฐ€ localhost:3000์œผ๋กœ ์ ‘๊ทผํ•˜๋“ฏ์ด, localhost:6006์œผ๋กœ ์ ‘๊ทผํ•˜์—ฌ Storybook์„ ์‹คํ–‰์‹œํ‚จ๋‹ค.

Storybook์„ ์‹คํ–‰ํ•˜๋ฉด /src/stories ํด๋” ์•ˆ์— ์žˆ๋˜, Storybook์—์„œ ๋งŒ๋“ค์–ด๋†“์€ ์˜ˆ์‹œ ์Šคํ† ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ Storybook์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๊ณ  ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์ƒํƒœ๋ฅผ
๋ณ€๊ฒฝํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์น˜์ง€ ์•Š์•„๋„ ์ƒํƒœ ๋ณ€ํ™”์— ๋”ฐ๋ฅธ ์ปดํฌ๋„ŒํŠธ์˜ ๋ณ€ํ™”๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ž‘์„ฑ ๋ฐฉ๋ฒ•

์ด์ œ ์ง์ ‘ Storybook ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž.
๊ทธ ์ „์—, ํŠœํ† ๋ฆฌ์–ผ์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์‚ฌ์šฉํ•  Styled-Components๋ฅผ ๋จผ์ € ์„ค์น˜ํ•œ๋‹ค.

npm install styled-components

์„ค์น˜๋ฅผ ์™„๋ฃŒํ–ˆ๋‹ค๋ฉด, ๊ฐ„๋‹จํ•œ ์Šคํ† ๋ฆฌ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด ๋ณด์ž.

src ํด๋” ์•ˆ์— Title.js ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ React ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด 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 ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ ๋‹ค.

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

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.bind({}); ๋Š” ์ •ํ•ด์ง„ ๋ฌธ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
export const RedTitle = Template.bind({});

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

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

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

Title.stories.js ํŒŒ์ผ์„ ์ €์žฅํ•˜๊ณ  Storybook์„ ํ™•์ธํ•ด ๋ณด์ž.
Storybook์€ ํ•ซ ๋ชจ๋“ˆ ๋ฆฌ๋กœ๋”ฉ์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • title: โ€œPractice/Titleโ€ ๋กœ ์ž‘์„ฑํ•ด ์ค€ ์ฝ”๋“œ๊ฐ€ ์ขŒ์ธก ๋ฉ”๋‰ด์—์„œ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ์ ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์ด๋ ‡๊ฒŒ Practice๋ผ๋Š” ์นดํ…Œ๊ณ ๋ฆฌ์— Title ์™ธ์˜ ์Šคํ† ๋ฆฌ๋„ ๋‹ด์•„์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • title.stories.js ์•ˆ์—์„œ ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•ด ๋งŒ๋“  ๋‘ ๊ฐœ์˜ ์Šคํ† ๋ฆฌ๊ฐ€ Title ์•ˆ์— ๋“ค์–ด์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
    ๊ฐ ์Šคํ† ๋ฆฌ์—์„œ๋Š” ์ž‘์„ฑํ•ด ์ค€ ์ „๋‹ฌ์ธ์ž๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์ „๋‹ฌ์ธ์ž์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๋ฌผ์ด ๋‹ฌ๋ผ์ง„ ๊ฒƒ๋„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

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

...
export const StorybookTitle = (args) =>{
    return <Title {...args} />
}

์ด ์Šคํ† ๋ฆฌ๋Š” ํ…œํ”Œ๋ฆฟ์„ ํ™œ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์ „๋‹ฌ์ธ์ž๋ฅผ ๋ฐ›๊ณ  ์žˆ๋‹ค. ์ €์žฅ ํ›„ ๋‹ค์‹œ Storybook์„ ํ™•์ธํ•ด ๋ณด์ž.

์ด๋ฒˆ์— ๋งŒ๋“  ์Šคํ† ๋ฆฌ๋Š” ์ „๋‹ฌ ์ธ์ž๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด ์ฃผ๋ฉด ๊ฑฐ๊ธฐ์— ๋งž์ถฐ ๋ชจ์Šต์ด ๋ณ€ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฒˆ์—๋Š” ์ „๋‹ฌ ์ธ์ž๋ฅผ ์ง์ ‘ ๋ฐ›์œผ๋ฉด์„œ, Styled Components๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์Šคํ† ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด๋ณด์ž.
์šฐ์„ , Button.js๋ผ๋Š” ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

Button.stories.js๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ด ์ค€๋‹ค.

// ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
import Button from "./Button";

export default {
    title: "Practice/Button",
    component: Button,

		// ์ด๋ฒˆ์— ์ž‘์„ฑํ•œ ์ „๋‹ฌ์ธ์ž์˜ ํƒ€์ž…์€ Storybook์„ ๋ณด๊ณ  ์ง์ ‘ ํ™•์ธํ•ด๋ณด์„ธ์š”.
    argTypes: {
        color: { control: 'color'},
        size: { control: { type:'radio'}, options : ['big', 'small'] },
        text: { control: 'text'}
    }
};

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

์ด๋ฒˆ์— ๋งŒ๋“  ์Šคํ† ๋ฆฌ์—์„œ๋Š” control: โ€˜colorโ€™์™€ control: { type: โ€˜radioโ€™, options: [] }๊ฐ€
๊ฐ๊ฐ ์–ด๋–ค ํ˜•ํƒœ๋กœ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•ด ์ฃผ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๋ฉด ๋œ๋‹ค.



useRef


React๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ, React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ๋•Œ DOM์„ ์ง์ ‘ ์กฐ์ž‘ํ•˜๋Š” ๊ฒƒ์€ ์ง€์–‘ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๋ฐฐ์› ๋‹ค.
ํ•˜์ง€๋งŒ, ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด DOM์„ ์ง์ ‘ ๊ฑด๋“œ๋ ค์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ useRef๋ผ๋Š” Hook ํ•จ์ˆ˜์ด๋‹ค.

์ง€๊ธˆ๊นŒ์ง€๋Š” React๋งŒ ๊ฐ€์ง€๊ณ  ๊ฑฐ์˜ ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋ก ํŠธ์—”๋“œ ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
ํ•˜์ง€๋งŒ React๋กœ ๋ชจ๋“  ๊ฐœ๋ฐœ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•  ์ˆ˜๋Š” ์—†๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด DOM ์—˜๋ฆฌ๋จผํŠธ์˜ ์ฃผ์†Œ๊ฐ’์„ ํ™œ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํŠนํžˆ ๊ทธ๋ ‡๋‹ค.

  • focus
  • text selection
  • media playback
  • ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
  • d3.js, greensock ๋“ฑ DOM ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ

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

const ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡ = useRef(์ฐธ์กฐ์ž๋ฃŒํ˜•)
// ์ด์ œ ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡ ๋ณ€์ˆ˜์— ์–ด๋–ค ์ฃผ์†Œ๊ฐ’์ด๋“  ๋‹ด์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
return (
    <div>
      <input ref={์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡} type="text" />
        {/* React์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ref๋ผ๋Š” ์†์„ฑ์— ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡์„ ๊ฐ’์œผ๋กœ ํ• ๋‹นํ•˜๋ฉด*/}
        {/* ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡ ๋ณ€์ˆ˜์—๋Š” input DOM ์—˜๋ฆฌ๋จผํŠธ์˜ ์ฃผ์†Œ๊ฐ€ ๋‹ด๊น๋‹ˆ๋‹ค. */}
        {/* ํ–ฅํ›„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ input DOM ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. */}
    </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์˜ ํŠน์ง•์ด์ž ์žฅ์ ์ธ ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™๊ณผ ๋ฐฐ์น˜๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์กฐ์‹ฌํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

profile
์•„์ด๋””์–ด๊ฐ€ ๋„˜์น˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๊ฟˆ๊ฟ‰๋‹ˆ๋‹ค ๐Ÿ”ฅ

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