Skip to content

Conversation

@hyeok02
Copy link
Contributor

@hyeok02 hyeok02 commented Dec 15, 2025

๐Ÿ“ ๋ฏธ์…˜ ๋ฒˆํ˜ธ

9์ฃผ์ฐจ Misson 0,1,2,3

๐Ÿ“‹ ๊ตฌํ˜„ ์‚ฌํ•ญ

  • Reducer ์‹ค์Šต
  • Redux Toolkit ํ™œ์šฉ UMC Play List ์ œ์ž‘
  • Modal Slice ํ™œ์šฉํ•˜์—ฌ, ๋ชจ๋‹ฌ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
  • Redux Toolkit์œผ๋กœ ๋งŒ๋“  UMC Play List๋ฅผ Zustand๋กœ ๋ฆฌํŒฉํ† ๋ง

๐Ÿ“Ž ์Šคํฌ๋ฆฐ์ƒท

2025-12-15.175308.mp4

โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • Merge ํ•˜๋ ค๋Š” ๋ธŒ๋žœ์น˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ๋‚˜์š”?
  • ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋‚˜์š”?
  • ๋ถˆํ•„์š”ํ•œ ์ฃผ์„์ด ์ œ๊ฑฐ๋˜์—ˆ๋‚˜์š”?
  • ์ฝ”๋“œ ์Šคํƒ€์ผ์ด ์ผ๊ด€์ ์ธ๊ฐ€์š”?

๐Ÿค” ์งˆ๋ฌธ ์‚ฌํ•ญ

@hyeok02 hyeok02 requested a review from woojo230 December 15, 2025 09:04
@hyeok02 hyeok02 self-assigned this Dec 15, 2025
@github-actions
Copy link

๐Ÿค– Gemini ์ฝ”๋“œ๋ฆฌ๋ทฐ ๊ฒฐ๊ณผ

์•ˆ๋…•ํ•˜์„ธ์š”, ์‹œ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ์„œ ์ œ๊ณตํ•ด์ฃผ์‹  Pull Request์˜ ๋ชจ๋“  ํŒŒํŠธ(1/6 ~ 6/6)๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ๋ฉด๋ฐ€ํžˆ ๊ฒ€ํ† ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ PR์—์„œ๋Š” ๋‹ค์–‘ํ•œ ํ•™์Šต ๋ฏธ์…˜์˜ ํ”์ ๊ณผ ํ•จ๊ป˜, ํ•ต์‹ฌ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ธฐ๋Šฅ์˜ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ Redux์—์„œ Zustand๋กœ ์ „ํ™˜๋˜๋Š” ๊ณผ์ •์„ ์—ฟ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์š”์ฒญํ•˜์‹  ๊ธฐ์ค€(ํŠนํžˆ src ๋‚ด๋ถ€ .ts ๋ฐ .tsx ํŒŒ์ผ์— ๋Œ€ํ•œ ์„ฑ๋Šฅ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ, ๋ณ€์ˆ˜๋ช…/์ฃผ์„, ์ค‘๋ณต/๋ฆฌํŒฉํ† ๋ง, ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ)์— ๋งž์ถฐ ํ†ตํ•ฉ ๋ฆฌ๋ทฐ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.


PR ์ „๋ฐ˜์— ๋Œ€ํ•œ ์ดˆ๊ธฐ ์ธ์ƒ ๋ฐ ๊ตฌ์กฐ ํŒŒ์•…

PR์€ ํฌ๊ฒŒ ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋งฅ๋ฝ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ํ”„๋กœ์ ํŠธ ์„ค์ • ๋ฐ ์˜์กด์„ฑ ๊ด€๋ฆฌ: .eslintrc.cjs, package-lock.json, .gitignore, README.md, index.html ๋“ฑ.
  2. useReducer ํ•™์Šต ๋ฏธ์…˜: UseReducerPage.tsx, UseReducerCompany.tsx.
  3. ํ•ต์‹ฌ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (Zustand ๊ธฐ๋ฐ˜): ๊ธฐ์กด Redux ๊ตฌํ˜„์—์„œ Zustand๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋œ ๊ฒƒ์œผ๋กœ ๋ณด์ด๋ฉฐ, App.tsx, constant/*, page/*, store/store.ts ๋“ฑ์˜ ํŒŒ์ผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

์ด ๋ฆฌ๋ทฐ๋Š” ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์˜ ์ฃผ ๋ชฉํ‘œ๊ฐ€ "ํ•ต์‹ฌ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ธฐ๋Šฅ"์ด๋ผ๊ณ  ํŒ๋‹จํ•˜์—ฌ, Zustand ๊ตฌํ˜„์„ ์ค‘์‹ฌ์œผ๋กœ ์ƒ์„ธํ•˜๊ฒŒ ๋‹ค๋ฃจ๊ณ , useReducer ๋ฏธ์…˜ ์ฝ”๋“œ๋Š” ๋ณ„๋„์˜ ์„น์…˜์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


1. ํ”„๋กœ์ ํŠธ ์„ค์ • ๋ฐ ์˜์กด์„ฑ ๊ด€๋ฆฌ (package-lock.json, .eslintrc.cjs ๋“ฑ)

  • .eslintrc.cjs: eslint:recommended, @typescript-eslint/recommended, react-hooks/recommended ๋“ฑ ๊ธฐ๋ณธ์ ์ธ ๋ฆฐํŒ… ๊ทœ์น™ ์„ค์ •์€ ์ข‹์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ, README.md์— ์–ธ๊ธ‰๋œ ๊ฒƒ์ฒ˜๋Ÿผ plugin:@typescript-eslint/recommended-type-checked ๋˜๋Š” strict-type-checked๋กœ ํ™•์žฅํ•˜์—ฌ ํƒ€์ž… ๊ธฐ๋ฐ˜์˜ ๋” ์—„๊ฒฉํ•œ ๋ฆฐํŒ… ๊ทœ์น™์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • package-lock.json:
    • @reduxjs/toolkit, @tanstack/react-query, zustand, react-hook-form, tailwindcss, axios, date-fns, lucide-react, react-icons, react-router-dom, react-select, zod ๋“ฑ ์ตœ์‹  ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ์–ด ํ˜„๋Œ€์ ์ธ ์Šคํƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
    • ๊ฐœ์„  ํ•„์š” ์‚ฌํ•ญ (๋งค์šฐ ์ค‘์š”):
      • eslint ๋ฐ inflight ํŒจํ‚ค์ง€ Deprecated ๋ฒ„์ „: eslint (8.57.1) ๋ฐ inflight (1.0.6) ํŒจํ‚ค์ง€๊ฐ€ Deprecated ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ inflight๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์•ผ๊ธฐํ•œ๋‹ค๊ณ  ๋ช…์‹œ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ด ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์ฆ‰์‹œ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ์•ˆ์ •์ ์ธ ๋Œ€์•ˆ์œผ๋กœ ๊ต์ฒดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. eslint๋„ ์ตœ์‹  ์•ˆ์ • ๋ฒ„์ „์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
      • glob ๋ฐ rimraf Deprecated: ์ด ๋‘ ํŒจํ‚ค์ง€ ์—ญ์‹œ Deprecated๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ์œผ๋‹ˆ, ํ˜ธํ™˜ ๊ฐ€๋Šฅํ•œ ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์•ˆ์ •์„ฑ๊ณผ ๋ณด์•ˆ์„ ํ™•๋ณดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
      • react-router Major ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ (v7): react-router ๋ฐ react-router-dom์ด 7.5.0์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. v6์—์„œ v7๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ ์ƒ๋‹นํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋ผ์šฐํŒ… ๋กœ์ง ๋ฐ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ๋ฒ• ๋“ฑ์— ๋Œ€ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ๋™๋ฐ˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
      • date-fns ๋ฒ„์ „ ๋ช…ํ™•ํ™”: date-fns์˜ ๋ฒ„์ „์ด 4.1.0์œผ๋กœ ๊ธฐ๋ก๋˜์–ด ์žˆ๋Š”๋ฐ, ํ˜„์žฌ npm์˜ date-fns ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” 3.x.x ๋Œ€๊ฐ€ ์ตœ์‹  ๋ฉ”์ด์ € ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. 4.x.x ๋ฒ„์ „์ด ์˜คํƒ€์ด๊ฑฐ๋‚˜ ํŠน์ • ํฌํฌ ๋ฒ„์ „์ธ์ง€ ํ™•์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
      • ๋ณด์•ˆ ๊ฐ์‚ฌ: ๋งŽ์€ ์˜์กด์„ฑ์ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์œผ๋ฏ€๋กœ npm audit ๋˜๋Š” yarn audit์„ ์‹คํ–‰ํ•˜์—ฌ ์ž ์žฌ์ ์ธ ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ๊ฒ€ํ† ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

2. ํ•ต์‹ฌ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ธฐ๋Šฅ (Zustand ๊ธฐ๋ฐ˜) ์ฝ”๋“œ ๋ฆฌ๋ทฐ (src ๋””๋ ‰ํ† ๋ฆฌ)

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

ํŒŒ์ผ๋ณ„ ๊ตฌ์ฒด์  ํ”ผ๋“œ๋ฐฑ

  • src/App.tsx
    • ์ „๋ฐ˜: React Router๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์šฐํŒ…์„ ์„ค์ •ํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.
    • ๊ฐœ์„ ์ : ํ–ฅํ›„ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๊ฐ€ ์ถ”๊ฐ€๋  ๊ฒฝ์šฐ, Navbar๋ฅผ <Outlet>์„ ํฌํ•จํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ ๋‚ด์— ๋ฐฐ์น˜ํ•˜์—ฌ ๋” ๊ตฌ์กฐ์ ์ธ ๋ ˆ์ด์•„์›ƒ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 6 ์ฐธ๊ณ )
  • src/constant/cartitems.ts
    • ์ „๋ฐ˜: ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ดํ…œ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•˜๊ณ  CartItemType ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํƒ€์ž…์„ ๋ช…ํ™•ํžˆ ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: CartItemType ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜๊ฐ€ ๋ช…ํ™•ํ•˜๊ณ  ์ข‹์Šต๋‹ˆ๋‹ค. id ํ•„๋“œ๋ฅผ readonly๋กœ ๋ช…์‹œํ•˜์—ฌ ๋ถˆ๋ณ€์„ฑ์„ ๊ฐ•์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 7 ์ฐธ๊ณ )
    • ๋ฆฌํŒฉํ† ๋ง: constant ๋””๋ ‰ํ† ๋ฆฌ๋ณด๋‹ค๋Š” src/data/cartItems.ts ๋“ฑ์œผ๋กœ ์ด๋™ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํŒŒ์ผ์ž„์„ ๋ช…ํ™•ํžˆ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 4 ์ฐธ๊ณ )
  • src/constant/icons.tsx
    • ์ „๋ฐ˜: SVG ์•„์ด์ฝ˜์„ React ์ปดํฌ๋„ŒํŠธ๋กœ ๋ž˜ํ•‘ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๊ฐœ์„ ์ :
      • ์ ‘๊ทผ์„ฑ (Accessibility): ์•„์ด์ฝ˜๋“ค์— aria-label ๋˜๋Š” title ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ชฉ์ ์„ ์„ค๋ช…ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 2 ์ฐธ๊ณ )
      • ์ผ๊ด€์„ฑ: package.json์— lucide-react๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ, ์ง์ ‘ SVG๋ฅผ ๋งŒ๋“œ๋Š” ๋Œ€์‹  lucide-react ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ณ  ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 5 ์ฐธ๊ณ )
      • ๋ฆฌํŒฉํ† ๋ง: constant ๋””๋ ‰ํ† ๋ฆฌ๋ณด๋‹ค๋Š” src/components/icons ๋˜๋Š” src/assets/icons ๋“ฑ์œผ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 4 ์ฐธ๊ณ )
  • src/main.tsx
    • ์ „๋ฐ˜: React ์•ฑ์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ๋กœ, Zustand Provider ๋Œ€์‹  createRoot ๋ฐ StrictMode๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: document.getElementById('root') as HTMLElement์™€ ๊ฐ™์ด ํƒ€์ž… ๋‹จ์–ธ์„ ์‚ฌ์šฉํ–ˆ์œผ๋‚˜, root ์—˜๋ฆฌ๋จผํŠธ์˜ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 8 ์ฐธ๊ณ )
  • src/page/cart.tsx
    • ์„ฑ๋Šฅ/๋ฆฌํŒฉํ† ๋ง: useEffect ๋‚ด์—์„œ calculateTotals ์•ก์…˜์„ ํ˜ธ์ถœํ•˜์—ฌ amount์™€ total์„ ์žฌ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉ์‹์€ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง๊ณผ ๊ณ„์‚ฐ์„ ์•ผ๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. amount์™€ total์€ cartItems์˜ ํŒŒ์ƒ ์ƒํƒœ(derived state)์ด๋ฏ€๋กœ, Zustand ์Šคํ† ์–ด ์•ก์…˜ ๋‚ด๋ถ€์—์„œ cartItems๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 1 ์ฐธ๊ณ )
    • ์„ฑ๋Šฅ/์ตœ์ ํ™”: useStore()๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋Œ€์‹ , ํ•„์š”ํ•œ ์ƒํƒœ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊ตฌ๋…ํ•˜๋Š” selector๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 6 ์ฐธ๊ณ )
  • src/page/cartitem.tsx
    • ์ ‘๊ทผ์„ฑ (Accessibility): ChevronUp, ChevronDown ์•„์ด์ฝ˜ ๋ฒ„ํŠผ์— aria-label ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์˜ ๊ธฐ๋Šฅ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 2 ์ฐธ๊ณ )
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: React.FC ํƒ€์ž… ๋Œ€์‹  props ํƒ€์ž…์„ ์ง์ ‘ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 5 ์ฐธ๊ณ )
  • src/page/modal.tsx
    • ์ ‘๊ทผ์„ฑ (Accessibility): ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ๋Š” ํ‚ค๋ณด๋“œ ํฌ์ปค์Šค ๊ด€๋ฆฌ (ํฌ์ปค์Šค ํŠธ๋žฉ, ๋ณต์›), Esc ํ‚ค๋กœ ๋‹ซ๊ธฐ, ARIA ์†์„ฑ (aria-modal, role) ์ถ”๊ฐ€ ๋“ฑ ์ ‘๊ทผ์„ฑ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ณ ๋ ค ์‚ฌํ•ญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 3 ์ฐธ๊ณ )
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: React.FC ํƒ€์ž… ๋Œ€์‹  props ํƒ€์ž…์„ ์ง์ ‘ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 5 ์ฐธ๊ณ )
  • src/page/navbar.tsx
    • ์Šคํƒ€์ผ๋ง (Tailwind CSS): bg-[#6c63ff]์™€ ๊ฐ™์ด ํ•˜๋“œ์ฝ”๋”ฉ๋œ hex ๊ฐ’ ๋Œ€์‹ , tailwind.config.js์— ์ปค์Šคํ…€ ์ƒ‰์ƒ์„ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 4 ์ฐธ๊ณ )
    • ์ ‘๊ทผ์„ฑ (Accessibility): ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ด์ฝ˜ ์˜† amount์— aria-label์ด๋‚˜ ์ˆจ๊ฒจ์ง„ ํ…์ŠคํŠธ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์„ฑ๋Šฅ/์ตœ์ ํ™”: useStore()๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋Œ€์‹ , ํ•„์š”ํ•œ ์ƒํƒœ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊ตฌ๋…ํ•˜๋Š” selector๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 6 ์ฐธ๊ณ )
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: React.FC ํƒ€์ž… ๋Œ€์‹  props ํƒ€์ž…์„ ์ง์ ‘ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ 5 ์ฐธ๊ณ )
  • src/store/store.ts (Zustand)
    • ์ „๋ฐ˜: Zustand๋กœ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, initialState ์ฃผ์„์€ Redux์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋œ ๋งฅ๋ฝ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
    • ์„ฑ๋Šฅ/๋ฆฌํŒฉํ† ๋ง:
      • amount์™€ total ๊ณ„์‚ฐ ๋กœ์ง์ด initialState ์„ค์ • ๋ถ€๋ถ„๊ณผ calculateTotals ์•ก์…˜์— ์ค‘๋ณต๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
      • calculateTotals ์•ก์…˜์€ cartItems๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋‹ค๋ฅธ ์•ก์…˜(increase, decrease, removeItem, clearCart)์˜ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋กœ ๋ฐœ์ƒํ•˜๋Š” ํŒŒ์ƒ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์—ญํ• ์ด๋ฏ€๋กœ, ๊ฐ ์•ก์…˜ ๋‚ด์—์„œ amount์™€ total์„ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.
    • ๋กœ์ง ๋ช…ํ™•ํ™”: decrease ์•ก์…˜์—์„œ amount๊ฐ€ 0์ด ๋˜๋ฉด ์•„์ดํ…œ์„ ์ œ๊ฑฐํ•˜๋Š”๋ฐ, removeItem์ด๋ผ๋Š” ๋ณ„๋„ ์•ก์…˜์ด ์žˆ์ง€๋งŒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. decrease ๋กœ์ง์—์„œ removeItem์„ ํ™œ์šฉํ•˜๋„๋ก ์—ฐ๊ฒฐํ•˜๋ฉด ๋” ๋ช…ํ™•ํ•ด์ง‘๋‹ˆ๋‹ค.
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: StoreState ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜๊ฐ€ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.

3. useReducer ํ•™์Šต ๋ฏธ์…˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ (src/page/UseReducerPage.tsx, src/page/UseReducerCompany.tsx)

useReducer ํ›…์˜ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์„ ์ž˜ ๊ตฌํ˜„ํ–ˆ์œผ๋‚˜, ํƒ€์ž… ์•ˆ์ „์„ฑ, ์ฝ”๋“œ ํ’ˆ์งˆ, ๊ทธ๋ฆฌ๊ณ  ๋ช‡ ๊ฐ€์ง€ ๊ฐœ์„ ์ ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

  • UseReducerPage.tsx
    • TypeScript: IAction ํƒ€์ž… ๊ฐœ์„  (Discriminated Unions): payload๊ฐ€ INCREASE ์•ก์…˜์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  payload?: number;๋กœ ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. INCREASE๊ฐ€ ํ•ญ์ƒ 1๋งŒ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค๋ฉด payload๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ๋งŒ์•ฝ ์ฆ๊ฐ€๋Ÿ‰์„ ๋ฐ›์•„์•ผ ํ•œ๋‹ค๋ฉด Discriminated Unions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ payload๋ฅผ ํ•„์ˆ˜๋กœ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
    • ์ฝ”๋“œ ํ’ˆ์งˆ: console.log ์ œ๊ฑฐ: ๊ฐœ๋ฐœ ๋””๋ฒ„๊น…์šฉ console.log๊ฐ€ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฐ ์ •๋ณด ๋…ธ์ถœ์˜ ์›์ธ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ ์‹œ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฆฌํŒฉํ† ๋ง: ์ดˆ๊ธฐ ์ƒํƒœ ๋ถ„๋ฆฌ: useReducer ํ›… ๋‚ด๋ถ€์— ์ง์ ‘ ์ •์˜๋œ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์— ๋ณ„๋„ ์ƒ์ˆ˜๋กœ ์„ ์–ธํ•˜์—ฌ ๊ฐ€๋…์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • UseReducerCompany.tsx
    • TypeScript: IAction ํƒ€์ž… ๊ฐœ์„  (Discriminated Unions ๋ฐ Nullish Coalescing): CHANGE_DEPARTMENT ์•ก์…˜์˜ payload๋ฅผ ํ•„์ˆ˜๋กœ ์ง€์ •ํ•˜๊ณ , reducer ๋‚ด๋ถ€์—์„œ payload๊ฐ€ undefined๊ฐ€ ๋  ๊ฐ€๋Šฅ์„ฑ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ํƒ€์ž…์„ ๊ฐ•ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
    • CSS/Tailwind: ์ž˜๋ชป๋œ Tailwind CSS ํด๋ž˜์Šค ์ˆ˜์ •: <p className="text-red-500 font-2xl">์—์„œ font-2xl์€ ์ž˜๋ชป๋œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. text-2xl๋กœ ์ˆ˜์ •ํ•ด์•ผ ์˜๋„ํ•œ ํ…์ŠคํŠธ ํฌ๊ธฐ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ๋ณ€์ˆ˜๋ช…/ํ•จ์ˆ˜๋ช…: ์•ก์…˜ ํƒ€์ž… ๋ช…๋ช… ์ผ๊ด€์„ฑ: RESET_TO_ZERO์™€ RESET์ฒ˜๋Ÿผ ๋น„์Šทํ•œ ์˜๋ฏธ์˜ ์•ก์…˜ ํƒ€์ž…์— ๋Œ€ํ•œ ๋ช…๋ช… ๊ทœ์น™์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
    • UX/์ ‘๊ทผ์„ฑ: Input Placeholder ํ…์ŠคํŠธ ๊ฐ„๊ฒฐํ™”: input ํƒœ๊ทธ placeholder ํ…์ŠคํŠธ๊ฐ€ ๋„ˆ๋ฌด ๊ธธ๊ณ  ๊ธฐ๋Šฅ์ ์ธ ์„ค๋ช…์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. Placeholder๋Š” ๊ฐ„๊ฒฐํ•œ ํžŒํŠธ๋ฅผ ์ œ๊ณตํ•˜๊ณ , ๊ธฐ๋Šฅ ์„ค๋ช…์€ ๋ณ„๋„์˜ ์•ˆ๋‚ด๋ฌธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์— ์ข‹์Šต๋‹ˆ๋‹ค.

๊ตฌ์ฒด์  ๊ฐœ์„  ์ œ์•ˆ (์ด 8๊ฐœ)

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

  1. Zustand ์Šคํ† ์–ด ๋‚ด ํŒŒ์ƒ ์ƒํƒœ(amount, total) ์ง์ ‘ ์—…๋ฐ์ดํŠธ ๋ฐ calculateTotals ์ œ๊ฑฐ:

    • src/store/store.ts์—์„œ cartItems๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ชจ๋“  ์•ก์…˜(increase, decrease, removeItem, clearCart) ๋‚ด์—์„œ amount์™€ total์„ ์ฆ‰์‹œ ์žฌ๊ณ„์‚ฐํ•˜์—ฌ ํ•จ๊ป˜ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ์œ„ํ•ด calculateDerivedState ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ๋„์ž…ํ•˜์—ฌ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ์ œ์•ˆ 3 ์ฐธ๊ณ )
    • ์ดํ›„ src/page/cart.tsx์—์„œ useEffect ๋‚ด calculateTotals() ํ˜ธ์ถœ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    // src/store/store.ts
    // calculateDerivedState ํ—ฌํผ ํ•จ์ˆ˜ ์ •์˜ (create ํ•จ์ˆ˜ ๋‚ด๋ถ€ ๋˜๋Š” ์™ธ๋ถ€์—)
    const calculateDerivedState = (items: CartItemType[]) => ({
      amount: items.reduce((sum, item) => sum + item.amount, 0),
      total: items.reduce((sum, item) => sum + item.price * item.amount, 0),
    });
    
    export const useStore = create<StoreState>((set, get) => ({
      cartItems: cartItemsData,
      ...calculateDerivedState(cartItemsData), // ์ดˆ๊ธฐ ์ƒํƒœ ์„ค์ • ์‹œ ํ™œ์šฉ
      isModalOpen: false,
    
      increase: (id) => set((state) => {
        const updatedItems = state.cartItems.map(i => i.id === id ? { ...i, amount: i.amount + 1 } : i);
        return { cartItems: updatedItems, ...calculateDerivedState(updatedItems) };
      }),
      decrease: (id) => {
        const itemToDecrease = get().cartItems.find(item => item.id === id);
        if (itemToDecrease && itemToDecrease.amount === 1) {
          get().removeItem(id); // amount๊ฐ€ 1์ผ ๋•Œ decreaseํ•˜๋ฉด 0์ด ๋˜๋ฏ€๋กœ removeItem ํ˜ธ์ถœ
        } else {
          set((state) => {
            const updatedItems = state.cartItems.map(i => i.id === id ? { ...i, amount: i.amount - 1 } : i);
            return { cartItems: updatedItems, ...calculateDerivedState(updatedItems) };
          });
        }
      },
      removeItem: (id) => set((state) => {
        const updatedItems = state.cartItems.filter(i => i.id !== id);
        return { cartItems: updatedItems, ...calculateDerivedState(updatedItems) };
      }),
      clearCart: () => set({ cartItems: [], amount: 0, total: 0 }),
      // calculateTotals ์•ก์…˜ ์ œ๊ฑฐ
    }));
  2. UI ์ปดํฌ๋„ŒํŠธ ์ ‘๊ทผ์„ฑ(Accessibility) ๊ฐ•ํ™”:

    • ์•„์ด์ฝ˜: src/constant/icons.tsx์˜ CartIcon, ChevronUp, ChevronDown ๋ฐ src/page/cartitem.tsx์˜ ๋ฒ„ํŠผ์— aria-label ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜๋ฏธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ: <svg aria-label="์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ด์ฝ˜">, <button aria-label="์ˆ˜๋Ÿ‰ ์ฆ๊ฐ€">)
    • ๋ชจ๋‹ฌ: src/page/modal.tsx์— ๋ชจ๋‹ฌ์ด ์—ด๋ฆด ๋•Œ ํฌ์ปค์Šค ํŠธ๋žฉ ๊ตฌํ˜„, Esc ํ‚ค๋กœ ๋‹ซ๊ธฐ, ARIA ์†์„ฑ (aria-modal="true", role="dialog") ์ถ”๊ฐ€ ๋“ฑ ์ ‘๊ทผ์„ฑ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  3. Zustand useStore ํ›…์—์„œ ์„ ํƒ์ž(Selector) ์‚ฌ์šฉ ์ตœ์ ํ™”:

    • src/page/cart.tsx, src/page/navbar.tsx, src/page/modal.tsx ๋“ฑ์—์„œ useStore()๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋ชจ๋“  ์ƒํƒœ/์•ก์…˜์„ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•˜๋Š” ๋Œ€์‹ , ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‹ค์ œ๋กœ ํ•„์š”ํ•œ ์ƒํƒœ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊ตฌ๋…ํ•˜๋„๋ก selector๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
    // src/page/cart.tsx ์˜ˆ์‹œ
    const cartItems = useStore((state) => state.cartItems);
    const amount = useStore((state) => state.amount);
    const total = useStore((state) => state.total);
    const openModal = useStore((state) => state.openModal);
  4. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: useReducer ์•ก์…˜ ํƒ€์ž…์— Discriminated Unions ์ ์šฉ:

    • src/page/UseReducerPage.tsx์™€ src/page/UseReducerCompany.tsx์˜ IAction ํƒ€์ž…์„ Discriminated Unions๋กœ ์žฌ์ •์˜ํ•˜์—ฌ ๊ฐ ์•ก์…˜ ํƒ€์ž…์— ๋”ฐ๋ผ payload์˜ ์กด์žฌ ์—ฌ๋ถ€์™€ ํƒ€์ž…์„ ๋ช…ํ™•ํžˆ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
    // src/page/UseReducerPage.tsx
    type Action =
      | { type: "INCREASE"; payload?: number } // payload ์‚ฌ์šฉ ์‹œ ๋ช…ํ™•ํžˆ
      | { type: "DECREASE" }
      | { type: "RESET_TO_ZERO" };
    
    // src/page/UseReducerCompany.tsx
    type CompanyAction =
      | { type: "CHANGE_DEPARTMENT"; payload: string } // payload ํ•„์ˆ˜
      | { type: "RESET" };
  5. React.FC ํƒ€์ž… ์‚ฌ์šฉ ์žฌ๊ณ  ๋ฐ ํ‘œ์ค€ํ™”:

    • React.FC๋Š” ์•”์‹œ์ ์œผ๋กœ children prop์„ ํฌํ•จํ•˜๋Š” ๋“ฑ์˜ ํŠน์ง•์ด ์žˆ์–ด ์ตœ์‹  React + TypeScript ํ™˜๊ฒฝ์—์„œ๋Š” props ํƒ€์ž…์„ ์ง์ ‘ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ React.FC๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  props ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ํ™œ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
    // Before: const App: React.FC = () => { ... };
    const App = () => { /* ... */ };
    
    // Before: const CartItem: React.FC<CartItemType> = ({ id, ... }) => { ... };
    const CartItem = ({ id, title, singer, price, img, amount }: CartItemType) => { /* ... */ };
  6. ์ฝ”๋“œ ํ’ˆ์งˆ ๋ฐ ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ:

    • console.log ์ œ๊ฑฐ: useReducer ์ฝ”๋“œ ๋“ฑ ๊ฐœ๋ฐœ ๋””๋ฒ„๊น…์šฉ console.log ๋ฌธ์„ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์‹œ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • Tailwind CSS ์ปค์Šคํ…€ ์ƒ‰์ƒ ์‚ฌ์šฉ: src/page/navbar.tsx์—์„œ bg-[#6c63ff]์™€ ๊ฐ™์ด ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์ƒ‰์ƒ ๊ฐ’์„ tailwind.config.js์— ์ •์˜๋œ ์ปค์Šคํ…€ ์ƒ‰์ƒ (์˜ˆ: theme.extend.colors.primary)์œผ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
    • ์ผ๊ด€๋œ ์•„์ด์ฝ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ: package.json์— lucide-react๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ, src/constant/icons.tsx์—์„œ ์ง์ ‘ SVG๋ฅผ ์ •์˜ํ•˜๋Š” ๋Œ€์‹  lucide-react ์ปดํฌ๋„ŒํŠธ(์˜ˆ: ShoppingCart, ChevronUp)๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ผ๊ด€์„ฑ์„ ํ™•๋ณดํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    • constants ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ๊ฐœ์„ : src/constant/cartitems.ts๋Š” src/data/cartItems.ts๋กœ, src/constant/icons.tsx๋Š” src/components/icons/index.tsx ๋“ฑ์œผ๋กœ ์ด๋™ํ•˜์—ฌ ํŒŒ์ผ์˜ ์—ญํ• ์— ๋งž๋Š” ๋””๋ ‰ํ† ๋ฆฌ ๋ฐ ๋ช…์นญ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  7. ๋ผ์šฐํ„ฐ ๊ตฌ์กฐ ๊ฐœ์„  (Layout Component ๋„์ž…):

    • ํ–ฅํ›„ ํŽ˜์ด์ง€๊ฐ€ ๋Š˜์–ด๋‚  ๊ฒƒ์„ ๋Œ€๋น„ํ•˜์—ฌ src/App.tsx์—์„œ Navbar๋ฅผ ํฌํ•จํ•˜๋Š” Layout ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ผ์šฐํŒ… ๊ตฌ์กฐ๋ฅผ ๋” ๋ช…ํ™•ํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    // src/components/Layout.tsx (์ƒˆ ํŒŒ์ผ)
    import { Outlet } from 'react-router-dom';
    import Navbar from '../page/navbar';
    const Layout = () => (<div><Navbar /><Outlet /></div>);
    export default Layout;
    
    // src/App.tsx
    import Layout from './components/Layout';
    const App = () => (<Router><Routes><Route path="/" element={<Layout />}><Route index element={<Cart />} /></Route></Routes></Router>);
  8. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ดˆ๊ธฐํ™” ์•ˆ์ •์„ฑ ๊ฐ•ํ™”:

    • src/main.tsx์—์„œ document.getElementById('root')!์™€ ๊ฐ™์ด Non-null assertion(!)์„ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ , root ์—˜๋ฆฌ๋จผํŠธ์˜ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ™•์ธํ•˜๊ณ  ๋ Œ๋”๋งํ•˜๋„๋ก ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
    const rootElement = document.getElementById('root');
    if (rootElement) {
      createRoot(rootElement).render(<StrictMode><App /></StrictMode>);
    } else {
      console.error("ID๊ฐ€ 'root'์ธ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
    }

์ „๋ฐ˜์ ์œผ๋กœ ์ฝ”๋“œ๋Š” ์ž˜ ์ž‘์„ฑ๋˜์–ด ์žˆ๊ณ  ํ•™์Šต ๊ณผ์ •์˜ ํ”์ ์ด ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค. ์œ„ ๊ฐœ์„  ์ œ์•ˆ๋“ค์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ์„ฑ๋Šฅ, ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž ์ ‘๊ทผ์„ฑ์„ ํ•œ์ธต ๋” ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ˆ˜๊ณ  ๋งŽ์œผ์…จ์Šต๋‹ˆ๋‹ค!

Copy link
Collaborator

@woojo230 woojo230 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useReducerPage๋Š” page๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ํ›…์œผ๋กœ ๋ถ„๋ฆฌํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants