Skip to content

rlacodud/front_7th_chapter3-2

ย 
ย 

Repository files navigation

๋ฐฐํฌ ๋งํฌ

basic ๋ฐฐํฌ ๊ฒฝ๋กœ advanced ๋ฐฐํฌ ๊ฒฝ๋กœ

๊ณผ์ œ์˜ ํ•ต์‹ฌ์ทจ์ง€

  • React์˜ hook ์ดํ•ดํ•˜๊ธฐ
  • ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•œ ์ดํ•ด
  • ์•ก์…˜๊ณผ ์ˆœ์ˆ˜ํ•จ์ˆ˜์˜ ๋ถ„๋ฆฌ

๊ณผ์ œ์—์„œ ๊ผญ ์•Œ์•„๊ฐ€๊ธธ ๋ฐ”๋ผ๋Š” ์ 

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์ƒํƒœ์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ์ƒํƒœ - cart, isCartFull vs isShowPopup
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํ›… - CartItemView, useCart(), useProduct()
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํ›… - Button, useRoute, useEvent ๋“ฑ
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ํ•จ์ˆ˜์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ํ•จ์ˆ˜ - calculateCartTotal(cart) vs capaitalize(str)

๊ธฐ๋ณธ๊ณผ์ œ

  • Component์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๊ธฐ

  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋งŒ ๋‹ค๋ฃจ๋Š” ๊ณ„์‚ฐ์„ ๋ถ„๋ฆฌํ•˜๊ธฐ

  • ๋ทฐ๋ฐ์ดํ„ฐ์™€ ์—”ํ‹ฐํ‹ฐ๋ฐ์ดํ„ฐ์˜ ๋ถ„๋ฆฌ์— ๋Œ€ํ•œ ์ดํ•ด

  • entities -> features -> UI ๊ณ„์ธต์— ๋Œ€ํ•œ ์ดํ•ด

  • Component์—์„œ ์‚ฌ์šฉ๋˜๋Š” Data๊ฐ€ ์•„๋‹Œ ๋กœ์ง๋“ค์€ hook์œผ๋กœ ์˜ฎ๊ฒจ์กŒ๋‚˜์š”?

  • ์ฃผ์–ด์ง„ hook์˜ ์ฑ…์ž„์— ๋งž๋„๋ก ์ฝ”๋“œ๊ฐ€ ๋ถ„๋ฆฌ๊ฐ€ ๋˜์—ˆ๋‚˜์š”?

  • ๊ณ„์‚ฐํ•จ์ˆ˜๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ์ด ๋˜์—ˆ๋‚˜์š”?

  • Component์—์„œ ์‚ฌ์šฉ๋˜๋Š” Data๊ฐ€ ์•„๋‹Œ ๋กœ์ง๋“ค์€ hook์œผ๋กœ ์˜ฎ๊ฒจ์กŒ๋‚˜์š”?

  • ์ฃผ์–ด์ง„ hook์˜ ์ฑ…์ž„์— ๋งž๋„๋ก ์ฝ”๋“œ๊ฐ€ ๋ถ„๋ฆฌ๊ฐ€ ๋˜์—ˆ๋‚˜์š”?

  • ๊ณ„์‚ฐํ•จ์ˆ˜๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ์ด ๋˜์—ˆ๋‚˜์š”?

  • ํŠน์ • Entitiy๋งŒ ๋‹ค๋ฃจ๋Š” ํ•จ์ˆ˜๋Š” ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‚˜์š”?

  • ํŠน์ • Entitiy๋งŒ ๋‹ค๋ฃจ๋Š” Component์™€ UI๋ฅผ ๋‹ค๋ฃจ๋Š” Component๋Š” ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‚˜์š”?

  • ๋ฐ์ดํ„ฐ ํ๋ฆ„์— ๋งž๋Š” ๊ณ„์ธต๊ตฌ์กฐ๋ฅผ ์ด๋ฃจ๊ณ  ์˜์กด์„ฑ์ด ๋งž๊ฒŒ ์ž‘์„ฑ์ด ๋˜์—ˆ๋‚˜์š”?

์‹ฌํ™”๊ณผ์ œ

  • ์ด๋ฒˆ ์‹ฌํ™”๊ณผ์ œ๋Š” Context๋‚˜ Jotai๋ฅผ ์‚ฌ์šฉํ•ด์„œ Props drilling์„ ์—†์• ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ์–ด๋–ค props๋Š” ๋‚จ๊ฒจ์•ผ ํ•˜๋Š”์ง€, ์–ด๋–ค props๋Š” ์ œ๊ฑฐํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๊ธฐ์ค€์„ ์„ธ์›Œ๋ณด์„ธ์š”.

  • Context๋‚˜ Jotai๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ตํžˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Context๋‚˜ Jotai๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ๊ตฌ์ถ•ํ–ˆ๋‚˜์š”?

  • ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด domain custom hook์„ ์ ์ ˆํ•˜๊ฒŒ ๋ฆฌํŒฉํ† ๋ง ํ–ˆ๋‚˜์š”?

  • ๋„๋ฉ”์ธ ์ปดํฌ๋„ŒํŠธ์— ๋„๋ฉ”์ธ props๋Š” ๋‚จ๊ธฐ๊ณ  props drilling์„ ์œ ๋ฐœํ•˜๋Š” ๋ถˆํ•„์š”ํ•œ props๋Š” ์ž˜ ์ œ๊ฑฐํ–ˆ๋‚˜์š”?

  • ์ „์ฒด์ ์œผ๋กœ ๋ถ„๋ฆฌ์™€ ์žฌ์กฐ๋ฆฝ์ด ๋” ์ˆ˜์›”ํ•ด์ง„ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ง„ ์ฝ”๋“œ๊ฐ€ ๋˜์—ˆ๋‚˜์š”?

๊ณผ์ œ ์…€ํ”„ํšŒ๊ณ 

๊ณผ์ œ๋ฅผ ํ•˜๋ฉด์„œ ๋‚ด๊ฐ€ ์•Œ๊ฒŒ๋œ ์ , ์ข‹์•˜๋˜ ์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

1. ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Functional Programming)

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ์‚ฌ์‹ค์ƒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฒ ํ•™์— ๊ฐ€๊นŒ์šด ๊ฐœ๋…์œผ๋กœ, ํ•œ ๋ฌธ์žฅ์œผ๋กœ ๋งํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋Š” ๋ณ€ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ํ”„๋กœ๊ทธ๋žจ์€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋“ค์˜ ์กฐํ•ฉ์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.

(1) ๋ฐ์ดํ„ฐ๋Š” ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค(Immutability) ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์˜ ๊ฐ’์„ ์ง์ ‘ ๋ฐ”๊พธ๋Š” ๋Œ€์‹ , ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๊ฐ•์กฐํ•ด๋Œ€๋Š” ๋ถˆ๋ณ€์„ฑ์˜ ๊ฐœ๋…์ธ๋ฐ ๊ฒฐ๊ตญ ์ƒํƒœ๊ฐ€ ์–ธ์ œ ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ๋ช…ํ™•ํ•ด์•ผ ๋ Œ๋”๋ง ํ‡ด์ ํ™”๋‚˜ ๋ณ€๊ฒฝ ์ถ”์ ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ•์กฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๐Ÿง ์™œ ๊ทธ๋ž˜์•ผ ํ• ๊นŒ์š”?

  • ๊ฐ’์ด ์ค‘๊ฐ„์— ๋ฐ”๋€Œ์ง€ ์•Š์œผ๋‹ˆ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง€๊ณ 
  • ๋””๋ฒ„๊น…์ด ์‰ฌ์›Œ์ง€๊ณ 
  • ๋™์‹œ์„ฑ ๋ฌธ์ œ(๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ, ๋น„๋™๊ธฐ)์—์„œ ์ถฉ๋Œ์ด ์ค„์–ด๋“ค๊ณ 
  • ์ƒํƒœ๋ฅผ ๋˜๋Œ๋ฆฌ๊ฑฐ๋‚˜ ์‹œ๊ฐ„ ์—ฌํ–‰ ๋””๋ฒ„๊น…(Time-travel debugging)์ด ๊ฐ€๋Šฅํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

(2) ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๊ตฌ์„ฑํ•œ๋‹ค(Pure Function) ์ˆœ์ˆ˜ ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์ผ๊นŒ์š”? ์•„๋ž˜๋งŒ ๊ธฐ์–ตํ•ฉ์‹œ๋‹ค!

  • ๊ฐ™์€ ์ž…๋ ฅ โ†’ ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ
  • ํ•จ์ˆ˜ ์™ธ๋ถ€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Œ
function pureAdd(a, b) {
  return a + b;         // ๊ฐ™์€ ์ž…๋ ฅ โ†’ ๊ฐ™์€ ๊ฒฐ๊ณผ
}

function impureAdd(a, b) {
  console.log("์‹คํ–‰๋จ"); // ์™ธ๋ถ€ ์ƒํƒœ ์‚ฌ์šฉ (์ฝ˜์†”)
  return a + b;
}

๊ทผ๋ฐ ์ €๋Ÿฐ util ํ•จ์ˆ˜ ๋‹จ๊ณ„์ •๋„๋ฉด ์ถฉ์กฑํ•˜๊ธฐ ์‰ฝ์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋“ฑ์˜ ๋ณต์žกํ•œ ๋กœ์ง์—์„œ๋Š” ๊ทธ๊ฒŒ ์–ด๋–ป๊ฒŒ ๊ฐ€๋Šฅํ• ๊นŒ์š”?

FP์—์„œ ๋งํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ”๋Š” ๋ชจ๋“  ๊ฑธ ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๋ผ๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ˆœ์ˆ˜ํ•จ์ˆ˜๋ฅผ ์ตœ๋Œ€ํ™”ํ•˜๊ณ  ๋ถ€์ˆ˜ํšจ๊ณผ๋Š” ์˜๋„๋œ ์œ„์น˜์— ๋ชจ์•„๋‘์–ด ๋ณ€๊ฒฝ ๋ฐ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ดํ•˜๋„๋ก ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

(3) ํ•จ์ˆ˜๋Š” ์กฐํ•ฉ ๊ฐ€๋Šฅํ•œ ์ž‘์€ ๋ธ”๋ก์ด๋‹ค(Composition) ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ํฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ•จ์ˆ˜๋ฅผ ๊ณ„์† ํ•ฉ์„ฑ(์กฐํ•ฉ)ํ•ด ๋‚˜๊ฐ€๋Š” ๋ฐฉ์‹์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.

const double = x => x * 2;
const inc = x => x + 1;

const process = x => double(inc(x));

process(3); // 8

์ด๊ฑธ FP์—์„œ๋Š” ํ•จ์ˆ˜ ํ•ฉ์„ฑ์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ๋ ˆ๊ณ  ๋ธ”๋Ÿญ ์Œ“๋“ฏ์ด ์ž‘์€ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์„œ ์กฐํ•ฉํ•˜๋Š” ๊ฑธ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฆ„์—์„œ๋ถ€ํ„ฐ ๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ์ด๋ ‡๊ฒŒ ํ•ด! ํ•˜๊ณ  ์–ด๋–ป๊ฒŒ(how) ํ• ์ง€๋ฅผ ์„ค๋ช…ํ•œ๋‹ค๋ฉด, ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ํ•จ์ˆ˜๊ฐ€ ์ธ์ž๋ฅผ ๋ฐ›๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋Œ๋ ค์ฃผ๋“ฏ์ด ์–ด๋–ค ๊ฒฐ๊ณผ๋ฅผ ์ฃผ๊ธฐ ์œ„ํ•ด ๋ฌด์—‡(what)์„ ํ• ์ง€๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

// ๋ช…๋ นํ˜•: ์–ด๋–ป๊ฒŒ ๋ฐ˜๋ณตํ• ์ง€ ์•Œ๋ ค์คŒ
let result = [];
for (let n of numbers) {
  result.push(n * 2);
}

// ์„ ์–ธํ˜•: ๋ฌด์—‡์„ ํ• ์ง€ ๋งํ•จ
numbers.map(n => n * 2);

2. ์—”ํ‹ฐํ‹ฐ(Entity)๋ž€?

ํด๋” ๊ตฌ์กฐ๋‚˜ ํŒŒ์ผ๋ช…์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์—”ํ‹ฐํ‹ฐ(Entity)๋ผ๋Š” ์šฉ์–ด๊ฐ€ ์žˆ๋Š”๋ฐ ์ •ํ™•ํžˆ ๋ฌด์Šจ ๋œป์ผ๊นŒ์š”?

**์—”ํ‹ฐํ‹ฐ(Entity)**๋Š” ๋„๋ฉ”์ธ(๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)์˜ ํ•ต์‹ฌ์ด ๋˜๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ์‰ฝ๊ฒŒ ๋งํ•ด ์•ฑ์ด ๋‹ค๋ฃจ๋Š” โ€œ์ง„์งœ ๋Œ€์ƒโ€์„ ๋งํ•ฉ๋‹ˆ๋‹ค.

์ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ Component, hook, function์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์ƒํƒœ์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ์ƒํƒœ - cart, isCartFull vs isShowPopup
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํ›… - CartItemView, useCart(), useProduct()
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํ›… - Button, useRoute, useEvent ๋“ฑ
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ํ•จ์ˆ˜์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ํ•จ์ˆ˜ - calculateCartTotal(cart) vs capaitalize(str)

์™œ Entity๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ถ„๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•˜๋Š”๊ฑธ๊นŒ์š”? ์ง€๊ธˆ ๊ณผ์ œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค.

๊ณผ์ œ๋Š” ์‡ผํ•‘๋ชฐ ํ”„๋กœ์ ํŠธ์ด๊ณ  ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ ๋‹ค๋ค„์ง€๋Š” ๊ฐœ์ฒด(data)์—๋Š” ํฌ๊ฒŒ ์ƒํ’ˆ, ์ฟ ํฐ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋กœ๋Š” ์ƒํ’ˆ ์นด๋“œ, ์ฟ ํฐ ์นด๋“œ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ดํ…œ ์นด๋“œ๊ฐ€ ๋Œ€ํ‘œ์ ์œผ๋กœ ์žˆ๊ฒ ์ฃ .

๋งŒ์•ฝ ๊ทน๋‹จ์ ์œผ๋กœ ์ด ์•„์ดํ…œ ์นด๋“œ๋ฅผ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ Œ๋”ํ•˜๊ฒŒ๋” ํ•œ๋‹ค๋ฉด ํ•œ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋ถ„๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ ๋ Œ๋”ํ•˜๊ฒŒ๋” ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ๋ฌธ์ œ ์—†๋Š” ๊ฑฐ ์•„๋‹Œ๊ฐ€์š”?๐Ÿค” ๋ช‡๊ฐœ์›” ๋’ค ์•„์ดํ…œ ์นด๋“œ์— ์ƒˆ๋กœ์šด ๊ฐœ์ฒด์ธ ๊ฒŒ์‹œ๊ธ€์ด ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ํ•  ๋•Œ ์ปดํฌ๋„ŒํŠธ ๋‚ด์— ๋ถ„๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๊ณ  ์ด๋Š” ๊ณง ๋‹ค๋ฅธ ๋ถ„๊ธฐ์—๋„ ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋Š” ์ž ์žฌ์  ์œ„ํ—˜์ด ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋„ ์šฐ๋ฆฌ๋Š” ๊ฐ ๊ฐœ์ฒด๋ณ„๋กœ ๋‹ค๋ฅธ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๊ฒฉ๋ฆฌ์‹œํ‚ค๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ํ™•์žฅํ•ด๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

3. Jotai

์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ๋Š” Zustand๋ฅผ ์‚ฌ์šฉํ•ด๋ดค๋Š”๋ฐ Jotai๋Š” ์ฒ˜์Œ ์ ‘ํ•ด๋ดค์Šต๋‹ˆ๋‹ค. (Jotai ์กฐํƒ€..) Jotai๋Š” React์™€ ์œ ์‚ฌํ•œ ๋ฌธ๋ฒ• ์ฒด๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ์–ด React์— ํŠนํ™”๋œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

์–ผ๋งˆ๋‚˜ ๋น„์Šทํ•˜๊ธธ๋ž˜..?ํ•˜๊ณ  ๋ณด๋ฉด ์ง„์งœ ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ธ‰์ด๋„ค์š”

// 1. React์˜ useState๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// 2. Jotai์˜ useAtom์„ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ
import { atom, useAtom } from 'jotai';

// ์ปดํฌ๋„ŒํŠธ ๋ฐ–์— atom(์ƒํƒœ์˜ ๋‹จ์œ„)์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค
const countAtom = atom(0);

function Counter() {
  // useState์™€ ์‚ฌ์šฉ๋ฒ•์ด ์™„์ „ํžˆ ๋™์ผํ•ฉ๋‹ˆ๋‹ค!
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

์ด๋งŒํผ ์œ ์‚ฌํ•œ ๋ฌธ๋ฒ• ์ฒด๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด React ํ”„๋กœ์ ํŠธ์—์„œ ์ „์—ญ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ๋„์ž…ํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ๋Ÿฌ๋‹์ปค๋ธŒ๋„ ์ ๊ณ  ์ „ํ™˜ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.


์ด๋ฒˆ ๊ณผ์ œ์—์„œ ๋‚ด๊ฐ€ ์ œ์ผ ์‹ ๊ฒฝ ์“ด ๋ถ€๋ถ„์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ๊ณผ์ œ ์ง„ํ–‰ ๊ณผ์ •

(1) ๊ฐœ๋… ์ •๋ฆฝ ํ•ด๋‹น ์ฑ•ํ„ฐ์—์„œ ๋‹ค๋ฃจ๋Š” ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์กŒ์Šต๋‹ˆ๋‹ค.

(2) ๊ตฌ์กฐ์˜ ์‹œ๊ฐํ™” ๊ทธ๋ฆฌ๊ณ  ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ธฐ๋กœ ๊ฒฐ์‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค.

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

(2-1) ํ…์ŠคํŠธ๋กœ ๊ธฐ๋Šฅ ์ •๋ฆฌ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด์„œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์ •๋ฆฌํ•˜๋ ค ํ•˜๋ฉด ๋ณต์žกํ•  ๊ฒƒ ๊ฐ™์•„์„œ ๋นŒ๋“œํ•œ ํŽ˜์ด์ง€์— ์ง์ ‘ ์•ก์…˜์„ ํ•ด๋ณด๋ฉฐ ๊ธฐ๋Šฅ์„ค๊ณ„์„œ๋ฅผ ์—ญ์œผ๋กœ ์ž‘์„ฑํ–ˆ๊ณ  ํ˜น์‹œ ๋ˆ„๋ฝํ•˜๊ฑฐ๋‚˜ ์ž˜๋ชป ์ž‘์„ฑํ•œ ๋ถ€๋ถ„์ด ์žˆ์„๊นŒ๋ด AI์—๊ฒŒ ๊ฒ€์ฆ์„ ๋งก๊ฒผ์Šต๋‹ˆ๋‹ค.

# ์‡ผํ•‘๋ชฐ
- ์‡ผํ•‘๋ชฐ์— ์กด์žฌํ•˜๋Š” ์ƒํ’ˆ ๋ชฉ๋ก์ด ๋…ธ์ถœ๋œ๋‹ค.
    - ์žฌ๊ณ ๊ฐ€ 5๊ฐœ ์ดํ•˜๋ฉด ํ’ˆ์ ˆ ์ž„๋ฐ• ํ…์ŠคํŠธ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
    - ํ• ์ธ์ด ์žˆ์„ ๊ฒฝ์šฐ ํ•ด๋‹น ํ• ์ธ ์ •๋ณด๋ฅผ ๋…ธ์ถœํ•œ๋‹ค.
    - ์žฌ๊ณ  ์†Œ์ง„ ์‹œ ํ’ˆ์ ˆ ๋ฒ„ํŠผ์œผ๋กœ ๋ณ€๊ฒฝ & ๊ฐ€๊ฒฉ ์˜์—ญ์ด SOLD OUT์œผ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.
- ๊ฒ€์ƒ‰ input ์ž…๋ ฅ โ†’ ํ•ด๋‹น input๊ฐ’์„ ํฌํ•จํ•˜๋Š” ์ƒํ’ˆ๋“ค์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    - ๊ฒ€์ƒ‰์–ด ์ž…๋ ฅ ํ›„ 500ms ์ง€์—ฐ ํ›„ ๊ฒ€์ƒ‰์ด ์‹คํ–‰๋œ๋‹ค. (debounce)
    - ์ƒํ’ˆ๋ช…๊ณผ ์ƒํ’ˆ ์„ค๋ช… ๋ชจ๋‘์—์„œ ๊ฒ€์ƒ‰ํ•œ๋‹ค.
    - ํฌํ•จํ•˜๋Š” ์ƒํ’ˆ์ด ์—†์œผ๋ฉด empty UI๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    - ๋นˆ ๊ฐ’์ด๋ฉด ์ „์ฒด ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
- ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ํ•ด๋‹น ์ƒํ’ˆ์ด ๋‹ด๊ธด๋‹ค.
    - ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
    - ์šฐ์ธก์— ํ•ด๋‹น ์ƒํ’ˆ์ด ์ ์šฉ๋œ๋‹ค.
    - ํ—ค๋”์˜ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ด์ฝ˜ ์˜†์— ์ด ์•„์ดํ…œ ๊ฐœ์ˆ˜๊ฐ€ ๋ฐฐ์ง€๋กœ ํ‘œ์‹œ๋œ๋‹ค.
- ์ˆ˜๋Ÿ‰ input ํด๋ฆญ โ†’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด์„ ์ƒํ’ˆ ์ˆ˜๊ฐ€ 1์”ฉ ์ฆ๊ฐํ•œ๋‹ค.
    - ์ง€์ • ๊ฐœ์ˆ˜ ์ด์ƒ์ด๋ฉด ์ง€์ •ํ•œ ํ• ์ธ์œจ์ด ์ ์šฉ๋œ๋‹ค.
    - ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— 10๊ฐœ ์ด์ƒ์ธ ์ƒํ’ˆ์ด ์žˆ์œผ๋ฉด ์ถ”๊ฐ€ 5% ํ• ์ธ์ด ์ ์šฉ๋œ๋‹ค. (์ตœ๋Œ€ 50%๊นŒ์ง€)
    - ์žฌ๊ณ  ์ด์ƒ์œผ๋กœ ์ฆ๊ฐ€ํ•  ๊ฒฝ์šฐ, ์ˆ˜๋Ÿ‰ ์—๋Ÿฌ ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค.
    - 1์—์„œ 0์œผ๋กœ ๊ฐ์†Œํ•  ๊ฒฝ์šฐ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ํ•ด๋‹น ์ƒํ’ˆ์ด ์ œ๊ฑฐ๋œ๋‹ค.
    - ์žฌ๊ณ  ์†Œ์ง„ ์‹œ ํ’ˆ์ ˆ ๋ฒ„ํŠผ์œผ๋กœ ๋ณ€๊ฒฝ & ๊ฐ€๊ฒฉ ์˜์—ญ์ด SOLD OUT์œผ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.
    - ๊ฐ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ดํ…œ์— ์ ์šฉ๋œ ํ• ์ธ์œจ์ด "-X%" ํ˜•ํƒœ๋กœ ํ‘œ์‹œ๋œ๋‹ค.
- ์ฟ ํฐ select ํด๋ฆญ โ†’ ์„ ํƒํ•œ ๊ฐ’์œผ๋กœ ์ฟ ํฐ ํ• ์ธ์ด ์ ์šฉ๋œ๋‹ค.
    - ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
    - ์ ์šฉ ๋ถˆ๊ฐ€ํ•œ ์ฟ ํฐ์ผ ๊ฒฝ์šฐ, ์—๋Ÿฌ ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (percentage ์ฟ ํฐ์€ 10,000์› ์ด์ƒ ๊ตฌ๋งค ์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
- ๊ฒฐ์ œ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๊ฒฐ์ œ ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค.
    - ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ํ•ด๋‹น ์ƒํ’ˆ์ด ์ œ๊ฑฐ๋œ๋‹ค.
    - ์„ ํƒ๋œ ์ฟ ํฐ์ด ํ•ด์ œ๋œ๋‹ค.
- ์žฅ๋ฐ”๊ตฌ๋‹ˆ X ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ํ•ด๋‹น ์ƒํ’ˆ์ด ์ œ๊ฑฐ๋œ๋‹ค.
- ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€๋กœ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ๋‹ค.
- ๋ฐ์ดํ„ฐ ์ €์žฅ/๋ณต์›
    - ์ƒํ’ˆ, ์ฟ ํฐ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ์ดํ„ฐ๊ฐ€ localStorage์— ์ž๋™์œผ๋กœ ์ €์žฅ๋œ๋‹ค.
    - ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž๋™์œผ๋กœ ๋ณต์›๋œ๋‹ค.

# ๊ด€๋ฆฌ์ž
- ์ƒˆ ์ƒํ’ˆ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒˆ ์ƒํ’ˆ ์ถ”๊ฐ€ form์ด ๋…ธ์ถœ๋œ๋‹ค.
    - ์ƒํ’ˆ๋ช…์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
    - ๊ฐ€๊ฒฉ์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
        - ๋ฌธ์ž์—ด ์ž…๋ ฅ โ†’ ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. (์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅ)
    - ์žฌ๊ณ ๋ฅผ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
        - ๋ฌธ์ž์—ด ์ž…๋ ฅ โ†’ ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. (์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅ)
    - ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒˆ ์ƒํ’ˆ ์ถ”๊ฐ€ form์ด ๋ฏธ๋…ธ์ถœ๋œ๋‹ค.
    - ํ• ์ธ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ• ์ธ ์ถ”๊ฐ€ form์ด ๋…ธ์ถœ๋œ๋‹ค. (๊ธฐ๋ณธ: 10๊ฐœ | 10%)
        - ํ• ์ธ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ• ์ธ ์ถ”๊ฐ€ form์ด ์ถ”๊ฐ€๋œ๋‹ค.
        - ํ• ์ธ ์ˆ˜๋Ÿ‰ input์€ type="number"์ด๋ฉฐ min="1" ์†์„ฑ์ด ์žˆ์–ด 0 ์ดํ•˜ ์ž…๋ ฅ ์‹œ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation์ด ์ž‘๋™ํ•œ๋‹ค.
        - ํ• ์ธ์œจ input์€ type="number"์ด๋ฉฐ max="100" ์†์„ฑ์ด ์žˆ์–ด 100 ์ด์ƒ ์ž…๋ ฅ ์‹œ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation์ด ์ž‘๋™ํ•œ๋‹ค.
        - ๊ฐ ํ• ์ธ ํ•ญ๋ชฉ์˜ X ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ•ด๋‹น ํ• ์ธ ํ•ญ๋ชฉ์ด ์‚ญ์ œ๋œ๋‹ค.
    - ํ•„์ˆ˜๊ฐ’(์ƒํ’ˆ๋ช…, ๊ฐ€๊ฒฉ, ์žฌ๊ณ )์„ ์ž…๋ ฅํ•˜๊ณ  ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒํ’ˆ ๋ชฉ๋ก์— ์ถ”๊ฐ€๋œ๋‹ค.
        - ์ƒˆ ์ƒํ’ˆ ์ถ”๊ฐ€ form์ด ๋ฏธ๋…ธ์ถœ๋œ๋‹ค.
        - ์ƒํ’ˆ ์ถ”๊ฐ€ ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
- ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ•ด๋‹น ์ƒํ’ˆ์ด ์‚ญ์ œ๋œ๋‹ค.
    - ์‚ญ์ œ ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
- ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒํ’ˆ ์ˆ˜์ • form์ด ๋…ธ์ถœ๋œ๋‹ค.
    - ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ˆ˜์ • ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
    - ์ƒํ’ˆ๋ช…์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
    - ๊ฐ€๊ฒฉ์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
        - ๋ฌธ์ž์—ด ์ž…๋ ฅ โ†’ ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. (์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅ)
    - ์žฌ๊ณ ๋ฅผ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
        - ๋ฌธ์ž์—ด ์ž…๋ ฅ โ†’ ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. (์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅ)
    - ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒํ’ˆ ์ˆ˜์ • form์ด ๋ฏธ๋…ธ์ถœ๋œ๋‹ค.
    - ํ• ์ธ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ• ์ธ ์ถ”๊ฐ€ form์ด ๋…ธ์ถœ๋œ๋‹ค. (๊ธฐ๋ณธ: 10๊ฐœ | 10%)
        - ํ• ์ธ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ• ์ธ ์ถ”๊ฐ€ form์ด ์ถ”๊ฐ€๋œ๋‹ค.
        - ํ• ์ธ ์ˆ˜๋Ÿ‰ input์€ type="number"์ด๋ฉฐ min="1" ์†์„ฑ์ด ์žˆ์–ด 0 ์ดํ•˜ ์ž…๋ ฅ ์‹œ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation์ด ์ž‘๋™ํ•œ๋‹ค.
        - ํ• ์ธ์œจ input์€ type="number"์ด๋ฉฐ max="100" ์†์„ฑ์ด ์žˆ์–ด 100 ์ด์ƒ ์ž…๋ ฅ ์‹œ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation์ด ์ž‘๋™ํ•œ๋‹ค.
        - ๊ฐ ํ• ์ธ ํ•ญ๋ชฉ์˜ X ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ•ด๋‹น ํ• ์ธ ํ•ญ๋ชฉ์ด ์‚ญ์ œ๋œ๋‹ค.
    - ํ•„์ˆ˜๊ฐ’(์ƒํ’ˆ๋ช…, ๊ฐ€๊ฒฉ, ์žฌ๊ณ )์„ ์ž…๋ ฅํ•˜๊ณ  ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒํ’ˆ์ด ์ˆ˜์ •๋œ๋‹ค.
        - ์ƒํ’ˆ ์ˆ˜์ • form์ด ๋ฏธ๋…ธ์ถœ๋œ๋‹ค.
        - ์ƒํ’ˆ ์ˆ˜์ • ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
- ์ฟ ํฐ ๊ด€๋ฆฌ ํƒญ ํด๋ฆญ โ†’ ์ฟ ํฐ ๊ด€๋ฆฌ ์ฝ˜ํ…์ธ ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
    - ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ํ•ด๋‹น ์ฟ ํฐ์ด ์‚ญ์ œ๋œ๋‹ค.
        - ์‚ญ์ œ ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
        - ์‚ญ์ œํ•œ ์ฟ ํฐ์ด ํ˜„์žฌ ์„ ํƒ๋œ ์ฟ ํฐ์ด๋ฉด ์ž๋™์œผ๋กœ ํ•ด์ œ๋œ๋‹ค.
    - ์ƒˆ ์ฟ ํฐ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒˆ ์ฟ ํฐ ์ƒ์„ฑ form์ด ๋…ธ์ถœ๋œ๋‹ค.
        - ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ƒˆ ์ฟ ํฐ ์ƒ์„ฑ form์ด ๋ฏธ๋…ธ์ถœ๋œ๋‹ค.
        - ์ฟ ํฐ๋ช…์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ฟ ํฐ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
        - ์ฟ ํฐ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ฟ ํฐ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
            - ์ฟ ํฐ ์ฝ”๋“œ ์ž…๋ ฅ ์‹œ ์ž๋™์œผ๋กœ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€ํ™˜๋œ๋‹ค.
        - ํ• ์ธ๊ธˆ์•ก์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ  ์ฟ ํฐ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ validation ์—๋Ÿฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
        - ํ• ์ธ ํƒ€์ž… select ํด๋ฆญ โ†’ ์„ ํƒํ•œ ๊ฐ’์œผ๋กœ ์ฟ ํฐ ํ• ์ธ ํƒ€์ž…์ด ๋ณ€๊ฒฝ๋œ๋‹ค.
        - ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ฟ ํฐ ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ฟ ํฐ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์—๋Ÿฌ ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค.
        - ํ•„์ˆ˜๊ฐ’(์ฟ ํฐ๋ช…, ์ฟ ํฐ ์ฝ”๋“œ, ํ• ์ธ ๊ธˆ์•ก)์„ ์ž…๋ ฅํ•˜๊ณ  ์ฟ ํฐ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์ฟ ํฐ ๋ชฉ๋ก์— ์ถ”๊ฐ€๋œ๋‹ค.
            - ์ƒˆ ์ฟ ํฐ ์ƒ์„ฑ form์ด ๋ฏธ๋…ธ์ถœ๋œ๋‹ค.
            - ์ฟ ํฐ ์ถ”๊ฐ€ ์„ฑ๊ณต ํ† ์ŠคํŠธ ํŒ์—…์ด ๋œฌ๋‹ค. (3์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์‚ฌ๋ผ์ง„๋‹ค)
- ์‡ผํ•‘๋ชฐ๋กœ ๋Œ์•„๊ฐ€๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ โ†’ ์‡ผํ•‘๋ชฐ ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ„๋‹ค.
- ๋ฐ์ดํ„ฐ ์ €์žฅ/๋ณต์›
    - ์ƒํ’ˆ, ์ฟ ํฐ ๋ฐ์ดํ„ฐ๊ฐ€ localStorage์— ์ž๋™์œผ๋กœ ์ €์žฅ๋œ๋‹ค.
    - ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž๋™์œผ๋กœ ๋ณต์›๋œ๋‹ค.

(2-2) Action / Calculation / Data๋กœ ์žฌ๋ถ„๋ฅ˜ ์œ„์—์„œ ํ…์ŠคํŠธ๋กœ ์ •๋ฆฌํ•œ ๋‚ด์šฉ์„ FP ๊ด€์ ์—์„œ Action / Calculation / Data๋กœ ์žฌ๋ถ„๋ฅ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ Action (๋ถ€์ˆ˜ํšจ๊ณผ)

[์‡ผํ•‘๋ชฐ]

  • ๊ฒ€์ƒ‰ input ์ž…๋ ฅ
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ˆ˜๋Ÿ‰ input ํด๋ฆญ
  • ์ฟ ํฐ select ํด๋ฆญ
  • ๊ฒฐ์ œ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ X ๋ฒ„ํŠผ ํด๋ฆญ
  • ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€๋กœ ๋ฒ„ํŠผ ํด๋ฆญ
  • ํ† ์ŠคํŠธ ์•Œ๋ฆผ ํ‘œ์‹œ/์ œ๊ฑฐ
  • localStorage ์ž๋™ ์ €์žฅ
  • ๊ฒ€์ƒ‰ debounce ์ฒ˜๋ฆฌ

[๊ด€๋ฆฌ์ž]

  • ์ƒˆ ์ƒํ’ˆ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ƒํ’ˆ๋ช… ์ž…๋ ฅ
  • ๊ฐ€๊ฒฉ ์ž…๋ ฅ
  • ์žฌ๊ณ  ์ž…๋ ฅ
  • ํ• ์ธ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ
  • ํ• ์ธ ํ•ญ๋ชฉ X ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ฟ ํฐ ๊ด€๋ฆฌ ํƒญ ํด๋ฆญ
  • ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ƒˆ ์ฟ ํฐ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์ฟ ํฐ๋ช… ์ž…๋ ฅ
  • ์ฟ ํฐ์ฝ”๋“œ ์ž…๋ ฅ
  • ํ• ์ธ๊ธˆ์•ก ์ž…๋ ฅ
  • ํ• ์ธ ํƒ€์ž… select ํด๋ฆญ
  • ์ฟ ํฐ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์‡ผํ•‘๋ชฐ๋กœ ๋Œ์•„๊ฐ€๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ

2๏ธโƒฃ Calculation (์ˆœ์ˆ˜ ๋กœ์ง)

  • ํ•„ํ„ฐ๋ง
  • ํ• ์ธ ๊ณ„์‚ฐ
  • ์ˆ˜๋Ÿ‰ ๊ณ„์‚ฐ
  • validation ํŒ๋‹จ
  • ์ฟ ํฐ ์ฝ”๋“œ ๋Œ€๋ฌธ์ž ๋ณ€ํ™˜
  • ์žฌ๊ณ  ๊ณ„์‚ฐ
  • ๊ฐ€๊ฒฉ ํฌ๋งทํŒ…
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ด ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ
  • ์ฟ ํฐ ์ ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํŒ๋‹จ
  • ์ค‘๋ณต ์ฟ ํฐ ์ฝ”๋“œ ๊ฒ€์ฆ
  • ์ฃผ๋ฌธ๋ฒˆํ˜ธ ์ƒ์„ฑ

3๏ธโƒฃ Data (์ •์ /์ƒํƒœ ๋ฐ์ดํ„ฐ)

  • ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฆฌ์ŠคํŠธ
  • ์ฟ ํฐ ๋ฆฌ์ŠคํŠธ
  • ์‹ ๊ทœ ์ฟ ํฐ form ๋ฐ์ดํ„ฐ
  • ํ• ์ธ ์ •์ฑ…
  • ์‹ ๊ทœ ์ƒํ’ˆ form ๋ฐ์ดํ„ฐ
  • ์ƒํ’ˆ ์ˆ˜์ • form ๋ฐ์ดํ„ฐ
  • ๊ฒ€์ƒ‰์–ด (searchTerm, debouncedSearchTerm)
  • ์„ ํƒ๋œ ์ฟ ํฐ (selectedCoupon)
  • ๊ด€๋ฆฌ์ž ๋ชจ๋“œ ์ƒํƒœ (isAdmin)
  • ์•Œ๋ฆผ ๋ฆฌ์ŠคํŠธ (notifications)
  • ํ™œ์„ฑ ํƒญ (activeTab)
  • ํผ ํ‘œ์‹œ ์ƒํƒœ (showProductForm, showCouponForm)
  • ์ˆ˜์ • ์ค‘์ธ ์ƒํ’ˆ ID (editingProduct)
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ด ๊ฐœ์ˆ˜ (totalItemCount)

(2-3) ํ”ผ๊ทธ์žผ์œผ๋กœ ์‹œ๊ฐํ™”ํ•˜๊ธฐ ์ด๋Ÿฐ ๊ฑธ ์ฒ˜์Œ ํ•ด๋ณด๋‹ค๋ณด๋‹ˆ ์ดˆ๋ฐ˜์—๋Š” ๊ฐ€์ด๋“œ๋ฅผ ์žก๊ณ  ์–ด๋–ค ๊ฑธ ์–ด๋””๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ํ™”์‚ดํ‘œ๋Š” ์–ด๋””๊นŒ์ง€ ์—ฐ๊ฒฐํ•ด์•ผ ํ• ์ง€์— ๋Œ€ํ•œ ๊ธฐ์ค€์ด ์žกํžˆ์ง€ ์•Š์•„ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ Q&A ๋•Œ ํ…Œ์˜ค๊ฐ€ ํ™”์‚ดํ‘œ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฑด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฉฐ, ์ด๋Š” ๊ณง a=>b๋ผ๊ณ  ํ•  ๋•Œ a๋Š” b๋ฅผ ์•Œ๊ณ  ์žˆ์ง€๋งŒ b๋Š” a๋ฅผ ๋ชจ๋ฅธ๋‹ค๋Š” ๋‹จ๋ฐฉํ–ฅ ํ๋ฆ„์„ ์˜๋ฏธํ•œ๋‹ค๊ณ  ํ•˜์—ฌ ์ดํ•ด๊ฐ€ ๊ฐ”์Šต๋‹ˆ๋‹ค.

๊ทธ ์ดํ›„๋กœ ๊ฐœ๋ณ„ ํ”Œ๋กœ์šฐ๋Š” ์‹œ๊ฐํ™”๋ฅผ ์™„์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ง„ํ–‰ํ•œ ํ”ผ๊ทธ์žผ ๋งํฌ image

(3) ๋ณธ๊ฒฉ์ ์ธ ๊ตฌ์กฐ ๊ฐœ์„  (3-1) ๊ณ ๋ฏผ 1๏ธโƒฃ ์•„์ด์ฝ˜ ์ •์˜ โ— ๋‘˜ ๋‹ค ์‚ญ์ œํ•˜๋Š” ์•„์ด์ฝ˜์ธ๋ฐ, ์—ญํ• ์„ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„์ง€์–ด์•ผ ํ• ๊นŒ? image image

X ์•„์ด์ฝ˜๊ณผ ์“ฐ๋ ˆ๊ธฐํ†ต ๋ชจ์–‘ ๋‘˜ ๋‹ค ๋ฌด์–ธ๊ฐ€๋ฅผ ์‚ญ์ œํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์•„์ด์ฝ˜์ž…๋‹ˆ๋‹ค.

๋„ค์ด๋ฐ์„ ํ•  ๋•Œ ์‹œ๊ฐ์ ์ธ ๊ฑธ ๊ธฐ์ค€์œผ๋กœ IconX, IconTrash๋ผ๋Š” ๋“ฑ์˜ ์ด๋ฆ„์œผ๋กœ ํ•˜๊ฒŒ ๋˜๋ฉด ์ถ”ํ›„์— ๋งŒ์•ฝ ์•„์ด์ฝ˜์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ ์ด๋ฆ„ ์ˆ˜์ •์ด๋‚˜ ์•„์ด์ฝ˜ ์ถ”๊ฐ€ ์ž‘์—…์ด ํ•„์š”ํ•˜๊ฒŒ ๋˜๊ณ  ์ด๋Š” ๊ณง ๋ถˆํ•„์š”ํ•œ ์ถ”๊ฐ€ ์ž‘์—…์„ ์ดˆ๋ž˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‘๊ฐœ์˜ ์—ญํ• ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ •ํ•ด๋‘๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • X ์•„์ด์ฝ˜: ์ผ๋ฐ˜์ ์œผ๋กœ ํผ์ด๋‚˜ ๋ชจ๋‹ฌ ํ˜•ํƒœ์—์„œ ๋‹ซ๊ธฐ/์ง€์šฐ๊ธฐ(on/off ๊ฐ€๋Šฅ)์˜ ์—ญํ• ์„ ํ•˜๋Š” ์•„์ด์ฝ˜ => IconClose
  • ์“ฐ๋ ˆ๊ธฐํ†ต ์•„์ด์ฝ˜: ์˜๊ตฌ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ์•„์ด์ฝ˜ => IconDelete ๊ทธ๋ ‡๊ฒŒ ํ•˜๋‹ˆ ๋„ค์ด๋ฐ์„ ์‰ฝ๊ฒŒ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค!

โ— ๋‘˜ ๋‹ค ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชจ์–‘์ธ๋ฐ, ์—ญํ• ์„ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„์ง€์–ด์•ผ ํ• ๊นŒ? image image

์ฒซ๋ฒˆ์งธ์™€ ์œ ์‚ฌํ•œ ๊ณ ๋ฏผ์œผ๋กœ, ๋‘˜ ๋‹ค ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชจ์–‘์ธ๋ฐ ์–ด๋–ป๊ฒŒ ๋„ค์ด๋ฐํ• ์ง€ ๊ณ ๋ฏผ๋์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์•ž์„  ๊ฒฝํ—˜์„ ๊ฒช๊ณ ๋‚˜๋‹ˆ ์ด๋ฒˆ์—๋„ ์—ญํ•  ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ๋ถ„ํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

  • ์นดํŠธ ์•„์ด์ฝ˜: ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ฐœ์ˆ˜๋ฅผ ๋ณด์—ฌ์ฃผ๊ฑฐ๋‚˜ ํด๋ฆญ ์‹œ ์•ก์…˜์ด ์กด์žฌํ• ๋งŒํ•œ ์˜์—ญ(ํ˜„์žฌ๋Š” ํ•ด๋‹น ๊ธฐ๋Šฅ๊นŒ์ง€๋Š” ๋ฏธ๊ฐœ๋ฐœ ์ƒํƒœ) => IconCartButton
  • ์‡ผํ•‘๋ฐฑ ์•„์ด์ฝ˜: ๋‹จ์ˆœํ•œ ์žฅ์‹ ์š”์†Œ => IconCartSymbol

2๏ธโƒฃ ๋™์ผํ•œ ๋‚ด์šฉ์˜ ํ† ์ŠคํŠธ ํŒ์—…์ธ๋ฐ ์ค‘๋ณต ๋…ธ์ถœ์„ ํ—ˆ์šฉํ•ด๋„ ๋˜๋Š”๊ฐ€? image ๋™์ผํ•œ ๋‚ด์šฉ์˜ ํ† ์ŠคํŠธ ํŒ์—…์ด ์•„์ง ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง€์ง€ ์•Š์•˜๋Š”๋ฐ ํ˜ธ์ถœํ•˜๋Š”๋งŒํผ ๋…ธ์ถœ๋˜๋Š” ๊ฒŒ ์—๋Ÿฌ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋™์ผํ•œ ๋‚ด์šฉ์ด๋ผ๋ฉด ํ•ด๋‹น ํ† ์ŠคํŠธ ํŒ์—…์ด ์‚ฌ๋ผ์ง€๊ธฐ ์ „๊นŒ์ง€๋Š” ์ค‘๋ณต ํ˜ธ์ถœ์„ ๋ง‰๋Š” ๊ฒƒ์œผ๋กœ ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ ์ƒํ’ˆ๋ชฉ๋ก์ด ๋น„์–ด์žˆ์œผ๋ฉด table ๋ถ€๋ถ„๋ถ€ํ„ฐ ์•ˆ ๋‚˜์™€์•ผ ํ•˜๋Š” ๊ฒŒ ์•„๋‹Œ๊ฐ€? image ๊ธฐ์กด์—๋Š” ์ƒํ’ˆ ๋ชฉ๋ก์ด ๋น„์–ด์žˆ์–ด๋„ table header ๋ถ€๋ถ„๊นŒ์ง€๋Š” ๋…ธ์ถœ๋˜๊ณ  body ๋ถ€๋ถ„๋งŒ ๋ฏธ๋…ธ์ถœ๋˜๋„๋ก ์ž‘์—…๋˜์–ด์žˆ์—ˆ์ง€๋งŒ, ๊ฐœ์ธ์ ์œผ๋กœ ์ด๊ฑด ์ ํ•ฉํ•˜์ง€ ์•Š์€ UI๋ผ๊ณ  ํŒ๋‹จ๋˜์–ด table ์ž์ฒด๊ฐ€ ๋ฏธ๋…ธ์ถœ๋˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

4๏ธโƒฃ isRecommended๋Š” ์–ด๋–ป๊ฒŒ ํ™•์ธํ•˜๋‚˜? image ์ƒํ’ˆ ๊ตฌ์กฐ์—์„œ๋Š” isRecommended๋ฅผ ๊ธฐ์ค€์œผ๋กœ BEST ๋ฑƒ์ง€ ๋…ธ์ถœ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ form์—์„œ๋Š” ๋„์ €ํžˆ isRecommended๋ฅผ ์ œ์–ดํ•˜๋Š” ์˜์—ญ์„ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ๊ณ  ์ด์ƒํ•œ ๋ถ€๋ถ„์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ BEST ๋ฑƒ์ง€๋Š” ๊ด€๋ฆฌ์ž ์žฌ๋Ÿ‰์œผ๋กœ ๋…ธ์ถœ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— form์—์„œ isRecommended๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

5๏ธโƒฃ ์˜๋ฏธ์—†๋Š” ๋ถ„๊ธฐ ๋ถˆํ•„์š”ํ•œ ๋ถ„๊ธฐ๋Š” ์‚ญ์ œํ–ˆ์Šต๋‹ˆ๋‹ค!

{(activeTab === "products" ? products : products).map((product) =>

(3-2) ๋””์ž์ธ ํŒจํ„ด ์ ์šฉ 1๏ธโƒฃ ์ƒ์„ฑ ํŒจํ„ด(Creational Patterns) โ—Factory Method(ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด) ์ฟ ํฐ ์ƒ์„ฑ ์‹œ ๋ณต์žกํ•œ ๊ฒ€์ฆ ๋กœ์ง์„ ์บก์Аํ™”ํ•˜์—ฌ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

// models/coupon.ts
export interface AddCouponResult {
  coupons: Coupon[];
  success: boolean;
  errorMessage?: string;
}
export const addCoupon = (
  coupons: Coupon[],
  newCoupon: Coupon
): AddCouponResult => {
  // 1. ๊ฒ€์ฆ
  const validation = validateCouponForm(newCoupon);
  if (!validation.isValid) {
    return {
      coupons,
      success: false,
      errorMessage: validation.errorMessage,
    };
  }
  // 2. ์ค‘๋ณต ์ฒดํฌ
  if (isCouponCodeDuplicate(coupons, newCoupon.code)) {
    return {
      coupons,
      success: false,
      errorMessage: "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ฟ ํฐ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.",
    };
  }
  // 3. ์ฟ ํฐ ์ƒ์„ฑ ๋ฐ ๋ฐ˜ํ™˜
  return {
    coupons: [...coupons, newCoupon],
    success: true,
  };
};
// AdminCouponForm.tsx
const result = addCoupon(coupons, couponForm);
if (!result.success) {
  handleNotificationAdd(result.errorMessage || "", "error");
  return;
}
setCoupons(result.coupons);

2๏ธโƒฃ ๊ตฌ์กฐ ํŒจํ„ด(Structural Patterns) โ—Facade(ํŒŒ์‚ฌ๋“œ ํŒจํ„ด)

// models/cart.ts
export const calculateCartTotal = (
  cart: CartItem[],
  selectedCoupon: Coupon | null
): {
  totalBeforeDiscount: number;
  totalAfterDiscount: number;
} => {
  // ๋‚ด๋ถ€์ ์œผ๋กœ ์—ฌ๋Ÿฌ ๋ณต์žกํ•œ ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰
  const totalBeforeDiscount = calculateCartOriginalTotal(cart);
  const totalAfterItemDiscount = calculateTotalBeforeCoupon(cart);
  const totalAfterDiscount = applyCouponDiscount(
    totalAfterItemDiscount,
    selectedCoupon
  );
  return {
    totalBeforeDiscount,
    totalAfterDiscount,
  };
};

๋ณต์žกํ•œ ํ• ์ธ ๊ณ„์‚ฐ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” calculateCartTotal(cart, coupon)๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

// ShoppingCouponPayment.tsx
const totals = calculateCartTotal(cart, selectedCoupon);

โ—Composite (์ปดํฌ์ง€ํŠธ ํŒจํ„ด) ์ž‘์€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋“ค์„ ์กฐํ•ฉํ•˜์—ฌ ๋ณต์žกํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ ๊ฐ ํ•จ์ˆ˜์˜ ๋…๋ฆฝ์ ์ธ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ  ํ•จ์ˆ˜ ์žฌ์‚ฌ์šฉ์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

// calculateItemTotal์€ ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ์กฐํ•ฉ
export const calculateItemTotal = (
  cart: CartItem[],
  item: CartItem
): number => {
  // ํ• ์ธ์œจ ๊ณ„์‚ฐ
  const discountRate = getMaxApplicableDiscount(cart, item);  
  // ํ• ์ธ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ
  return calculateDiscountedPrice(                            
    item.product.price,
    item.quantity,
    discountRate
  );
};
// getMaxApplicableDiscount๋„ ์—ฌ๋Ÿฌ ํ•จ์ˆ˜ ์กฐํ•ฉ
export const getMaxApplicableDiscount = (
  cart: CartItem[],
  item: CartItem
): number => {
// ๊ธฐ๋ณธ ํ• ์ธ
  const baseDiscount = getMaxDiscountRate(                    
    item.product.discounts,
    item.quantity
  );
  // ๋Œ€๋Ÿ‰ ๊ตฌ๋งค ํ• ์ธ
  if (hasBulkPurchase(cart)) {                                
    return Math.min(baseDiscount + 0.05, 0.5);
  }
  return baseDiscount;
};

3๏ธโƒฃ ํ–‰๋™ ํŒจํ„ด(Behavioral Patterns) โ—Strategy(์ „๋žต ํŒจํ„ด) ํ• ์ธ ํƒ€์ž…(amount/percentage)์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ณ„์‚ฐ ๋ฐฉ์‹์„ ์ ์šฉํ•จ์œผ๋กœ์จ ์ƒˆ๋กœ์šด ํ• ์ธ ํƒ€์ž… ์ถ”๊ฐ€ ์‹œ ํ™•์žฅ์ด ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

// models/coupon.ts
export const applyCouponDiscount = (
  totalBeforeCoupon: number,
  coupon: Coupon | null
): number => {
  if (!coupon) {
    return totalBeforeCoupon;
  }
  // Strategy: ํ• ์ธ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ ์šฉ
  if (coupon.discountType === "amount") {
    // ์ •์•ก ํ• ์ธ ์ „๋žต
    return Math.max(0, totalBeforeCoupon - coupon.discountValue);
  } else {
    // ์ •๋ฅ  ํ• ์ธ ์ „๋žต
    return Math.round(
      totalBeforeCoupon * (1 - coupon.discountValue / 100)
    );
  }
};

โ—Template Method(ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด) - ๋ถ€๋ถ„์  ์ ์šฉ ๊ฐ ๋‹จ๊ณ„๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ๊ฒ€์ฆ โ†’ ์ฒดํฌ โ†’ ์ƒ์„ฑ์˜ ์ˆœ์„œ๊ฐ€ ํ•ญ์ƒ ์œ ์ง€๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

// coupon.ts
export const addCoupon = (
  coupons: Coupon[],
  newCoupon: Coupon
): AddCouponResult => {
  // ํ…œํ”Œ๋ฆฟ: 1. ๊ฒ€์ฆ โ†’ 2. ์ค‘๋ณต ์ฒดํฌ โ†’ 3. ์ƒ์„ฑ
  const validation = validateCouponForm(newCoupon);  // 1๋‹จ๊ณ„
  if (!validation.isValid) {
    return { coupons, success: false, errorMessage: validation.errorMessage };
  }
  if (isCouponCodeDuplicate(coupons, newCoupon.code)) {  // 2๋‹จ๊ณ„
    return { coupons, success: false, errorMessage: "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ฟ ํฐ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค." };
  }
  return { coupons: [...coupons, newCoupon], success: true };  // 3๋‹จ๊ณ„
};

4๏ธโƒฃ ์ถ”๊ฐ€ ์„ค๊ณ„ ์›์น™ ๋ฐ ํŒจํ„ด GoF ํŒจํ„ด ์™ธ์—๋„ ์ ์šฉ๋œ ์ค‘์š”ํ•œ ์„ค๊ณ„ ์›์น™๋“ค์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

โ— Pure Function Pattern(์ˆœ์ˆ˜ ํ•จ์ˆ˜ ํŒจํ„ด) ๋™์ผํ•œ ์ž…๋ ฅ์— ๋Œ€ํ•ด ํ•ญ์ƒ ๋™์ผํ•œ ์ถœ๋ ฅ์„ ๋ณด์žฅํ•˜๊ณ  ๋””๋ฒ„๊น…์ด ์šฉ์ดํ•˜๋‹ค.

// discount.ts
export const getMaxDiscountRate = (
  discounts: Discount[],
  quantity: number
): number => {
  return discounts.reduce((maxDiscount, discount) => {
    return quantity >= discount.quantity && discount.rate > maxDiscount
      ? discount.rate
      : maxDiscount;
  }, 0);
};

โ— Result Pattern(๊ฒฐ๊ณผ ํŒจํ„ด) ์˜ˆ์™ธ ๋Œ€์‹  Result ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ํ–ฅ์ƒ๋˜๊ณ  ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// cart.ts
export type AddToCartError = "OUT_OF_STOCK" | "EXCEEDS_STOCK" | null;
export interface AddToCartResult {
  cart: CartItem[];
  error: AddToCartError;
}
export const addProductToCart = (
  cart: CartItem[],
  product: ProductWithUI
): AddToCartResult => {
  const remainingStock = getRemainingStock(product, cart);
  if (remainingStock <= 0) {
    return { cart, error: "OUT_OF_STOCK" };
  }
  // ...
  return { cart: nextCart, error: null };
};

โ— Immutability Pattern (๋ถˆ๋ณ€์„ฑ ํŒจํ„ด) ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•จ์œผ๋กœ์จ React์˜ ๋ถˆ๋ณ€์„ฑ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•ฉ๋‹ˆ๋‹ค.

// cart.ts
export const removeProductFromCart = (
  cart: CartItem[],
  productId: string
): CartItem[] => {
  return cart.filter((item) => item.product.id !== productId); // ์ƒˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜
};
export const updateCartItemQuantity = (
  cart: CartItem[],
  productId: string,
  newQuantity: number
): CartItem[] => {
  return cart.map((item) =>
    item.product.id === productId
      ? { ...item, quantity: newQuantity }  // ์ƒˆ ๊ฐ์ฒด ์ƒ์„ฑ
      : item
  );
};

โ—Single Responsibility Principle(๋‹จ์ผ ์ฑ…์ž„ ์›์น™) ์—”ํ‹ฐํ‹ฐ๋ณ„ ๋ชจ๋“ˆ์„ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ๋ชจ๋“ˆ ๊ฐ„ ๊ฒฐํ•ฉ๋„๋Š” ๊ฐ์†Œ์‹œํ‚ค๊ณ  ๋ณ€๊ฒฝ ์˜ํ–ฅ ๋ฒ”์œ„๋ฅผ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

models/
  โ”œโ”€โ”€ cart.ts      โ†’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ด€๋ จ ๋กœ์ง๋งŒ
  โ”œโ”€โ”€ coupon.ts    โ†’ ์ฟ ํฐ ๊ด€๋ จ ๋กœ์ง๋งŒ
  โ”œโ”€โ”€ discount.ts  โ†’ ํ• ์ธ ๊ด€๋ จ ๋กœ์ง๋งŒ
  โ””โ”€โ”€ product.ts   โ†’ ์ƒํ’ˆ ๊ด€๋ จ ๋กœ์ง๋งŒ

โ— Separation of Concerns (๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ) ์ปดํฌ๋„ŒํŠธ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ์žฌ์‚ฌ์šฉ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

Before (์ปดํฌ๋„ŒํŠธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํฌํ•จ)
// ShoppingCartItem.tsx (์ด์ „)
const calculateItemTotal = (item: CartItem): number => {
  const { price } = item.product;
  const { quantity } = item;
  const discount = getMaxApplicableDiscount(item, cart);
  return Math.round(price * quantity * (1 - discount));
};

After (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ชจ๋ธ๋กœ ๋ถ„๋ฆฌ)
// models/cart.ts
export const calculateItemTotal = (cart, item) => { /* ... */ };
// ShoppingCartItem.tsx
const itemTotal = calculateItemTotal(cart, item);

(4) Jotai ์ ์šฉ ๊ตฌ์กฐ ๊ฐœ์„ ์„ ์ง„ํ–‰ํ•˜๋ฉฐ props drilling์ด ๊ฑฐ๋“ญ๋  ๋•Œ๋งˆ๋‹ค ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ๋ฅผ ๋œ ํ•ด์•ผ ํ•˜๋‚˜ ์‹ถ์–ด์ง€๊ธฐ๊นŒ์ง€ ํ–ˆ์Šต๋‹ˆ๋‹ค.

src/basic/
โ”œโ”€โ”€ App.tsx (L0) โš ๏ธ Props ์ƒ์„ฑ
โ”‚   โ”œโ”€โ”€ cart: CartItem[]
โ”‚   โ”œโ”€โ”€ products: ProductWithUI[]
โ”‚   โ”œโ”€โ”€ setCart: Dispatch<...>
โ”‚   โ””โ”€โ”€ addNotification: (message, type) => void
โ”‚
โ””โ”€โ”€ pages/
    โ””โ”€โ”€ PageShopping.tsx (L1) โš ๏ธ Props ์ „๋‹ฌ๋งŒ (์‚ฌ์šฉ ์•ˆ ํ•จ)
        โ”œโ”€โ”€ cart: CartItem[] โฌ‡๏ธ
        โ”œโ”€โ”€ products: ProductWithUI[] โฌ‡๏ธ
        โ”œโ”€โ”€ setCart: Dispatch<...> โฌ‡๏ธ
        โ””โ”€โ”€ handleNotificationAdd: (message, type) => void โฌ‡๏ธ
        โ”‚
        โ””โ”€โ”€ shopping/
            โ””โ”€โ”€ ShoppingList.tsx (L2) โš ๏ธ Props ์ „๋‹ฌ๋งŒ (์‚ฌ์šฉ ์•ˆ ํ•จ)
                โ”œโ”€โ”€ cart: CartItem[] โฌ‡๏ธ
                โ”œโ”€โ”€ products: ProductWithUI[] โฌ‡๏ธ
                โ”œโ”€โ”€ setCart: Dispatch<...> โฌ‡๏ธ
                โ””โ”€โ”€ handleNotificationAdd: (message, type) => void โฌ‡๏ธ
                โ”‚
                โ””โ”€โ”€ product/
                    โ””โ”€โ”€ ShoppingProductItem.tsx (L3) โš ๏ธ Props ์ „๋‹ฌ๋งŒ (์‚ฌ์šฉ ์•ˆ ํ•จ)
                        โ”œโ”€โ”€ cart: CartItem[] โฌ‡๏ธ
                        โ”œโ”€โ”€ products: ProductWithUI[] โฌ‡๏ธ
                        โ”œโ”€โ”€ setCart: Dispatch<...> โฌ‡๏ธ
                        โ””โ”€โ”€ handleNotificationAdd: (message, type) => void โฌ‡๏ธ
                        โ”‚
                        โ””โ”€โ”€ components/entity/product/
                            โ””โ”€โ”€ ProductItem.tsx (L4) โœ… ์ตœ์ข… ์‚ฌ์šฉ์ฒ˜
                                โ”œโ”€โ”€ cart: CartItem[] โœ… ์‹ค์ œ ์‚ฌ์šฉ
                                โ”œโ”€โ”€ products: ProductWithUI[] โœ… ์‹ค์ œ ์‚ฌ์šฉ
                                โ”œโ”€โ”€ setCart: Dispatch<...> โœ… ์‹ค์ œ ์‚ฌ์šฉ
                                โ””โ”€โ”€ handleNotificationAdd: (message, type) => void โœ… ์‹ค์ œ ์‚ฌ์šฉ

ํ•˜์ง€๋งŒ ๊ทธ๊ฒŒ ๊ณผ์ œ์˜ ๋ชฉํ‘œ์ด๋‹ˆ ์•„๋ž‘๊ณณํ•˜์ง€ ์•Š๊ณ  props drilling์„ ์ง„ํ–‰ํ–ˆ๊ณ  ๋“œ๋””์–ด ๊ฐœ์„ ํ•  ๋•Œ์ž…๋‹ˆ๋‹ค!!!!

์ ์šฉํ•˜๋Š” ๊ณผ์ •์€ ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. useState๋กœ ์ •์˜ํ–ˆ๋˜ ๋‚ด์šฉ๋“ค์€ ๋‹ค Jotai๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜๋ฉด ๋กœ์ปฌ ํŽ˜์ด์ง€์—์„œ๋Š” ๋ฌธ์ œ์—†์ด ๋™์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

(4-1) ๋Œ์•„์˜จ ํ…Œ์ŠคํŠธ์™€ ๋กœ์ปฌ์˜ ์ฐจ์ด ๊ทธ๋Ÿฌ๋‚˜ ํ…Œ์ŠคํŠธ์—์„œ๋Š” "์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ"๋ฒ„ํŠผ์„ ์ฐพ์ง€ ๋ชปํ•ด์„œ ํ…Œ์ŠคํŠธ๊ฐ€ 12๊ฐœ์ •๋„ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค..(๋Œ์•„์˜จ ํ…Œ์ŠคํŠธ์™€ ๋กœ์ปฌ์˜ ์ฐจ์ด)

fireEvent.click(screen.getAllByText('์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ')[0]);

์Šค๋ƒ…์ƒท์„ ํ™•์ธํ•ด๋ณด๋‹ˆ ํ…Œ์ŠคํŠธ์˜ ์•Œ๋ฆผ์ด ๋งŽ์ด ์ƒ์„ฑ๋˜์–ด ํŽ˜์ด์ง€์— ๋‚จ์•„์žˆ์—ˆ๊ณ  ๊ทธ๋กœ ์ธํ•ด ๋ฒ„ํŠผ์„ ์ฐพ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

์ฆ‰, ๊ฐ ํ…Œ์ŠคํŠธ์˜ ์ƒํƒœ๊ฐ€ ๊ฒฉ๋ฆฌ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด๊ณ  ๋งค ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ์‹œ์ž‘ ๋ถ€๋ถ„์— App์„ ๋ Œ๋”ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๊ฐ App์ด ๋…๋ฆฝ๋œ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋„๋ก ํ•˜๋ฉด ๋˜์ง€ ์•Š์„๊นŒ ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

test('์ƒํ’ˆ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค', async () => {
  render(<App />);

์ด๋ฅผ ์œ„ํ•ด Provider๋กœ App์„ ๊ฐ์‹ธ์„œ App ๋ Œ๋”๋ง ์‹œ ๋งค๋ฒˆ ์ƒˆ๋กœ์šด Provider๊ฐ€ ์ƒ์„ฑ๋˜๋„๋ก ํ–ˆ๊ณ  ์ด๋ฅผ ํ†ตํ•ด ์ด์ „ ํ…Œ์ŠคํŠธ์˜ ์ƒํƒœ๊ฐ€ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ฒŒ๋” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!


์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ†ตํ•ด ์•ž์œผ๋กœ ํ•ด๋ณด๊ณ  ์‹ถ์€๊ฒŒ ์žˆ๋‹ค๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”!

์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋А๋‚€ ์ ์€ ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๊ฐœ์„ ํ•˜๋Š” ์ž‘์—…์€ ์ฐจ๋ผ๋ฆฌ ๋ช…ํ™•ํ•œ ๊ธฐ์ค€์ด ์žˆ์–ด์„œ ์ ‘๊ทผํ•˜๊ธฐ ์‰ฌ์› ์ง€๋งŒ, ์ฒ˜์Œ๋ถ€ํ„ฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋‚˜ ๋””์ž์ธ ํŒจํ„ด์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ณ ๋ คํ•˜๋Š” ๊ฒƒ์€ ํ›จ์”ฌ ์–ด๋ ต๋‹ค๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์•ž์œผ๋กœ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ๋ฐ”๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋Œ€์‹ 

  • ๊ธฐ๋Šฅ ํ”Œ๋กœ์šฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ 
  • Action / Calculation / Data ๋ฅผ ์ •๋ฆฌํ•˜๊ณ 
  • ์—”ํ‹ฐํ‹ฐ ๋‹จ์œ„๋กœ ๊ตฌ์กฐ๋ฅผ ๋จผ์ € ์žก์€ ๋’ค
  • ๊ทธ ํ›„์— ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ์‹ ์œผ๋กœ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ตฌ์กฐ์˜ ํ๋ฆ„์„ ์„ค๊ณ„ํ•˜๋Š” ์Šต๊ด€์„ ๋งŒ๋“ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ†ตํ•ด ๊ทธ ์ค‘์š”์„ฑ์„ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ๋‹ค์Œ ํ”„๋กœ์ ํŠธ๋‚˜ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์— ์ ์šฉํ•ด๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค!

๋ฆฌ๋ทฐ ๋ฐ›๊ณ  ์‹ถ์€ ๋‚ด์šฉ์ด๋‚˜ ๊ถ๊ธˆํ•œ ๊ฒƒ์— ๋Œ€ํ•œ ์งˆ๋ฌธ ํŽธํ•˜๊ฒŒ ๋‚จ๊ฒจ์ฃผ์„ธ์š” :)

  • ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ธฐ๋Šฅ๊ณผ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ ์–ด๋–ค ๊ธฐ์ค€์ด๋‚˜ ์ ‘๊ทผ ๋ฐฉ์‹์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์ข‹์„๊นŒ์š”?
  • ๊ตฌ์กฐ ์„ค๊ณ„ ๋‹จ๊ณ„์—์„œ ํ”ผ๊ทธ์žผ์ด๋‚˜ ๋…ธ์…˜์— ์–ด๋–ค ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๋ฉด ์‹ค์ „์—์„œ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ์ง€ ๋…ธํ•˜์šฐ๊ฐ€ ์žˆ์„๊นŒ์š”?
  • ํŒจํ„ด๊ณผ ํ•จ์ˆ˜ํ˜• ์‚ฌ๊ณ ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ›ˆ๋ จํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค!

์ž์„ธํ•œ ๊ตฌํ˜„ ๊ณผ์ •๊ณผ ํšŒ๊ณ ๋Š” ์•„๋ž˜ ๋ธ”๋กœ๊ทธ์— ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค! ๐Ÿ˜Š 7์ฃผ์ฐจ_๋””์ž์ธ ํŒจํ„ด๊ณผ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ทธ๋ฆฌ๊ณ  ์ƒํƒœ ๊ด€๋ฆฌ ์„ค๊ณ„ WIL 7์ฃผ์ฐจ_Chapter3-2. ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ทธ๋ฆฌ๊ณ  ์ƒํƒœ ๊ด€๋ฆฌ ์„ค๊ณ„

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 99.5%
  • Other 0.5%