Skip to content

๐Ÿง˜ ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ƒ๊ฐํ•  ๊ฒƒ๋“ค ๐Ÿง˜ mithi/react-philosophies ๋ฒˆ์—ญ๋ณธ

Notifications You must be signed in to change notification settings

lim-jiwoo/react-philosophies-ko

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 

Repository files navigation

react-philosophies-ko

๐Ÿง˜ ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ƒ๊ฐํ•  ๊ฒƒ๋“ค ๐Ÿง˜ mithi/react-philosophies ์˜ ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ๋ณธ (๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ: 2023.04.08)

๋ชฉ์ฐจ

  1. ์†Œ๊ฐœ๊ธ€
  2. ์ตœ์†Œํ•œ์˜ ์›์น™
  3. ํ–‰๋ณต์„ ์œ„ํ•œ ๋””์ž์ธ
  4. ์„ฑ๋Šฅ ํŒ
  5. ํ…Œ์ŠคํŒ… ์›์น™
  6. ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ๊ณต์œ ํ•œ ์ธ์‚ฌ์ดํŠธ

๐Ÿง˜ 0. ์†Œ๊ฐœ๊ธ€

react-philosophies ๋Š”:

  • React ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ œ๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ๋“ค์ž…๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด๋‚˜ ์ œ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•  ๋•Œ ํ•ญ์ƒ ์—ผ๋‘์— ๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ€์ด๋“œ๋ผ์ธ์ผ ๋ฟ์ด๋ฉฐ, ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ฅผ ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค.
  • ์ง€์†์ ์œผ๋กœ ๊ฐœ์ •๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์„œ๋กœ, ์ œ ๊ฒฝํ—˜์ด ์Œ“์ผ์ˆ˜๋ก ๋ฐœ์ „ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • ๋Œ€๋ถ€๋ถ„ ๊ธฐ๋ณธ์ ์ธ ๋ฆฌํŒฉํ† ๋ง ๋ฐฉ๋ฒ•, SOLID ์›์น™, ๊ทธ๋ฆฌ๊ณ  ์ต์ŠคํŠธ๋ฆผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์•„์ด๋””์–ด์˜ ๋ณ€ํ˜• ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค... ๊ทธ์ € React์— ๋งž์ถฐ ์ ์šฉํ•œ ๊ฒƒ๋ฟ์ด์ฃ  ๐Ÿ™‚

react-philosophies ๋Š” ์ œ ์ฝ”๋”ฉ ์—ฌ์ • ์ค‘ ๋ฐœ๊ฒฌํ•œ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์˜๊ฐ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๊ทธ์ค‘ ๋ช‡ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค:

๐Ÿง˜ 1. ์ตœ์†Œํ•œ์˜ ์›์น™

1.1 ์ปดํ“จํ„ฐ๊ฐ€ ๋‚˜๋ณด๋‹ค ๋˜‘๋˜‘ํ•œ ๊ฒฝ์šฐ๋ฅผ ์ธ์‹ํ•˜์ž

  1. ESLint๋กœ ์ฝ”๋“œ๋ฅผ ์ •์  ๋ถ„์„ํ•˜์ž. rule-of-hooks ๋ฐ exhaustive-deps ๊ทœ์น™์„ ํ™œ์„ฑํ™”ํ•˜์—ฌ React ํŠน์ • ์˜ค๋ฅ˜๋ฅผ ์žก์„ ์ˆ˜ ์žˆ๋‹ค.
  2. "strict" ๋ชจ๋“œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜์ž. 2023๋…„์ด๋‹ˆ๊นŒ.
  3. ๋ชจ๋“  ์˜์กด์„ฑ์„ ์†”์งํ•˜๊ฒŒ ๋ช…์‹œํ•˜์ž. useMemo, useCallback ๋ฐ useEffect ์—์„œ ๋œจ๋Š” exhaustive-deps ๊ฒฝ๊ณ /์˜ค๋ฅ˜๋ฅผ ๊ณ ์น˜์ž. ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ์—†์ด ์ฝœ๋ฐฑ์„ ํ•ญ์ƒ ์ตœ์‹ ์œผ๋กœ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด "์ตœ์‹  ref ํŒจํ„ด" ์„ ์‚ฌ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  4. ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด map์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐ˜๋“œ์‹œ ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•˜์ž.
  5. ํ›…์€ ํ•ญ์ƒ ์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ์—์„œ๋งŒ ํ˜ธ์ถœํ•˜์ž. ๋ฐ˜๋ณต๋ฌธ์ด๋‚˜ ์กฐ๊ฑด๋ฌธ, ์ค‘์ฒฉ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ํ›…์„ ํ˜ธ์ถœํ•˜์ง€ ๋ง์ž.
  6. "Can't perform state update on unmounted component" ๊ฒฝ๊ณ ๋ฅผ ์ดํ•ดํ•˜์ž. facebook/react/pull/22114
  7. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์—ฌ๋Ÿฌ ๋ ˆ๋ฒจ์— error boundaries ๋ฅผ ์ถ”๊ฐ€ํ•ด "์ฃฝ์Œ์˜ ํ•˜์–€ ํ™”๋ฉด(white screen of death)" ๋ฅผ ๋ฐฉ์ง€ํ•˜์ž. ๋˜ํ•œ ์›ํ•œ๋‹ค๋ฉด ์ด๋ฅผ ์‚ฌ์šฉํ•ด Sentry ๊ฐ™์€ ์˜ค๋ฅ˜ ๋ชจ๋‹ˆํ„ฐ๋ง ์„œ๋น„์Šค์— ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ์ˆ˜๋„ ์žˆ๋‹ค. (React ์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ)
  8. ์ฝ˜์†”์— ์˜ค๋ฅ˜๋‚˜ ๊ฒฝ๊ณ ๊ฐ€ ๋œจ๋Š” ๋ฐ๋Š” ๋‹ค ์ด์œ ๊ฐ€ ์žˆ๋‹ค.
  9. tree-shaking์„ ๊ธฐ์–ตํ•˜์ž!
  10. Prettier (๋˜๋Š” ๊ทธ ๋Œ€์•ˆ) ์€ ์ฝ”๋“œ ํ˜•์‹์„ ์ž๋™์œผ๋กœ ์ˆ˜์ •ํ•ด ๋งค๋ฒˆ ์ผ๊ด€๋œ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•ด์ค€๋‹ค. ๋” ์ด์ƒ ์ด์— ๋Œ€ํ•ด ์ƒ๊ฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค!
  11. Typescript ๋‚˜ NextJS ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์‚ถ์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค€๋‹ค.
  12. ์˜คํ”ˆ ์†Œ์Šค ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์ด๊ฑฐ๋‚˜, ์—ฌ์œ ๊ฐ€ ์žˆ๋‹ค๋ฉด Code Climate (๋˜๋Š” ๋น„์Šทํ•œ ํˆด) ์„ ๊ฐ•๋ ฅ ์ถ”์ฒœํ•œ๋‹ค. ์ž๋™ ๊ฐ์ง€๋œ ์ฝ”๋“œ ์Šค๋ฉœ(code smell) ์€ ํ˜„์žฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ์ค„์ด๋Š” ๋ฐ ํฐ ๋™๊ธฐ๋ถ€์—ฌ๊ฐ€ ๋œ๋‹ค!

1.2 ์ฝ”๋“œ๋Š” ํ•„์š”์•…์ด๋‹ค

"์ตœ์„ ์˜ ์ฝ”๋“œ๋Š” ์•„๋ฌด ์ฝ”๋“œ๋„ ์ ์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค. ๋‹น์‹ ์ด ๋งŒ๋“  ์ƒˆ๋กœ์šด ํ•œ ์ค„์˜ ์ฝ”๋“œ๋Š” ๊ณง ๋””๋ฒ„๊น…์ด ํ•„์š”ํ•œ ์ฝ”๋“œ, ์ฝ๊ณ  ์ดํ•ดํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ, ์ง€์›ํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค." โ€• Jeff Atwood

"๋‚˜์˜ ๊ฐ€์žฅ ์ƒ์‚ฐ์ ์ธ ๋‚  ์ค‘ ํ•˜๋‚˜๋Š” ์ฒœ ์ค„์˜ ์ฝ”๋“œ๋ฅผ ์‚ญ์ œํ•œ ๋‚ ์ด์—ˆ๋‹ค." โ€• Eric S. Raymond

๋”๋ณด๊ธฐ: Write Less Code - Rich Harris, Code is evil - Artem Sapegin

1.2.1 ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „์— ํ•œ ๋ฒˆ ๋” ์ƒ๊ฐํ•˜์ž

์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ• ์ˆ˜๋ก, ๋” ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๋ธŒ๋ผ์šฐ์ €๋กœ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•œ๋‹ค. ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ›Œ๋ฅญํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ธฐ๋Šฅ์„ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ์ž๋ฌธํ•˜์ž.

๐Ÿ™ˆ ์ •๋ง ํ•„์š”ํ•œ๊ฐ€? ๋ถˆํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ๋Š” ์˜์กด์„ฑ/์ฝ”๋“œ ์˜ˆ์‹œ
  1. Redux ๊ฐ€ ์ •๋ง ํ•„์š”ํ• ๊นŒ? ๊ทธ๋Ÿด ์ˆ˜๋„ ์žˆ์ง€๋งŒ, React ์ž์ฒด๊ฐ€ ์ด๋ฏธ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž„์„ ์žŠ์ง€ ๋ง์ž.

  2. Apollo client ๊ฐ€ ์ •๋ง ํ•„์š”ํ• ๊นŒ? Apollo client ์—๋Š” ์ˆ˜๋™ ์ •๊ทœํ™”์™€ ๊ฐ™์€ ๋ฉ‹์ง„ ๊ธฐ๋Šฅ์ด ๋งŽ์ง€๋งŒ, ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ๋งŽ์ด ์ฆ๊ฐ€์‹œํ‚จ๋‹ค. Apollo client ์—๋งŒ ์กด์žฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด, react-query ๋‚˜ SWR ๊ฐ™์ด ๋” ์ž‘์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ณ ๋ คํ•ด๋ณด์ž (ํ˜น์€ ์•„์˜ˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ๋„ ๋ฐฉ๋ฒ•์ด๋‹ค).

  3. Axios ๋Š” ์–ด๋–จ๊นŒ? Axios ๋Š” fetch ๋‚ด์žฅํ•จ์ˆ˜๋กœ๋Š” ์‰ฝ๊ฒŒ ๋Œ€์ฒดํ•  ์ˆ˜ ์—†๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฉ‹์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ํ•˜์ง€๋งŒ ๋งŒ์ผ Axios ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์œ ์ผํ•œ ์ด์œ ๊ฐ€ ๋‹จ์ง€ API ๊ฐ€ ๋” ๊น”๋”ํ•˜๊ฒŒ ์ƒ๊ฒผ๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๋ฉด, fetch ๋ฅผ ๋ž˜ํผ๋กœ ๊ฐ์‹ธ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์ž (redaxios ๋‚˜ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์„œ). ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด Axios ์˜ ์ตœ๊ณ ์˜ ๊ธฐ๋Šฅ๋“ค์„ ์ •๋ง๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ์ƒ๊ฐํ•ด๋ณด์ž.

  4. Decimal.js ๋Š”? Big.js ๋‚˜ ๋‹ค๋ฅธ ์ž‘์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ ์ถฉ๋ถ„ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

  5. Lodash/underscoreJS ๋Š”? you-dont-need/You-Dont-Need-Lodash-Underscore

  6. MomentJS ๋Š”? you-dont-need/You-Dont-Need-Momentjs

  7. ํ…Œ๋งˆ(๋ผ์ดํŠธ/๋‹คํฌ ๋ชจ๋“œ)๋ฅผ ์œ„ํ•ด Context ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๋Œ€์‹  css ๋ณ€์ˆ˜ ์‚ฌ์šฉ์„ ๊ณ ๋ คํ•ด๋ณด์ž.

  8. ์‹ฌ์ง€์–ด Javascript ๋งˆ์ € ํ•„์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋‹ค. CSS ๋Š” ๊ฐ•๋ ฅํ•˜๋‹ค. you-dont-need/You-Dont-Need-JavaScript


1.2.2 ๋˜‘๋˜‘ํ•˜๋ ค ํ•˜์ง€ ๋ง์ž. YAGNI!

"๋ฏธ๋ž˜์— ๋‚ด ์†Œํ”„ํŠธ์›จ์–ด์— ์–ด๋–ค ์ผ์ด ์ƒ๊ธธ๊นŒ? ์•„, ์ด๋Ÿฐ ์ผ์ด๋‚˜ ์ €๋Ÿฐ ์ผ์ด ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ๊ฒ ๊ตฐ. ์–ด์ฐจํ”ผ ์ด ๋ถ€๋ถ„์„ ์ž‘์—…ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๋ฏธ๋ฆฌ ๋‹ค ๊ตฌํ˜„ํ•ด๋ฒ„๋ฆฌ์ž. ๋ฏธ๋ž˜๋ฅผ ๋Œ€๋น„ํ•ด์„œ."

ํ•„์š” ์—†์„ ๊ฑฐ๋‹ค (You Aren't Gonna Need It)! ์–ธ์ œ๋‚˜ ์‹ค์ œ๋กœ ํ•„์š”ํ•  ๋•Œ ๊ตฌํ˜„ํ•˜๊ณ , ๋ฏธ๋ฆฌ ์˜ˆ์ƒํ•ด์„œ ๊ตฌํ˜„ํ•˜์ง€ ๋ง์ž. ์ฝ”๋“œ๋Š” ์ ์„ ์ˆ˜๋ก ์ข‹๋‹ค! (Martin Fowler: YAGNI, C2 Wiki: You Arent Gonna Need It!)

๊ด€๋ จ ์„น์…˜: 2.4 ์ค‘๋ณต์€ ์ž˜๋ชป๋œ ์ถ”์ƒํ™”๋ณด๋‹ค ํ›จ์”ฌ ์ €๋ ดํ•˜๋‹ค

1.3 ํ•œ๋ฒˆ ๋ฐฉ๋ฌธํ•œ ์ฝ”๋“œ๋Š” ๋” ๋‚˜์€ ์ƒํƒœ๋กœ ๋– ๋‚˜๋ผ

1.3.1 ์ฝ”๋“œ ์Šค๋ฉœ์„ ๊ฐ์ง€ํ•˜๊ณ  ํ•„์š”ํ•˜๋ฉด ์กฐ์น˜๋ฅผ ์ทจํ•˜์ž

๋ญ”๊ฐ€ ์ž˜๋ชป๋œ ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค๋ฉด, ์ฆ‰์‹œ ๊ณ ์น˜์ž. ๋งŒ์•ฝ ์‰ฝ๊ฒŒ ๊ณ ์น  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ๋‹น์žฅ ๊ณ ์น  ์‹œ๊ฐ„์ด ์—†๋‹ค๋ฉด, ์ ์–ด๋„ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ฐ„๊ฒฐํ•œ ์„ค๋ช…์„ ๋‹ด์€ ์ฃผ์„์ด๋ผ๋„ ๋‹ฌ์ž (FIXME ๋˜๋Š” TODO). ๋‹ค๋ฅธ ๋ชจ๋‘๊ฐ€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž. ๋‹น์‹ ์ด ์‹ ๊ฒฝ์„ ์“ฐ๊ณ  ์žˆ๋‹ค๋Š” ๊ฑธ ์•Œ๋ฆด ๋ฟ ์•„๋‹ˆ๋ผ, ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๋„ ์ด์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์„ ๋•Œ ๋˜‘๊ฐ™์ด ํ•˜๋„๋ก ๋…๋ คํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ™ˆ ์‰ฝ๊ฒŒ ์žก์„ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ์Šค๋ฉœ ์˜ˆ์‹œ
  • โŒ ๋งค์šฐ ๋งŽ์€ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š” ๋ฉ”์„œ๋“œ ๋˜๋Š” ํ•จ์ˆ˜
  • โŒ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด boolean ๋กœ์ง
  • โŒ ํ•œ ํŒŒ์ผ ๋‚ด์— ๋„ˆ๋ฌด ๋งŽ์€ ์ค„์˜ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ
  • โŒ (์„œ์‹์ด ๋‹ค๋ฅด๋”๋ผ๋„) ๊ตฌ๋ฌธ์ƒ(syntactically) ๋™์ผํ•œ ์ค‘๋ณต๋˜๋Š” ์ฝ”๋“œ
  • โŒ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ํ•จ์ˆ˜ ๋˜๋Š” ๋ฉ”์„œ๋“œ
  • โŒ ๋งŽ์€ ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ํด๋ž˜์Šค/์ปดํฌ๋„ŒํŠธ
  • โŒ ๋‹จ์ผ ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ ๋‚ด์— ๋„ˆ๋ฌด ๋งŽ์€ ์ค„์˜ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ
  • โŒ ๋ฐ˜ํ™˜๋ฌธ์ด ๋งŽ์€ ํ•จ์ˆ˜ ๋˜๋Š” ๋ฉ”์„œ๋“œ
  • โŒ ์ผ์น˜ํ•˜์ง„ ์•Š์ง€๋งŒ ๊ตฌ์กฐ๊ฐ€ ๋™์ผํ•œ ์ค‘๋ณต๋˜๋Š” ์ฝ”๋“œ (์˜ˆ: ๋ณ€์ˆ˜๋ช…์ด ๋‹ค๋ฆ„)

์ฝ”๋“œ ์Šค๋ฉœ์€ ๋ฐ˜๋“œ์‹œ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•˜์ง„ ์•Š๋Š”๋‹ค. ๊ทธ์ € ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์Œ์„ ์•Œ๋ ค์ค„ ๋ฟ์ด๋‹ค

1.3.2 ๊ฐ€์ฐจ์—†์ด ๋ฆฌํŒฉํ„ฐ๋งํ•˜๋ผ. ๋‹จ์ˆœํ•จ์ด ๋ณต์žกํ•จ๋ณด๋‹ค ๋‚ซ๋‹ค

CL (์—ญ: ๊ตฌ๊ธ€์—์„œ PR์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์šฉ์–ด) ์ด ํ•„์š” ์ด์ƒ์œผ๋กœ ๋ณต์žกํ•œ๊ฐ€? CL ์˜ ๋ชจ๋“  ๋ ˆ๋ฒจ์—์„œ ๋‹ค์Œ์„ ํ™•์ธํ•˜๋ผ โ€” ๊ฐ ์ค„์ด ๋„ˆ๋ฌด ๋ณต์žกํ•œ๊ฐ€? ํ•จ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•œ๊ฐ€? ํด๋ž˜์Šค๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•œ๊ฐ€? "๋„ˆ๋ฌด ๋ณต์žกํ•˜๋‹ค"๋ผ๋Š” ๊ฑด ์ผ๋ฐ˜์ ์œผ๋กœ "์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ์‚ฌ๋žŒ์ด ๋นจ๋ฆฌ ์ดํ•ดํ•  ์ˆ˜ ์—†๋‹ค"๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๋˜๋Š” "๊ฐœ๋ฐœ์ž๊ฐ€ ์ด ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋ ค ํ•  ๋•Œ ๋ฒ„๊ทธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ํ™•๋ฅ ์ด ๋†’๋‹ค"๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๊ธฐ๋„ ํ•œ๋‹ค. โ€• Google Engineering Practices: What to look for in a code review

๐Ÿ’โ€โ™€๏ธ TIP: ๋ณต์žกํ•œ ์กฐ๊ฑด๋ฌธ ์„ ๋‹จ์ˆœํ™”ํ•˜๊ณ , ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๋นจ๋ฆฌ ๋ฆฌํ„ด(early return)ํ•˜์ž

๐Ÿ™ˆ ๋น ๋ฅธ ๋ฆฌํ„ด ์˜ˆ์‹œ
# โŒ Not-so-good

if (loading) {
  return <LoadingScreen />
} else if (error) {
  return <ErrorScreen />
} else if (data) {
  return <DataScreen />
} else {
  throw new Error('This should be impossible')
}

# โœ… BETTER

if (loading) {
  return <LoadingScreen />
} 

if (error) {
  return <ErrorScreen />
}

if (data) {
  return <DataScreen />
}

throw new Error('This should be impossible')

๐Ÿ’โ€โ™€๏ธ TIP: ๋ฐ˜๋ณต๋ฌธ๋ณด๋‹ค ์—ฐ์‡„์ ์ธ ๊ณ ์ฐจ ํ•จ์ˆ˜(chained HOF)๋ฅผ ์„ ํ˜ธํ•˜์ž

๋šœ๋ ทํ•œ ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ์—†๊ณ  ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, ์ „ํ†ต์ ์ธ ๋ฐ˜๋ณต๋ฌธ์„ ์—ฐ์‡„๋œ ๊ณ ์ฐจ ํ•จ์ˆ˜(map, filter, find, findIndex, some ๋“ฑ)๋กœ ๋Œ€์ฒดํ•˜์ž. Stackoverflow: ๋ฐ˜๋ณต๋ฌธ ๋Œ€์‹  ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์žฅ์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

1.4 ๋” ์ž˜ํ•  ์ˆ˜ ์žˆ๋‹ค

๐Ÿ’โ€โ™€๏ธ TIP: ์ƒํƒœ(state)๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ๋„ฃ์–ด์ค„ ํ•„์š”๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๋Œ€์‹  ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค

useEffect ๋‚˜ useCallback ํ›…์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์— (useState ์˜) setState ๋‚˜ (useReducer ์˜) dispatch ๋Š” ๋„ฃ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. React ๊ฐ€ ์ด๋“ค์˜ ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ESLint ๋Š” ๋ถˆํ‰ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.

๐Ÿ™ˆ ์˜ˆ์‹œ
โŒ Not-so-good
const decrement = useCallback(() => setCount(count - 1), [setCount, count])
const decrement = useCallback(() => setCount(count - 1), [count])

โœ… BETTER
const decrement = useCallback(() => setCount(count => (count - 1)), [])

๐Ÿ’โ€โ™€๏ธ TIP: ๋งŒ์•ฝ useMemo ๋‚˜ useCallback ์— ์˜์กด์„ฑ์ด ์—†๋‹ค๋ฉด, ๋ญ”๊ฐ€ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ผ ์ˆ˜ ์žˆ๋‹ค

๐Ÿ™ˆ ์˜ˆ์‹œ
โŒ Not-so-good
const MyComponent = () => {
   const functionToCall = useCallback(x: string => `Hello ${x}!`,[])
   const iAmAConstant = useMemo(() => { return {x: 5, y: 2} }, [])
   /* ์—ฌ๊ธฐ์„œ functionToCall ์™€ iAmAConstant ์‚ฌ์šฉ */
}
       
โœ… BETTER 
const I_AM_A_CONSTANT =  { x: 5, y: 2 }
const functionToCall = (x: string) => `Hello ${x}!`
const MyComponent = () => {
   /* ์—ฌ๊ธฐ์„œ functionToCall ์™€ iAmAConstant ์‚ฌ์šฉ */
}

๐Ÿ’โ€โ™€๏ธ TIP: ์ปค์Šคํ…€ ์ปจํ…์ŠคํŠธ๋ฅผ ํ›…์œผ๋กœ ๊ฐ์‹ธ๋ฉด ๋” ๋ณด๊ธฐ ์ข‹์€ API ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค

๋” ๋ณด๊ธฐ ์ข‹์„ ๋ฟ ์•„๋‹ˆ๋ผ, ์ด์ œ ๋‘ ๊ฐœ๊ฐ€ ์•„๋‹Œ ํ•˜๋‚˜๋งŒ import ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ™ˆ ์˜ˆ์‹œ
โŒ Not-so-good
// ๋งค๋ฒˆ ๋‘ ๊ฐœ๋ฅผ ๋ชจ๋‘ import ํ•ด์•ผ ํ•œ๋‹ค
import { useContext } from "react"
import { SomethingContext } from "some-context-package"

function App() {
  const something = useContext(SomethingContext) // ๋‚˜์˜์ง€ ์•Š์ง€๋งŒ, ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค
}
โœ…  Better  
// ํŒŒ์ผ ํ•˜๋‚˜์— ๋‹ค์Œ ํ›…์„ ์„ ์–ธํ•œ๋‹ค
function useSomething() {
  const context = useContext(SomethingContext)
  if (context === undefined) {
    throw new Error('useSomething must be used within a SomethingProvider')
  }
  return context
}
  
// ๋งค๋ฒˆ ํ•˜๋‚˜๋งŒ import ํ•˜๋ฉด ๋œ๋‹ค
import { useSomething } from "some-context-package"

function App() {
  const something = useSomething() // ๋” ๋ณด๊ธฐ ์ข‹๋‹ค
}  

๐Ÿ’โ€โ™€๏ธ TIP: ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฝ”๋”ฉํ•˜๊ธฐ ์ „์— ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋ ์ง€ ์ƒ๊ฐํ•˜์ž

API ์ž‘์„ฑ์€ ์–ด๋ ต๋‹ค. ๋” ๋‚˜์€ API ๋ฅผ ๋””์ž์ธํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ์œ ์šฉํ•œ ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” README Driven Development ์ด๋‹ค. RDD ๋ฅผ ๋งน๋ชฉ์ ์œผ๋กœ ๋”ฐ๋ฅด์ž๋Š” ์˜๋ฏธ๋Š” ์•„๋‹ˆ์ง€๋งŒ, ์•„์ด๋””์–ด๋Š” ํ›Œ๋ฅญํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ๊ฒฝํ—˜์ƒ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์— API (์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋ ์ง€) ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋ฉด, ๋Œ€๋ถ€๋ถ„ ๊ทธ๋ ‡์ง€ ์•Š์•˜์„ ๋•Œ๋ณด๋‹ค ๋” ์ž˜ ๋””์ž์ธ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŒ๋“ค์–ด์ง€๊ณค ํ–ˆ๋‹ค.

๐Ÿง˜ 2. ํ–‰๋ณต์„ ์œ„ํ•œ ๋””์ž์ธ

"์ปดํ“จํ„ฐ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋Š” ๋ฐ”๋ณด๋ผ๋„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ข‹์€ ๊ฐœ๋ฐœ์ž๋Š” ์‚ฌ๋žŒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค." โ€• Martin Fowler

"์ฝ๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„ ๋Œ€ ์“ฐ๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์˜ ๋น„์œจ์€ 10 ๋Œ€ 1 ์ด์ƒ์ด๋‹ค. ์šฐ๋ฆฐ ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋ฅผ ์ ๊ธฐ ์œ„ํ•ด ๋Š˜ ์˜›๋‚  ์ฝ”๋“œ๋ฅผ ์ฝ๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋นจ๋ฆฌ ์ผํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด๋ผ." โ€• Robert C. Martin (๊ทธ์˜ ์ •์น˜์  ๊ฒฌํ•ด์— ๋™์˜ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์•„๋‹˜)

TL;DR

  1. ๐Ÿ’– ์ค‘๋ณต๋˜๋Š” ์ƒํƒœ๋ฅผ ์ œ๊ฑฐํ•ด ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ”ผํ•˜์ž.
  2. ๐Ÿ’– ๋ฐ”๋‚˜๋‚˜๋งŒ ์ „๋‹ฌํ•˜์ž. ๋ฐ”๋‚˜๋‚˜๋ฅผ ๋“ค๊ณ  ์žˆ๋Š” ๊ณ ๋ฆด๋ผ์™€ ์ „์ฒด ์ •๊ธ€์„ ๋‹ค ๊ฐ™์ด ์ „๋‹ฌํ•˜์ง€ ๋ง๊ณ . (props ๋กœ ์›์‹œ ๊ฐ’์„ ์ „๋‹ฌํ•˜์ž)
  3. ๐Ÿ’– ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘๊ณ  ๋‹จ์ˆœํ•˜๊ฒŒ ์œ ์ง€ํ•˜์ž - ๋‹จ์ผ ์ฑ…์ž„ ์›์น™!
  4. ๐Ÿ’– ์ค‘๋ณต์€ ์ž˜๋ชป๋œ ์ถ”์ƒํ™”๋ณด๋‹ค ํ›จ์”ฌ ์ €๋ ดํ•˜๋‹ค (๋„ˆ๋ฌด ์ด๋ฅธ / ๋ถ€์ ์ ˆํ•œ ์ผ๋ฐ˜ํ™”๋ฅผ ์ง€์–‘ํ•˜์ž)
  5. ํ•ฉ์„ฑ (composition) ์„ ์‚ฌ์šฉํ•ด ํ”„๋กœํผํ‹ฐ ๋“œ๋ฆด๋ง (prop drilling) ์„ ํ”ผํ•˜์ž (Michael Jackson). Context ๊ฐ€ ๋ชจ๋“  ์ƒํƒœ ๊ณต์œ  ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋‹ต์€ ์•„๋‹ˆ๋‹ค.
    (์—ญ: ๋ฆฌ์•กํŠธ context ์— ๋„ฃ๋Š” ๊ฒƒ์€ prop ์ด์–ด์•ผ ํ•˜๋ฉฐ, Context ๋ฅผ ์ž˜ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ๋Š” ์ž˜ ๋ณด์ง€ ๋ชปํ–ˆ๊ณ , ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋‹Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ์— ๋Œ€ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์˜ณ์•˜๋‹ค๊ณ  ๋งํ•œ๋‹ค)
  6. ๊ฑฐ๋Œ€ํ•œ useEffect ๋ฅผ ์ž‘์€ ๋…๋ฆฝ์ ์ธ useEffect ๋“ค๋กœ ๋‚˜๋ˆ„์ž. (KCD: Myths about useEffect)
  7. ๋กœ์ง์„ ํ›…๊ณผ ํ—ฌํผ ํ•จ์ˆ˜๋กœ ์ถ”์ถœํ•˜์ž.
  8. useCallback, useMemo, useEffect ์˜ ์˜์กด์„ฑ์œผ๋กœ ๊ฐ€๋Šฅํ•œ ํ•œ ์›์‹œ ๊ฐ’๋งŒ ์‚ฌ์šฉํ•˜์ž.
  9. useCallback, useMemo, useEffect ์— ๋„ˆ๋ฌด ๋งŽ์€ ์˜์กด์„ฑ์„ ๋‘์ง€ ๋ง์ž.
  10. ์ƒํƒœ์˜ ์ผ๋ถ€ ๊ฐ’์ด ๋‹ค๋ฅธ ์ƒํƒœ ๊ฐ’ ๋˜๋Š” ์ด์ „ ์ƒํƒœ ๊ฐ’์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ, ๋‹ค์ˆ˜์˜ useState ๋ณด๋‹ค useReducer ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๊ฐ„์†Œํ•ด์ง„๋‹ค.
  11. Context ๋ฅผ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „์—ญ์œผ๋กœ ์‚ฌ์šฉํ•  ํ•„์š”๋Š” ์—†๋‹ค. Context ๋Š” ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์—์„œ ์ตœ๋Œ€ํ•œ ๋‚ฎ์€ ์œ„์น˜์— ๋ฐฐ์น˜ํ•ด๋ผ. ๋ณ€์ˆ˜, ์ฃผ์„, ์ƒํƒœ (๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ƒฅ ์ „๋ฐ˜์ ์œผ๋กœ ์ฝ”๋“œ) ๋ฅผ ์—ฐ๊ด€๋œ/์‚ฌ์šฉ๋˜๋Š” ๊ณณ์— ์ตœ๋Œ€ํ•œ ๊ฐ€๊นŒ์ด ์œ„์น˜์‹œํ‚ค๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๋‹ค.

๐Ÿ’– 2.1 ์ค‘๋ณต๋˜๋Š” ์ƒํƒœ๋ฅผ ์ œ๊ฑฐํ•ด ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ”ผํ•˜์ž

์ค‘๋ณต๋œ ์ƒํƒœ๊ฐ€ ์žˆ์œผ๋ฉด ์ผ๋ถ€ ์ƒํƒœ๊ฐ€ ๋™๊ธฐํ™”๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ๋ณต์žกํ•œ ์ƒํ˜ธ์ž‘์šฉ ์‹œํ€€์Šค๋ฅผ ๋”ฐ๋ผ๊ฐ€๋‹ค ๋ณด๋ฉด ์ƒํƒœ ๊ฐ’ ๊ฐฑ์‹ ์„ ์žŠ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋™๊ธฐํ™” ๋ฒ„๊ทธ๋ฅผ ํ”ผํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„, ์ดํ•ดํ•˜๊ธฐ๋„ ๋” ์‰ฝ๊ณ  ์ฝ”๋“œ ์–‘๋„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. ๋”๋ณด๊ธฐ: KCD: Don't Sync State. Derive It!, Tic-Tac-Toe

๐Ÿ™ˆ ์˜ˆ์‹œ 1
๐Ÿ“๐Ÿ–Š๏ธ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ / ๋ฌธ์ œ ์„ค๋ช… ๋ณด๊ธฐ

๋‹น์‹ ์€ ์ง๊ฐ ์‚ผ๊ฐํ˜•์˜ ๋‹ค์Œ ์†์„ฑ์„ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค.

  • ์„ธ ๋ณ€์˜ ๊ธธ์ด
  • ๋‘˜๋ ˆ
  • ๋„“์ด

์‚ผ๊ฐํ˜•์€ API ๋กœ ๊ฐ€์ ธ์˜จ ๋‘ ์ˆซ์ž {a: number, b: number} ๋กœ ๊ตฌ์„ฑ๋œ ๊ฐ์ฒด์ด๋‹ค. ๋‘ ์ˆซ์ž๋Š” ์ง๊ฐ ์‚ผ๊ฐํ˜•์˜ ์งง์€ ๋‘ ๋ณ€์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.


โŒ ์ข‹์ง€ ์•Š์€ ์†”๋ฃจ์…˜
const TriangleInfo = () => {
  const [triangleInfo, setTriangleInfo] = useState<{a: number, b: number} | null>(null)
  const [hypotenuse, setHypotenuse] = useState<number | null>(null)
  const [perimeter, setPerimeter] = useState<number | null>(null)
  const [areas, setArea] = useState<number | null>(null)

  useEffect(() => {
    fetchTriangle().then(t => setTriangleInfo(t))
  }, [])

  useEffect(() => {
    if(!triangleInfo) {
      return
    }
    
    const { a, b } = triangleInfo
    const h = computeHypotenuse(a, b)
    setHypotenuse(h)
    const newArea = computeArea(a, b)
    setArea(newArea)
    const p = computePerimeter(a, b, h)
    setPerimeter(p)

  }, [triangleInfo])

  if (!triangleInfo) {
    return null
  }

  /*** show info here ****/
}
โœ… ๋” "๋‚˜์€" ์†”๋ฃจ์…˜
const TriangleInfo = () => {
  const [triangleInfo, setTriangleInfo] = useState<{
    a: number;
    b: number;
  } | null>(null)

  useEffect(() => {
    fetchTriangle().then((r) => setTriangleInfo(r))
  }, []);

  if (!triangleInfo) {
    return
  }

  const { a, b } = triangeInfo
  const area = computeArea(a, b)
  const hypotenuse = computeHypotenuse(a, b)
  const perimeter = computePerimeter(a, b, hypotenuse)
 
  /*** show info here ****/
};
๐Ÿ™ˆ ์˜ˆ์‹œ 2
๐Ÿ“๐Ÿ–Š๏ธ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ ์‚ฌํ•ญ / ๋ฌธ์ œ ์„ค๋ช… ๋ณด๊ธฐ

๋‹น์‹ ์€ ๋‹ค์Œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋””์ž์ธํ•ด์•ผ ํ•œ๋‹ค:

  1. ๊ณ ์œ ํ•œ ์ ๋“ค์˜ ๋ชฉ๋ก์„ API ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.
  2. x ๋˜๋Š” y ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„ํŠผ์ด ์žˆ๋‹ค (์˜ค๋ฆ„์ฐจ์ˆœ)
  3. maxDistance ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„ํŠผ์ด ์žˆ๋‹ค (10 ์”ฉ ์ฆ๊ฐ€ํ•˜๋ฉฐ, ์ดˆ๊ธฐ๊ฐ’์€ 100 ์ด๋‹ค)
  4. ์›์  (0, 0) ์—์„œ ํ˜„์žฌ maxDistance ๋ณด๋‹ค ๋” ๋–จ์–ด์ ธ ์žˆ์ง€ ์•Š์€ ์ ๋“ค๋งŒ ํ‘œ์‹œํ•œ๋‹ค
  5. ๋ชฉ๋ก์— 100๊ฐœ์˜ ํ•ญ๋ชฉ๋งŒ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค (๋”ฐ๋ผ์„œ ์ตœ์ ํ™”๋Š” ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค). ๋งŽ์€ ์ˆ˜์˜ ํ•ญ๋ชฉ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” useMemo ๋กœ ์ผ๋ถ€ ๊ณ„์‚ฐ์„ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.

โŒ ์ข‹์ง€ ์•Š์€ ์†”๋ฃจ์…˜
type SortBy = 'x' | 'y'
const toggle = (current: SortBy): SortBy => current === 'x' ? : 'y' : 'x'

const Points = () => {
  const [points, setPoints] = useState<{x: number, y: number}[]>([])
  const [filteredPoints, setFilteredPoints] = useState<{x: number, y: number}[]>([])
  const [sortedPoints, setSortedPoints] = useState<{x: number, y: number}[]>([])
  const [maxDistance, setMaxDistance] = useState<number>(100)
  const [sortBy, setSortBy] = useState<SortBy>('x')
  
  useEffect(() => {
    fetchPoints().then(r => setPoints(r))
  }, [])
  
  useEffect(() => {
    const sorted = sortPoints(points, sortBy)
    setSortedPoints(sorted)
  }, [sortBy, points])

  useEffect(() => {
    const filtered = sortedPoints.filter(p => getDistance(p.x, p.y) < maxDistance)
    setFilteredPoints(filtered)
  }, [sortedPoints, maxDistance])

  const otherSortBy = toggle(sortBy)
  const pointToDisplay = filteredPoints.map(
    p => <li key={`${p.x}|{p.y}`}>({p.x}, {p.y})</li>
  )

  return (
    <>
      <button onClick={() => setSortBy(otherSortBy)}>
        Sort by: {otherSortBy}
      <button>
      <button onClick={() => setMaxDistance(maxDistance + 10)}>
        Increase max distance
      <button>
      Showing only points that are less than {maxDistance} units away from origin (0, 0)
      Currently sorted by: '{sortBy}' (ascending)
      <ol>{pointToDisplay}</ol>
    </>
  )
}
โœ… ๋” "๋‚˜์€" ์†”๋ฃจ์…˜
// NOTE: You can also use useReducer instead
type SortBy = 'x' | 'y'
const toggle = (current: SortBy): SortBy => current === 'x' ? : 'y' : 'x'

const Points = () => {
  const [points, setPoints] = useState<{x: number, y: number}[]>([])
  const [maxDistance, setMaxDistance] = useState<number>(100)
  const [sortBy, setSortBy] = useState<SortBy>('x')

  useEffect(() => {
    fetchPoints().then(r => setPoints(r))
  }, [])
  

  const otherSortBy = toggle(sortBy)
  const filtedPoints = points.filter(p => getDistance(p.x, p.y) < maxDistance)
  const pointToDisplay = sortPoints(filteredPoints, sortBy).map(
    p => <li key={`${p.x}|{p.y}`}>({p.x}, {p.y})</li>
  )

  return (
    <>
      <button onClick={() => setSortBy(otherSortBy)}>
        Sort by: {otherSortBy} <button>
      <button onClick={() => setMaxDistance(maxDistance + 10)}>
        Increase max distance
      <button>
      Showing only points that are less than {maxDistance} units away from origin (0, 0)
      Currently sorted by: '{sortBy}' (ascending)
      <ol>{pointToDisplay}</ol>
    </>
  )
}

๐Ÿ’– 2.2 ๋ฐ”๋‚˜๋‚˜๋งŒ ์ „๋‹ฌํ•˜์ž. ๋ฐ”๋‚˜๋‚˜๋ฅผ ๋“ค๊ณ  ์žˆ๋Š” ๊ณ ๋ฆด๋ผ์™€ ์ „์ฒด ์ •๊ธ€์„ ๋‹ค ๊ฐ™์ด ์ „๋‹ฌํ•˜์ง€ ๋ง๊ณ 

"๋ฐ”๋‚˜๋‚˜๋ฅผ ์›ํ–ˆ๋Š”๋ฐ ๋ฐ”๋‚˜๋‚˜๋ฅผ ๋“ค๊ณ  ์žˆ๋Š” ๊ณ ๋ฆด๋ผ์™€ ์ •๊ธ€ ์ „์ฒด๋ฅผ ์–ป์—ˆ๋‹ค." โ€• Joe Armstrong

์ด๋Ÿฌํ•œ ํ•จ์ •์— ๋น ์ง€์ง€ ์•Š์œผ๋ ค๋ฉด, ๊ฐ€๋Šฅํ•œ ํ•œ ์›์‹œ ๊ฐ’ (boolean, string, number ๋“ฑ) ์„ props ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. (React.memo ๋กœ ์ตœ์ ํ™”ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋„ ์›์‹œ ๊ฐ’์„ ๋„˜๊ธฐ๋Š” ๊ฒŒ ์ข‹๋‹ค)

์ปดํฌ๋„ŒํŠธ๋Š” ๋”ฑ ์ž๊ธฐ ์ผ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์„ ๋งŒํผ๋งŒ ์•Œ๊ณ , ๊ทธ ์ด์ƒ์€ ์•Œ์ง€ ๋ชปํ•ด์•ผ ํ•œ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋Š” ๋˜๋„๋ก ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฌด์—‡์ด๊ณ  ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๋Š” ์ฑ„ ํ˜‘๋ ฅํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‘ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ์˜์กด๋„๊ฐ€ ๋‚ฎ์•„์ง„๋‹ค. ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉํ•œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ, ๊ต์ฒด ๋˜๋Š” ์ œ๊ฑฐํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค. stackoverflow:2832017

๐Ÿ™ˆ Example
๐Ÿ“๐Ÿ–Š๏ธ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ / ๋ฌธ์ œ ์„ค๋ช… ๋ณด๊ธฐ

๋‹น์‹ ์€ ๋‘ ์ปดํฌ๋„ŒํŠธ Summary ์™€ SeeMore ๋ฅผ ํ‘œ์‹œํ•˜๋Š” MemberCard ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. MemberCard ๋Š” id ๋ฅผ prop ์œผ๋กœ ๋ฐ›๋Š”๋‹ค. MemberCard ๋Š” id ๋ฅผ ๋ฐ›์•„ ํ•ด๋‹น Member ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” useMember ํ›…์„ ์‚ฌ์šฉํ•œ๋‹ค.

type Member = {
  id: string
  firstName: string
  lastName: string
  title: string
  imgUrl: string
  webUrl: string
  age: number
  bio: string
  /****** 100 more fields ******/
}

SeeMore ์ปดํฌ๋„ŒํŠธ๋Š” member ์˜ age ์™€ bio ๋ฅผ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค. member ์˜ age ์™€ bio ๋ฅผ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜ ์ˆจ๊ธฐ๋Š” ํ† ๊ธ€ ๋ฒ„ํŠผ์„ ํฌํ•จํ•œ๋‹ค.

Summary ์ปดํฌ๋„ŒํŠธ๋Š” member ์˜ ์‚ฌ์ง„์„ ํ‘œ์‹œํ•œ๋‹ค. ๋˜ํ•œ title, firstName ๋ฐ lastName ์„ ํ‘œ์‹œํ•œ๋‹ค (์˜ˆ: Mr. Vincenzo Cassano). member ์˜ ์ด๋ฆ„์„ ํด๋ฆญํ•˜๋ฉด member ์˜ ๊ฐœ์ธ ์‚ฌ์ดํŠธ๋กœ ์ด๋™ํ•œ๋‹ค. Summary ์ปดํฌ๋„ŒํŠธ์—๋Š” ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๋„ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. (์˜ˆ: ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค ํฐํŠธ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๋ฐ ๋ฐฐ๊ฒฝ์ƒ‰์ด ๋ฌด์ž‘์œ„๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค. ๊ฐ„๋‹จํžˆ "๋žœ๋ค ์Šคํƒ€์ผ๋ง ๊ธฐ๋Šฅ" ์ด๋ผ๊ณ  ๋ถ€๋ฅด์ž)


โŒ ์ข‹์ง€ ์•Š์€ ์†”๋ฃจ์…˜
const Summary = ({ member } : { member: Member }) => {
  /*** include "the random styling feature" ***/
  return (
    <>
      <img src={member.imgUrl} />
      <a href={member.webUrl} >
        {member.title}. {member.firstName} {member.lastName}
      </a>
    </>
  )
}

const SeeMore = ({ member }: { member: Member }) => {
  const [seeMore, setSeeMore] = useState<boolean>(false)
  return (
    <>
      <button onClick={() => setSeeMore(!seeMore)}>
        See {seeMore ? "less" : "more"}
      </button>
      {seeMore && <>AGE: {member.age} | BIO: {member.bio}</>}
    </>
  )
}

const MemberCard = ({ id }: { id: string })) => {
  const member = useMember(id)
  return <><Summary member={member} /><SeeMore member={member} /></>
}
โœ… ๋” "๋‚˜์€" ์†”๋ฃจ์…˜
const Summary = ({ imgUrl, webUrl, header }: { imgUrl: string, webUrl: string, header: string }) => {
  /*** include "the random styling feature" ***/
  return (
    <>
      <img src={imgUrl} />
      <a href={webUrl}>{header}</a>
    </>
  )
}

const SeeMore = ({ componentToShow }: { componentToShow: ReactNode }) => {
  const [seeMore, setSeeMore] = useState<boolean>(false)
  return (
    <>
      <button onClick={() => setSeeMore(!seeMore)}>
        See {seeMore ? "less" : "more"}
      </button>
      {seeMore && <>{componentToShow}</>}
    </>
  )
}


const MemberCard = ({ id }: { id: string }) => {
  const { title, firstName, lastName, webUrl, imgUrl, age, bio } = useMember(id)
  const header = `${title}. ${firstName} ${lastName}`
  return (
    <>
      <Summary {...{ imgUrl, webUrl, header }} />
      <SeeMore componentToShow={<>AGE: {age} | BIO: {bio}</>} />
    </>
  )
}

โœ… ๋” "๋‚˜์€" ์†”๋ฃจ์…˜์—์„œ, SeeMore ์™€ Summary ๋ฅผ Member ์—์„œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๋ฐ์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ๋ชฉํ•˜์ž. CurrentUser, Pet, Post ๋“ฑ ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฐ์ฒด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’– 2.3 ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘๊ณ  ๋‹จ์ˆœํ•˜๊ฒŒ ์œ ์ง€ํ•˜์ž

๋‹จ์ผ ์ฑ…์ž„ ์›์น™์ด๋ž€?

์ปดํฌ๋„ŒํŠธ๋Š” ๋‹จ ํ•˜๋‚˜์˜ ์ผ๋งŒ ํ•ด์•ผ ํ•œ๋‹ค. ๋˜ํ•œ ๊ฐ€๋Šฅํ•œ ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„์˜ ์œ ์˜๋ฏธํ•œ(useful) ์ผ์„ ํ•ด์•ผ ํ•œ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋Š” ๊ทธ ๋ชฉ์ ์„ ์ถฉ์กฑํ•˜๋Š” ์ฑ…์ž„๋งŒ์„ ์ ธ์•ผ ํ•œ๋‹ค.

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

์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹จ์ผ ์ฑ…์ž„์„ ์ง€๊ณ  ์žˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ?

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

์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ, props, ํ›…, ๊ทธ๋ฆฌ๊ณ  ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ๋ณ€์ˆ˜ ๋ฐ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ  (๋„ˆ๋ฌด ๋งŽ์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค) ์ž๋ฌธํ•˜์ž: ์ด๋“ค์ด ์‹ค์ œ๋กœ ์ปดํฌ๋„ŒํŠธ์˜ ๋ชฉ์ ์„ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š”๊ฐ€? ์ผ๋ถ€๊ฐ€ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด, ์ด๋“ค์„ ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์˜ฎ๊ธฐ๊ฑฐ๋‚˜ ํฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋” ์ž‘์€ ๋‹จ์œ„๋กœ ๋ถ„ํ•ดํ•˜๋Š” ๋ฐฉ์•ˆ์„ ๊ณ ๋ คํ•ด๋ณด์ž.

(์œ„ ๋‹จ๋ฝ์€ 2015๋…„์— ์ž‘์„ฑํ•œ ํ•„์ž์˜ ๊ธ€์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•œ๋‹ค: Three things I learned from Sandi Metzโ€™s book as a non-Ruby programmer)

๐Ÿ™ˆ Example
๐Ÿ“๐Ÿ–Š๏ธ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ / ๋ฌธ์ œ ์„ค๋ช… ๋ณด๊ธฐ

๋‹น์‹ ์€ ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์•„์ดํ…œ์„ ๊ตฌ๋งคํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ˆ˜ํ•œ ์ข…๋ฅ˜์˜ ๋ฒ„ํŠผ์„ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๋Š” ๊ฐ€๋ฐฉ, ์˜์ž ๋ฐ ์Œ์‹์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๊ฐ ๋ฒ„ํŠผ์€ ์•„์ดํ…œ์„ ์„ ํƒํ•˜๊ณ  "์ €์žฅ"ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋‹ฌ์„ ์—ฐ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ์—์„œ ์„ ํƒํ•œ ํ•ญ๋ชฉ์„ "์ €์žฅ"ํ•œ ๊ฒฝ์šฐ, ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๋Š” "์˜ˆ์•ฝ"๋œ๋‹ค.
  • ์˜ˆ์•ฝ๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ฒ„ํŠผ์—๋Š” ์ฒดํฌ๋งˆํฌ(โˆจ)๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค.
  • ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์˜ˆ์•ฝ๋œ ์ƒํƒœ๋ผ๋„ ์˜ˆ์•ฝ์„ ์ˆ˜์ •(์•„์ดํ…œ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•จ)ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ ์œ„๋ฅผ hoverํ•˜๋ฉด WavingHand ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•จ๊ป˜ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค.
  • ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ์— ์•„์ดํ…œ์ด ์—†์„ ๋•Œ ๋ฒ„ํŠผ์„ ๋น„ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•œ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋œ ๋ฒ„ํŠผ ์œ„๋ฅผ hoverํ•˜๋ฉด ํˆดํŒ์— "์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅ"์ด๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ผ์•ผ ํ•œ๋‹ค.
  • ์นดํ…Œ๊ณ ๋ฆฌ์— ์•„์ดํ…œ์ด ์—†์œผ๋ฉด ๋ฒ„ํŠผ์˜ ๋ฐฐ๊ฒฝ์€ ํšŒ์ƒ‰์ด์–ด์•ผ ํ•œ๋‹ค.
  • ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์˜ˆ์•ฝ๋œ ์ƒํƒœ๋ผ๋ฉด ๋ฒ„ํŠผ์˜ ๋ฐฐ๊ฒฝ์€ ์ดˆ๋ก์ƒ‰์ด์–ด์•ผ ํ•œ๋‹ค.
  • ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์— ์•„์ดํ…œ์ด ์žˆ๊ณ  ์˜ˆ์•ฝ๋˜์ง€ ์•Š์€ ์ƒํƒœ๋ผ๋ฉด ๋ฒ„ํŠผ์˜ ๋ฐฐ๊ฒฝ์€ ๋นจ๊ฐ„์ƒ‰์ด์–ด์•ผ ํ•œ๋‹ค.
  • ์นดํ…Œ๊ณ ๋ฆฌ์— ํ•ด๋‹นํ•˜๋Š” ๋ฒ„ํŠผ์—๋Š” ๊ณ ์œ ํ•œ ๋ผ๋ฒจ ๋ฐ ์•„์ด์ฝ˜์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.

โŒ ์ข‹์ง€ ์•Š์€ ์†”๋ฃจ์…˜
type ShopCategoryTileProps = {
  isBooked: boolean
  icon: ReactNode
  label: string
  componentInsideModal?: ReactNode
  items?: {name: string, quantity: number}[]
}

const ShopCategoryTile = ({
  icon,
  label,
  items
  componentInsideModal,
}: ShopCategoryTileProps ) => {
  const [openDialog, setOpenDialog] = useState(false)
  const [hover, setHover] = useState(false)
  const disabled = !items || items.length  === 0
  return (
    <>
      <Tooltip title="Not Available" show={disabled}>
        <StyledButton
          className={disabled ? "grey" : isBooked ? "green" : "red" }
          disabled={disabled}
          onClick={() => disabled ? null : setOpenDialog(true) }
          onMouseEnter={() => disabled ? null : setHover(true)}
          onMouseLeave={() => disabled ? null : setHover(false)}
        >
          {icon}
          <StyledLabel>{label}<StyledLabel/>
          {!disabled && isBooked && <FaCheckCircle/>}
          {!disabled && hover && <WavingHand />}
        </StyledButton>
      </Tooltip>
      {componentInsideModal &&
        <Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
          {componentInsideModal}
        </Dialog>
      }
    </>
  )
}
โœ… ๋” "๋‚˜์€" ์†”๋ฃจ์…˜
// split into two smaller components!

const DisabledShopCategoryTile = ({ icon, label }: { icon: ReactNode, label: string }) => {
  return (
    <Tooltip title="Not available">
      <StyledButton disabled={true} className="grey">
        {icon}
        <StyledLabel>{label}<StyledLabel/>
      </Button>
    </Tooltip>
  )
}

type ShopCategoryTileProps = {
  icon: ReactNode
  label: string
  isBooked: boolean
  componentInsideModal: ReactNode
}

const ShopCategoryTile = ({
  icon,
  label,
  isBooked,
  componentInsideModal,
}: ShopCategoryTileProps ) => {
  const [openDialog, setOpenDialog] = useState(false)
  const [hover, setHover] = useState(false)

  return (
    <>
      <StyledButton
        disabled={false}
        className={isBooked ? "green" : "red"}
        onClick={() => setOpenDialog(true) }
        onMouseEnter={() => setHover(true)}
        onMouseLeave={() => setHover(false)}
      >
        {icon}
        <StyledLabel>{label}<StyledLabel/>
        {isBooked && <FaCheckCircle/>}
        {hover && <WavingHand />}
      </StyledButton>
      {openDialog &&
        <Dialog onClose={() => setOpenDialog(false)}>
          {componentInsideModal}
        </Dialog>
      }}
    </>
  )
}

์ฐธ๊ณ : ์œ„ ์˜ˆ์‹œ๋Š” ์‹ค์ œ ํ”„๋กœ๋•์…˜์—์„œ ๋ณธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹จ์ˆœํ™”ํ•œ ๋ฒ„์ „์ด๋‹ค.

โŒ ์ข‹์ง€ ์•Š์€ ์†”๋ฃจ์…˜
const ShopCategoryTile = ({
  item,
  offers,
}: {
  item: ItemMap;
  offers?: Offer;
}) => {
  const dispatch = useDispatch();
  const location = useLocation();
  const history = useHistory();
  const { items } = useContext(OrderingFormContext)
  const [openDialog, setOpenDialog] = useState(false)
  const [hover, setHover] = useState(false)
  const isBooked =
    !item.disabled && !!items?.some((a: Item) => a.itemGroup === item.group)
  const isDisabled = item.disabled || !offers
  const RenderComponent = item.component

  useEffect(() => {
    if (openDialog && !location.pathname.includes("item")) {
      setOpenDialog(false)
    }
  }, [location.pathname]);
  const handleClose = useCallback(() => {
    setOpenDialog(false)
    history.goBack()
  }, [])

  return (
    <GridStyled
      xs={6}
      sm={3}
      md={2}
      item
      booked={isBooked}
      disabled={isDisabled}
    >
      <Tooltip
        title="Not available"
        placement="top"
        disableFocusListener={!isDisabled}
        disableHoverListener={!isDisabled}
        disableTouchListener={!isDisabled}
      >
        <PaperStyled
          disabled={isDisabled}
          elevation={isDisabled ? 0 : hover ? 6 : 2}
        >
          <Wrapper
            onClick={() => {
              if (isDisabled) {
                return;
              }
              dispatch(push(ORDER__PATH));
              setOpenDialog(true);
            }}
            disabled={isDisabled}
            onMouseEnter={() => !isDisabled && setHover(true)}
            onMouseLeave={() => !isDisabled && setHover(false)}
          >
            {item.icon}
            <Typography variant="button">{item.label}</Typography>
            <CheckIconWrapper>
              {isBooked && <FaCheckCircle size="26" />}
            </CheckIconWrapper>
          </Wrapper>
        </PaperStyled>
      </Tooltip>
      <Dialog fullScreen open={openDialog} onClose={handleClose}>
        {RenderComponent && (
          <RenderComponent item={item} offer={offers} onClose={handleClose} />
        )}
      </Dialog>
    </GridStyled>
  )
}

๐Ÿ’– 2.4 ์ค‘๋ณต์€ ์ž˜๋ชป๋œ ์ถ”์ƒํ™”๋ณด๋‹ค ํ›จ์”ฌ ์ €๋ ดํ•˜๋‹ค

๋„ˆ๋ฌด ์ด๋ฅธ/๋ถ€์ ์ ˆํ•œ ์ผ๋ฐ˜ํ™”๋ฅผ ์ง€์–‘ํ•˜์ž. ๊ฐ„๋‹จํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด ํฐ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด, ๋‹ค๋ฅธ ์˜ต์…˜์„ ๊ณ ๋ คํ•˜์ž. ๋‹ค์Œ ๊ธ€์„ ๊ฐ•๋ ฅํžˆ ์ถ”์ฒœํ•œ๋‹ค: Sandi Metz: The Wrong Abstraction.

๋ณต์žกํ•จ์˜ ํ•œ ์œ ํ˜•์€ ์˜ค๋ฒ„ ์—”์ง€๋‹ˆ์–ด๋ง(over-engineering)์ด๋‹ค. ๊ฐœ๋ฐœ์ž๋“ค์ด ์ฝ”๋“œ๋ฅผ ํ•„์š” ์ด์ƒ์œผ๋กœ ์ผ๋ฐ˜ํ™”(generic)ํ•˜๊ฑฐ๋‚˜ ํ˜„์žฌ ์‹œ์Šคํ…œ์—์„œ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ๊ฒฝ์šฐ๋‹ค. ๊ฐœ๋ฐœ์ž๋“ค์ด ๋ฏธ๋ž˜์— ํ•ด๊ฒฐํ•ด์•ผํ• ์ง€๋„ ๋ชจ๋ฅด๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ, ํ˜„์žฌ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋„๋ก ๊ถŒ์žฅํ•˜์ž. ๋ฏธ๋ž˜์˜ ๋ฌธ์ œ๋Š” ์‹ค์ œ๋กœ ๋‹น๋ฉดํ•ด ๋ฌธ์ œ์˜ ํ˜•ํƒœ์™€ ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์„ ๋•Œ ํ•ด๊ฒฐํ•˜๋ฉด ๋œ๋‹ค. โ€• Google Engineering Practices: What to look for in a code review

๋”๋ณด๊ธฐ: KCD: AHA Programming, C2 Wiki: Contrived Interfaces/The Expensive Setup Smell/Premature Generalization

๐Ÿง˜ 3. ์„ฑ๋Šฅ ํŒ

"๋„ˆ๋ฌด ์ด๋ฅธ ์ตœ์ ํ™”๋Š” ๋งŒ์•…์˜ ๊ทผ์›์ด๋‹ค." โ€• Tony Hoare

"ํ•œ ๋ฒˆ์˜ ์ •ํ™•ํ•œ ์ธก์ •์ด ์ฒœ ๊ฐœ์˜ ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ๋ณด๋‹ค ๋‚ซ๋‹ค." โ€• Grace Hopper

TL;DR

  1. ๋Š๋ฆฌ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค๋ฉด, ๋ฒค์น˜๋งˆํฌ(benchmark)๋กœ ์ฆ๋ช…ํ•˜์ž. "๋ถˆํ™•์‹คํ•œ ์ƒํ™ฉ์—์„œ ์ถ”์ธกํ•˜๋ ค๋Š” ์œ ํ˜น์„ ๊ฒฌ๋ŽŒ๋ผ." React Developer Tools (Chrome extension) ์˜ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋‹น์‹ ์˜ ์นœ๊ตฌ๋‹ค!
  2. useMemo ๋Š” ์ฃผ๋กœ ๋น„์‹ผ ๊ณ„์‚ฐ์„ ์œ„ํ•ด์„œ๋งŒ ์‚ฌ์šฉํ•˜์ž.
  3. ๋ฆฌ๋ Œ๋”๋ง์„ ์ค„์ด๊ธฐ ์œ„ํ•ด React.memo, useMemo, useCallback ๋ฅผ ์‚ฌ์šฉํ•  ๊ฑฐ๋ผ๋ฉด ์˜์กด์„ฑ์ด ๋งŽ์ง€ ์•Š์•„์•ผ ํ•˜๋ฉฐ, ๋Œ€๋ถ€๋ถ„ ์›์‹œ ๊ฐ’์ด์–ด์•ผ ํ•œ๋‹ค.
  4. React.memo, useMemo, useCallback ๊ฐ€ ๋‹น์‹ ์ด ์˜๋„ํ–ˆ๋˜ ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์ž. (์ •๋ง ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์žˆ๋Š”๊ฐ€? ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์˜๋ฏธ ์žˆ๋Š” ์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ์ด๋ค„์งˆ ๊ฒƒ์„ ์‹ค์ฆ์ ์œผ๋กœ ์ž…์ฆํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? ๋ฉ”๋ชจ์ด์ œ์ด์…˜์ด ๋•Œ๋กœ๋Š” ์•ฑ ์„ฑ๋Šฅ์„ ๋” ์•…ํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•˜์ž!)
  5. ๋ˆˆ์„ ๊นœ๋นก์ผ ๋•Œ๋งˆ๋‹ค ์Šค์Šค๋กœ ๋•Œ๋ฆฌ๋Š” ์ง“์€ ๋ฉˆ์ถ”์ž. (fix slow renders before fixing rerenders)
    (์—ญ: ๋ˆ„๊ตฐ๊ฐ€ ๋ˆˆ์„ ๊นœ๋นก์ผ ๋•Œ๋งˆ๋‹ค ์Šค์Šค๋กœ๋ฅผ ๋•Œ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ๊ฐ€? ๋•Œ๋ฆฌ์ง€(slow render) ์•Š๊ธฐ ์œ„ํ•ด ๋ˆˆ์„ ๋œ ๊นœ๋นก์ด๋ ค(less re-render) ๋…ธ๋ ฅํ•˜์ง€ ๋ง๊ณ , ๋•Œ๋ฆฌ๋Š” ๊ฑธ ๋ฉˆ์ถ”๊ณ  ๋งˆ์Œ๊ป ๊นœ๋นก์ด์ž. ์ฆ‰, ๋ฆฌ๋ Œ๋”๋ง์„ ์ค„์ด๋Š” ๊ฒƒ๋ณด๋‹ค ๋Š๋ฆฐ ๋ Œ๋”๋ง์„ ์—†์• ๋Š” ๋ฐ ์ง‘์ค‘ํ•˜์ž๋Š” ๋‚ด์šฉ์ด๋‹ค)
  6. ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์— ์ตœ๋Œ€ํ•œ ๊ฐ€๊น๊ฒŒ ๋‘๋ฉด ์ฝ”๋“œ๋ฅผ ์ฝ๊ธฐ ํ›จ์”ฌ ์‰ฌ์›Œ์งˆ ๋ฟ ์•„๋‹ˆ๋ผ ์•ฑ์ด ๋” ๋นจ๋ผ์ง„๋‹ค. (state colocation) (์—ญ: React ์ตœ์ ํ™”ํ•˜๊ธฐ ์ฝ์–ด๋ณด๊ธฐ)
  7. Context ๋Š” ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋‚˜๋ˆ ์•ผ ํ•˜๋ฉฐ, ํ•˜๋‚˜์˜ context provider ์— ๋„ˆ๋ฌด ๋งŽ์€ ๊ฐ’์„ ๋„ฃ์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค. ์ปจํ…์ŠคํŠธ์˜ ์–ด๋–ค ํ•œ ๊ฐ’์ด๋ผ๋„ ๋ฐ”๋€๋‹ค๋ฉด, ์‹ค์ œ๋ก  ๋ฐ”๋€ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„ ํ•ด๋‹น ์ปจํ…์ŠคํŠธ๋ฅผ ์†Œ๋น„ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  8. state ์™€ dispatch ํ•จ์ˆ˜๋ฅผ ๋ถ„๋ฆฌํ•ด context ๋ฅผ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
  9. lazy loading ๊ณผ bundle/code splitting ์„ ์ดํ•ดํ•˜์ž.
  10. ๊ฑฐ๋Œ€ํ•œ ๋ฆฌ์ŠคํŠธ๋Š” ์œˆ๋„์ž‰(window) ํ•˜์ž. (tannerlinsley/react-virtual ๋‚˜ ๋น„์Šทํ•œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์ž)
  11. ๋Œ€๊ฐœ ๋ฒˆ๋“ค ํฌ๊ธฐ๊ฐ€ ์ž‘์„์ˆ˜๋ก ์•ฑ์ด ๋นจ๋ผ์ง„๋‹ค. ์ƒ์„ฑํ•œ ์ฝ”๋“œ ๋ฒˆ๋“ค์„ source-map-explorer ๋˜๋Š” @next/bundle-analyzer (NextJS ์‚ฌ์šฉ์‹œ) ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
  12. ํผ(form) ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ฐœ์ธ์ ์œผ๋กœ react-hook-forms ๋ฅผ ์ถ”์ฒœํ•œ๋‹ค. ์ข‹์€ ์„ฑ๋Šฅ๊ณผ ์ข‹์€ ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜์˜ ๊ท ํ˜•์„ ์ž˜ ๋งž์ท„๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
์„ฑ๋Šฅ์— ๋Œ€ํ•ด ์ฝ์–ด๋ณด๋ฉด ์ข‹์„ KCD ๊ธ€๋“ค

๐Ÿง˜ 4. ํ…Œ์ŠคํŒ… ์›์น™

"ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ผ. ๋„ˆ๋ฌด ๋งŽ์ด๋Š” ๋ง๊ณ . ๋Œ€๋ถ€๋ถ„ ํ†ตํ•ฉ(integration)์œผ๋กœ." โ€• Guillermo Rauch

TL;DR

  1. ํ…Œ์ŠคํŠธ๋Š” ํ•ญ์ƒ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์‹ค์ œ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ์Šต์„ ๋‹ฎ์•„์•ผ ํ•œ๋‹ค.
  2. ๊ตฌํ˜„์ ์ธ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š๋„๋ก ์œ ์˜ํ•˜์ž - ์‚ฌ์šฉ์ž๋Š” ์‚ฌ์šฉํ•˜์ง€๋„, ๋ณด์ง€๋„, ์•Œ์ง€๋„ ๋ชปํ•  ๊ฒƒ๋“ค์ด๋‹ค
  3. ์•„๋ฌด๊ฒƒ๋„ ๋ง๊ฐ€๋œจ๋ฆฌ์ง€ ์•Š์•˜๋‹ค๋Š” ํ™•์‹ ์ด ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋„ ๋“ค์ง€ ์•Š๋Š”๋‹ค๋ฉด, ํ…Œ์ŠคํŠธ์˜ (๋‹จ ํ•˜๋‚˜๋ฟ์ธ) ์†Œ์ž„์„ ๋‹คํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ์ด๋‹ค.
  4. ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•œ ํ›„ ๋™์ผํ•œ ์‚ฌ์šฉ์ž ๋™์ž‘์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ๊ฑฐ์˜ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋ฉด ์˜ฌ๋ฐ”๋ฅธ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๋‹ค.
  5. ํ”„๋ก ํŠธ์—”๋“œ๋Š” 100% ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋‹ฌ์„ฑํ•  ํ•„์š”๋Š” ์—†๋‹ค. 70% ์ •๋„๋ฉด ์ถฉ๋ถ„ํ•  ๊ฒƒ์ด๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ์ƒ์‚ฐ์„ฑ์„ ๋†’์—ฌ์•ผ์ง€, ์ž‘์—…์„ ๋Šฆ์ถฐ์„  ์•ˆ ๋œ๋‹ค. ํ…Œ์ŠคํŠธ ์œ ์ง€ ๊ด€๋ฆฌ๋Š” ์ž‘์—… ์†๋„๋ฅผ ๋Šฆ์ถœ ์ˆ˜ ์žˆ๋‹ค. ํŠน์ • ์‹œ์  ์ดํ›„์—” ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์–ป๋Š” ์ด์ ์ด ๊ธ‰์†๋„๋กœ ์ค„์–ด๋“ค ๊ฒƒ์ด๋‹ค.
  6. ๊ฐœ์ธ์ ์œผ๋กœ Jest, React testing library, Cypress, ๊ทธ๋ฆฌ๊ณ  Mock service worker ๋ฅผ ์ข‹์•„ํ•œ๋‹ค.
ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•ด ์ฝ์–ด๋ณด๋ฉด ์ข‹์„ KCD ๊ธ€๋“ค

๐Ÿง˜ 5. ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ๊ณต์œ ํ•œ ์ธ์‚ฌ์ดํŠธ

If you'd like to share some of the things you think about when you write React code that I didn't touch upon, you can submit a PR and add them to this section. Thanks for taking the time to share your ideas!

๋น„์Šทํ•˜์ง€๋งŒ ๊ฐœ์ธ์ ์œผ๋กœ ๋” ๊ฐ•๋ ฅํ•˜๋‹ค๊ณ  ๋Š๋ผ๋Š” ์›์น™์€ ์ง€๋‚˜์น˜๊ฒŒ ๊ฐ•๋ ฅํ•œ ๊ถŒํ•œ์„ ๊ฐ€์ง„ setters ๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, setUser ํ•จ์ˆ˜ ๋Œ€์‹  updateEmail ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅด๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.

์ปดํฌ๋„ŒํŠธ๋Š” ๋‹จ์ผ ์ฑ…์ž„์„ ์ ธ์•ผ ํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์ถ”์ƒํ™” ์ŠคํŽ™ํŠธ๋Ÿผ์—์„œ ์ž๊ธฐ ์œ„์น˜๊ฐ€ ์–ด๋””์ธ์ง€ ํ™•์‹คํžˆ ํ•ด์•ผ ํ•œ๋‹ค. ํ•œ์ชฝ ๋์—” <Button>, <Heading>, <Modal> ๊ฐ™์€ ์ผ๋ฐ˜์ ์ธ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ๊ณ , ๋‹ค๋ฅธ ์ชฝ ๋์—๋Š” <Homepage> ๋‚˜ <ContactForm> ๊ฐ™์€ ์ผํšŒ์„ฑ ํ…œํ”Œ๋ฆฟ์ด ์žˆ๋‹ค. ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์ด ์ŠคํŽ™ํŠธ๋Ÿผ์—์„œ ๋ช…ํ™•ํ•œ ์œ„์น˜์— ์žˆ์–ด์•ผ ํ•œ๋‹ค. <Button> ์ปดํฌ๋„ŒํŠธ๋Š” user prop ์„ ๊ฐ€์ง€๋ฉด ์•ˆ ๋œ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋” ๋†’์€ ์ถ”์ƒํ™” ๊ฐœ๋…์ด๊ณ , Button ์€ ๋‚ฎ์€ ์ถ”์ƒํ™” ์ปดํฌ๋„ŒํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

(Note, the above is taken from his response to my email... I really appreciate that he took the time to share his ideas ๐Ÿ™‚)

About

๐Ÿง˜ ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ƒ๊ฐํ•  ๊ฒƒ๋“ค ๐Ÿง˜ mithi/react-philosophies ๋ฒˆ์—ญ๋ณธ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published