๐ŸŽฃ 05. ์‹ค๋ฌด 100% ํ™œ์šฉ๋ฒ•: ์ปค์Šคํ…€ ํ›…๊ณผ ์•ˆํ‹ฐํŒจํ„ด

๐Ÿ“‹ ๊ฐœ์š”

Zustand ์Šคํ† ์–ด๋ฅผ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์™€ ๊ฒฐํ•ฉํ•  ๋•Œ ์ง€์ผœ์•ผ ํ•  ์ปค์Šคํ…€ ํ›… ์ถ”์ƒํ™” ํŒจํ„ด๊ณผ ์น˜๋ช…์ ์ธ ๋™๊ธฐํ™” ์•ˆํ‹ฐํŒจํ„ด

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ์Šคํ† ์–ด ํ›…(useStore)์„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์ปค์Šคํ…€ ํ›…์œผ๋กœ ์บก์Аํ™”(Encapsulation)ํ•˜๋Š” ์ด์œ ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒํƒœ(State) ๊ตฌ๋… ์—†์ด ์•ก์…˜(Action) ํ•จ์ˆ˜๋งŒ ์ถ”์ถœํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ์™„์ „ํžˆ ์ฐจ๋‹จํ•˜๋Š” ํŒจํ„ด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • useEffect๋ฅผ ์‚ฌ์šฉํ•ด React ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๋‚˜ Props๋ฅผ Zustand์— ๊ฐ•์ œ ๋™๊ธฐํ™”ํ•˜๋ ค๋Š” ํ–‰์œ„๊ฐ€ ์™œ ์ตœ์•…์˜ ์•ˆํ‹ฐํŒจํ„ด์ธ์ง€ ์ดํ•ดํ•œ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 10๋ถ„ / ํ•ต์‹ฌ ํŒŒํŠธ: 6๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
์ธ๋ผ์ธ ์…€๋ ‰ํ„ฐ์˜ ๋ฌธ์ œ์  โ†’ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ์ถ”์ƒํ™” โ†’ ํŒŒ์ƒ ๋กœ์ง ์€๋‹‰ โ†’ ์•ก์…˜ ๋ถ„๋ฆฌ โ†’ useEffect ๋™๊ธฐํ™” ์•ˆํ‹ฐํŒจํ„ด ๊ฒฝ๊ณ 

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "๋ฆฌ๋“œ ๋‹˜! Zustand ์ •๋ง ํŽธํ•ด์š”. ์š”์ฆ˜์—” ์ œ๊ฐ€ ์ปดํฌ๋„ŒํŠธ ์งค ๋•Œ๋งˆ๋‹ค ๋ฌด์กฐ๊ฑด ํŒŒ์ผ ๋งจ ์œ„์— const user = useAppStore(state => state.user.profile) ์ด๋ ‡๊ฒŒ ์…€๋ ‰ํ„ฐ๋ฅผ ์งœ๋„ฃ๊ณ  ์‹œ์ž‘ํ•ด์š”. ๊ทผ๋ฐ ์œ ์ € ์Šคํ† ์–ด ๊ตฌ์กฐ๊ฐ€ ํ•œ ๋ฒˆ ๋ฐ”๋€Œ๋‹ˆ๊นŒ ํ•œ 50๊ฐœ ํŒŒ์ผ ์ฐพ์•„๋‹ค๋‹ˆ๋ฉด์„œ ์…€๋ ‰ํ„ฐ๋ฅผ ๋‹ค ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๊ฑฐ ๋งž๋‚˜์š”?"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, Zustand๊ฐ€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์—†๋‹ค๊ณ  ํ•ด์„œ ์ถ”์ƒํ™”(Abstraction) ๊นŒ์ง€ ์ƒ๋žตํ•˜๋ผ๋Š” ๋œป์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์Šคํ† ์–ด์˜ ๋‚ด๋ถ€ ๊นŠ์€ ๊ตฌ์กฐ(Depth)๋ฅผ ๋„ˆ๋ฌด ํ›คํžˆ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’์•„์ง‘๋‹ˆ๋‹ค. ๊ธ€๋กœ๋ฒŒ ์Šคํ† ์–ด๋ฅผ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ์˜ˆ์˜๊ฒŒ ํฌ์žฅํ•˜๋Š” ์‹ค๋ฌด ํ…Œํฌ๋‹‰์„ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

Zustand ๋ฌธ์„œ๋ฅผ ์—ด๋ฉด ๊ฐ€์žฅ ๋จผ์ € ๋‚˜์˜ค๋Š” ์ฝ”๋“œ๋Š” const bears = useStore((state) => state.bears) ์ž…๋‹ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ง์ ‘ ์Šคํ† ์–ด ์ „์ฒด ์ƒํƒœ ํŠธ๋ฆฌ์— ์ ‘๊ทผํ•˜์—ฌ ์›ํ•˜๋Š” ์กฐ๊ฐ์„ ๋–ผ์–ด์˜ค๋Š”(Selecting) ๋ฐฉ์‹์€ ๋น ๋ฅด๊ณ  ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค๋ฌด ํ™˜๊ฒฝ์—์„œ ํ”„๋กœ๋•ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด ๋ฌธ์ œ์ ๋“ค์ด ์†์ถœํ•ฉ๋‹ˆ๋‹ค.

  1. ์ค‘๋ณต๊ณผ ํŒŒํŽธํ™”: ์ˆ˜๋งŽ์€ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋™์ผํ•œ Selector ์ฝœ๋ฐฑ์ด ๋ณต์‚ฌ-๋ถ™์—ฌ๋„ฃ๊ธฐ ๋ฉ๋‹ˆ๋‹ค.
  2. ๋ฆฌํŒฉํ† ๋ง ์ง€์˜ฅ: ์Šคํ† ์–ด ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ํ•œ ๋ฒˆ ๋ณ€๊ฒฝ(state.user -> state.auth.user)ํ•˜๋ฉด, ํ•ด๋‹น ์Šคํ† ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ˆ˜์‹ญ ๊ฐœ์˜ ํŒŒ์ผ์—์„œ ์ „๋ถ€ ์—๋Ÿฌ๊ฐ€ ํ„ฐ์ง‘๋‹ˆ๋‹ค.
  3. ๋ Œ๋”๋ง ์ง€๋ขฐ๋ฐญ: ์•ก์…˜(ํ•จ์ˆ˜) ํ•˜๋‚˜๋งŒ ํ•„์š”ํ•œ๋ฐ ์Šคํ† ์–ด ๊ป๋ฐ๊ธฐ๋ฅผ ์ž˜๋ชป ๊ตฌ๋…ํ•˜์—ฌ ๋ฌดํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋‚ฉ๋‹ˆ๋‹ค (์ด์ „ ๋ Œ๋”๋ง ์ตœ์ ํ™” ์ฑ•ํ„ฐ ์ฐธ๊ณ ).

์ž˜ ์งœ์ธ ์•„ํ‚คํ…์ฒ˜๋Š” "์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋Š”์ง€"๋ฅผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ชจ๋ฅด๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. Zustand ์Šคํ† ์–ด๋ฅผ ์ปค์Šคํ…€ ํ›…(Custom Hook) ์ด๋ผ๋Š” ์™ธํˆฌ๋กœ ์ž…ํ˜€์„œ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ—๏ธ 1. ์Šคํ† ์–ด ์บก์Аํ™”: ์ปค์Šคํ…€ ํ›… (Custom Hooks)

์˜์ฒ ์ด๊ฐ€ ์งœ๋†“์€ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

โŒ ์ธ๋ผ์ธ ์…€๋ ‰ํ„ฐ ๋‚จ์šฉ

// components/UserProfile.tsx
import { useAuthStore } from '@/store/authStore';
 
export function UserProfile() {
  // ๐Ÿšจ ์˜์ฒ : "์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ์Šคํ† ์–ด ์•ˆ์ชฝ์„ ์ง์ ‘ ๋‹ค ๋’ค์ ธ์„œ ๊บผ๋‚ด์™€์•ผ์ง€!"
  const userName = useAuthStore(state => state.user.profile.name);
  const avatar = useAuthStore(state => state.user.profile.avatarUrl);
  // ...
}

โœ… ์ปค์Šคํ…€ ํ›…์œผ๋กœ ์€๋‹‰ (Encapsulation)

์Šคํ† ์–ด๋ฅผ ์ •์˜ํ•œ ํŒŒ์ผ(authStore.ts) ๋งจ ๋‹จ์—, ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ์ปค์Šคํ…€ ํ›…๋“ค์„ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์„œ export ํ•ฉ๋‹ˆ๋‹ค.

// store/authStore.ts
import { create } from 'zustand';
 
const useAuthStore = create(...)
 
// ๐Ÿฆ ์˜ํ˜ธ: "์ปดํฌ๋„ŒํŠธํ•œํ…Œ๋Š” ์Šคํ† ์–ด ์Œฉ์–ผ(useAuthStore)์„ ๋ณด์—ฌ์ฃผ์ง€ ๋งˆ์„ธ์š”. ์˜ˆ์˜๊ฒŒ ํฌ์žฅํ•ด์„œ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค."
export const useUserName = () => useAuthStore(state => state.user.profile.name);
export const useUserAvatar = () => useAuthStore(state => state.user.profile.avatarUrl);
export const useIsLoggedIn = () => useAuthStore(state => !!state.accessToken);

์ด์ œ ์ปดํฌ๋„ŒํŠธ์˜ ์ฝ”๋“œ๊ฐ€ ์••๋„์ ์œผ๋กœ ์šฐ์•„ํ•ด์ง‘๋‹ˆ๋‹ค.

// components/UserProfile.tsx
import { useUserName, useUserAvatar, useIsLoggedIn } from '@/store/authStore';
 
export function UserProfile() {
  const isLoggedIn = useIsLoggedIn();
  const name = useUserName();
  const avatar = useUserAvatar();
  
  // ๐Ÿ’ก ์ปดํฌ๋„ŒํŠธ๋Š” ์ด๊ฒŒ Zustand์—์„œ ์˜ค๋Š”์ง€, Context์—์„œ ์˜ค๋Š”์ง€ ์•Œ ํ•„์š”๋„ ์—†์Šต๋‹ˆ๋‹ค!
}

์œ ์ง€๋ณด์ˆ˜์˜ ๋งˆ๋ฒ•: ํ›—๋‚  ์Šคํ† ์–ด ๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹จ ํ•œ ์ค„๋„ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์˜ค์ง authStore.ts ํŒŒ์ผ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ์ปค์Šคํ…€ ํ›…์˜ Selector ๋ฆฌํ„ด๊ฐ’๋งŒ ์ˆ˜์ •ํ•ด์ฃผ๋ฉด ์•ฑ ์ „์ฒด์— ๋ฐฉ์–ด์„ ์ด ์ณ์ง‘๋‹ˆ๋‹ค.


๐Ÿ’Ž 2. ํŒŒ์ƒ ์ƒํƒœ(Derived State) ๋กœ์ง ๊ฐ์ถ”๊ธฐ

์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ด์•ก์ด๋‚˜ ํ•„ํ„ฐ๋ง๋œ ๊ฒŒ์‹œ๊ธ€์ฒ˜๋Ÿผ ๊ณ„์‚ฐ์ด ํ•„์š”ํ•œ ๊ฐ’(ํŒŒ์ƒ ์ƒํƒœ)์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋„ ์ปค์Šคํ…€ ํ›…์ด ๋น›์„ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

// โŒ ์ปดํฌ๋„ŒํŠธ ๋‹จ์—์„œ ์—ฐ์‚ฐ (๋ฌด๊ฑฐ์šด ํ›…)
function CartSummary() {
  const items = useCartStore(state => state.items);
  const total = items.reduce((sum, i) => sum + i.price, 0); // ๋ Œ๋”๋ง๋งˆ๋‹ค ๊ณ„์‚ฐ
}
 
// โœ… ์Šคํ† ์–ด ๋‹จ์—์„œ Selector๋กœ ์—ฐ์‚ฐ ์€๋‹‰
export const useCartTotal = () => useCartStore(state => 
  state.items.reduce((sum, item) => sum + item.price, 0)
);
 
function CartSummaryPro() {
  // ๐Ÿฆ ์˜ํ˜ธ: "์ปดํฌ๋„ŒํŠธ๋Š” ๋ทฐ(View)๋งŒ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ๊ณ„์‚ฐ์‹์€ ์Šคํ† ์–ด๋กœ ๋ฐ€์–ด ๋„ฃ์œผ์„ธ์š”."
  const total = useCartTotal(); 
}

๐Ÿš€ 3. ๊ตฌ๋… ๋Š๊ธฐ: Action ์ „์šฉ Hook

๊ฐ€์žฅ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜๋Š” ๋ Œ๋”๋ง ์ตœ์ ํ™” ์‹คํŒจ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค. ๊ทธ์ € ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ ์ž‘๋™ํ•  '๋ฉ”์„œ๋“œ(ํ•จ์ˆ˜)' ํ•˜๋‚˜๋งŒ ํ•„์š”ํ•œ๋ฐ ๋ฌด์‹ฌ๊ฒฐ์— ์Šคํ† ์–ด๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

// โŒ ์˜์ฒ ์ด์˜ ๋ฌดํ•œ ๋ Œ๋”๋ฒ„ํŠผ
function LogoutButton() {
  // ๐Ÿšจ Zustand์˜ ๋ชจ๋“  ์ƒํƒœ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋ฐ”๋€Œ๋ฉด ์ด ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ๋ Œ๋”๋ง๋จ
  const { logout } = useAuthStore(); 
  return <button onClick={logout}>๋กœ๊ทธ์•„์›ƒ</button>
}

ํ•จ์ˆ˜๋„ ์—ญ์‹œ Selector๋กœ ํ•˜๋‚˜์”ฉ ์งš์–ด์™€์•ผ ํ•˜์ง€๋งŒ, ์•ก์…˜์ด ๋งŽ์•„์ง€๋ฉด ๋ฐ˜๋ณต ๋…ธ๊ฐ€๋‹ค๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ 5๋…„ ์ฐจ๋“ค์€ Actions ๊ฐ์ฒด๋ฅผ ๋ฌถ์–ด์„œ ๋นผ๋ƒ…๋‹ˆ๋‹ค.

// store/authStore.ts
const useAuthStore = create((set) => ({
  user: null,
  // ๐Ÿ’ก ์ƒํƒœ(State)์™€ ์•ก์…˜(Action) ํ•จ์ˆ˜ ๊ฐ์ฒด๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ށ์Šค๋ฅผ ๋‚˜๋ˆ  ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค
  actions: {
    login: () => set(...),
    logout: () => set(...),
  }
}));
 
// Action๋งŒ ์™ ๋นผ๊ฐ€๋Š” ์ „์šฉ ํ›…
export const useAuthActions = () => useAuthStore(state => state.actions);
// components/LogoutButton.tsx
import { useAuthActions } from '@/store/authStore';
 
function LogoutButtonPro() {
  // โœ… ์™„๋ฒฝ ์ฐจ๋‹จ! ์ƒํƒœ๊ฐ’์ด ์ฒœ ๋ฒˆ ๋ณ€ํ•ด๋„ ์ด ๋ฒ„ํŠผ์€ ์ดˆ๊ธฐ 1ํšŒ๋งŒ ๋ Œ๋”๋ง๋˜๊ณ  ๋๋‚ฉ๋‹ˆ๋‹ค.
  const { logout } = useAuthActions(); 
  return <button onClick={logout}>๋กœ๊ทธ์•„์›ƒ</button>
}

โ˜ ๏ธ 4. ์ตœ์•…์˜ ์•ˆํ‹ฐํŒจํ„ด: useEffect๋กœ ๋™๊ธฐํ™”ํ•˜๊ธฐ

Zustand ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๊ฐ€์žฅ ๊ฒฝ๊ณ ํ•˜๋Š” ์น˜๋ช…์ ์ธ ์•ˆํ‹ฐํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ›์€ props๋‚˜ ๋กœ์ปฌ useState ๊ฐ’์„ Zustand ๊ธ€๋กœ๋ฒŒ ์Šคํ† ์–ด์— ์‘ค์…” ๋„ฃ๊ธฐ ์œ„ํ•ด useEffect๋ฅผ ๋‚จ์šฉํ•˜๋Š” ํ–‰์œ„์ž…๋‹ˆ๋‹ค.

โŒ ์˜์ฒ ์ด์˜ ํญ๋ฐœํ•˜๋Š” ํƒ€์ด๋จธ

// ๐Ÿฃ ์˜์ฒ : "๊ฒŒ์‹œ๊ธ€ ID Props๋ฅผ ๋ฐ›์•˜์œผ๋‹ˆ Zustand ์Šคํ† ์–ด์— ๋„ฃ์–ด๋‘ฌ์•ผ ๋‹ค๋ฅธ ์• ๋“ค๋„ ์ด ID๋ฅผ ์•Œ์ง€!"
function PostDetail({ postId }: { postId: string }) {
  const setPostId = useAppStore(state => state.setPostId);
 
  // ๐Ÿšจ ์ตœ์•…์˜ ์•ˆํ‹ฐํŒจํ„ด
  useEffect(() => {
    setPostId(postId); // ๋ Œ๋”๋ง -> Effect ๋ฐœ์ƒ -> ์ƒํƒœ ๋ณ€๊ฒฝ -> ์žฌ๋ Œ๋”๋ง ๋ฌดํ•œ ๋ฃจํ”„ ์œ„ํ—˜!
  }, [postId, setPostId]);
 
  return <div>...</div>
}

์ด ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๋‚ณ์Šต๋‹ˆ๋‹ค.

  1. React์˜ ๋ฐ์ดํ„ฐ ๋‹จ๋ฐฉํ–ฅ ํ๋ฆ„(Top-Down)์„ ์ •๋ฉด์œผ๋กœ ์œ„๋ฐฐํ•ฉ๋‹ˆ๋‹ค.
  2. ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ์งํ›„(Effect) ์ „์—ญ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆํ•„์š”ํ•œ ์—ฐ์‡„ ๋ฆฌ๋ Œ๋”๋ง ํญํ’(Waterfall)์„ ์ผ์œผํ‚ต๋‹ˆ๋‹ค.
  3. ์ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ(Unmount) ๋  ๋•Œ ์Šคํ† ์–ด์˜ postId๋ฅผ ๋ˆ„๊ฐ€ ์ฒญ์†Œํ•  ๊ฑด๊ฐ€์š”? ์“ฐ๋ ˆ๊ธฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์Šต๋‹ˆ๋‹ค.

๐Ÿฆ ์˜ํ˜ธ์˜ ์ง€์˜ฅ ํƒˆ์ถœ ์†”๋ฃจ์…˜:
"Props๋กœ ๋‚ด๋ ค์˜จ ๋ฐ์ดํ„ฐ๋Š” ๊ทธ๋ƒฅ Props๋กœ ์“ฐ๊ฑฐ๋‚˜, ํ•˜์œ„ ํŠธ๋ฆฌ์—์„œ๋งŒ ๊ณต์œ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๊ฑฐ์ฐฝํ•œ Zustand ๋Œ€์‹  React Context API๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๋ ค์ฃผ์„ธ์š”. ์ • ์ „์—ญ ์Šคํ† ์–ด๋ฅผ ์ฐŒ๋ฅด๊ณ  ์‹ถ๋‹ค๋ฉด ๋ Œ๋”๋ง ์ค‘(useEffect)์ด ์•„๋‹Œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(๋ฒ„ํŠผ ํด๋ฆญ, ํŽ˜์ด์ง€ ์ด๋™ ์•ก์…˜ ๋‚ด๋ถ€) ์—์„œ ์Šคํ† ์–ด๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. Zustand ์Šคํ† ์–ด๋ฅผ ํ”„๋กœ์ ํŠธ ๋‚ด ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์ ธ๋‹ค ์“ธ ๋•Œ ๊ฐ€์žฅ ๊ถŒ์žฅ๋˜๋Š” ์ถ”์ƒํ™” ํŒจํ„ด์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

  • A) ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—์„œ useStore๋ฅผ ํ†ต์งธ๋กœ ์ž„ํฌํŠธํ•˜์—ฌ ์ธ๋ผ์ธ ์ฝœ๋ฐฑ์œผ๋กœ ์ƒํƒœ๋ฅผ ๊บผ๋‚ด ์“ด๋‹ค.
  • B) ์Šคํ† ์–ด ์ •์˜ ํŒŒ์ผ ํ•˜๋‹จ์— ์ „์šฉ ์ปค์Šคํ…€ ํ›…(useUser, useAuthActions)์„ ๋งŒ๋“ค์–ด ๊ทธ๊ฒƒ๋งŒ export ํ•œ๋‹ค.
  • C) Context API๋กœ ํ•œ ๋ฒˆ ๋” ๊ฐ์‹ธ์„œ ๋‚ด๋ ค ๋ณด๋‚ธ๋‹ค.
  • D) ์ „์—ญ ๊ฐ์ฒด์ธ Window์— ์Šคํ† ์–ด๋ฅผ ๋งˆ์šดํŠธํ•˜์—ฌ ์ง์ ‘ ์ฐธ์กฐํ•œ๋‹ค.

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์Šคํ† ์–ด ํ›…์„ ์€๋‹‰(Encapsulation)ํ•˜์—ฌ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋ฐฐํฌํ•˜๋ฉด, ์ปดํฌ๋„ŒํŠธ๋Š” ์ „์—ญ ์ƒํƒœ์˜ ๋‚ด๋ถ€ ๋ށ์Šค์™€ ๊ตฌ์กฐ์— ์˜์กดํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์Šคํ† ์–ด ํ˜•ํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ์ปค์Šคํ…€ ํ›… ๋‹จ 1๊ณณ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๊ทน๋Œ€ํ™”๋ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๋งค๋ฒˆ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ณด๋”ฐ๋ฆฌ(์Šคํ† ์–ด)๋ฅผ ์ง์ ‘ ํ’€์–ด์„œ ํ—ค์ง‘์–ด ๋†“์ง€ ๋งˆ์„ธ์š”. ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ฑด๋„ค์ฃผ๋Š” ์ง์›(์ปค์Šคํ…€ ํ›…)์„ ๊ณ ์šฉํ•ฉ์‹œ๋‹ค."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๐Ÿฆ ์˜ํ˜ธ: "์Šคํ† ์–ด๋Š” ์ˆจ๊ธฐ๊ณ , ์ปค์Šคํ…€ ํ›…์œผ๋กœ๋งŒ ์†Œํ†ตํ•˜๋ผ."

Q2. ํ•จ์ˆ˜(Action) ํ•˜๋‚˜๋งŒ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ˜ธ์ถœํ•˜๋ ค๊ณ  ์Šคํ† ์–ด๋ฅผ ์ฐธ์กฐํ–ˆ์„ ๋•Œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ฆฌ๋ Œ๋”๋ง์ด ์ž์ฃผ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, ๊ฐ€์žฅ ์˜์‹ฌํ•ด์•ผ ํ•˜๋Š” ํŒจํ„ด์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

  • A) useStore(state => state) ์ฒ˜๋Ÿผ ์Šคํ† ์–ด ์ „์ฒด๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์•„ ๋น„๊ตฌ์กฐํ™” ํ• ๋‹น(Destructuring)์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๊บผ๋ƒˆ๋‹ค.
  • B) ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ ๋‚ด๋ถ€์—์„œ get() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.
  • C) ์ปดํฌ๋„ŒํŠธ์— React.memo๋ฅผ ์”Œ์šฐ์ง€ ์•Š์•˜๋‹ค.
  • D) useStore(state => state.logout) ๊ณผ ๊ฐ™์ด ํ•จ์ˆ˜ ํ•˜๋‚˜๋งŒ ์ฐ์–ด์„œ ๊ฐ€์ ธ์™”๋‹ค.

โœ… ์ •๋‹ต: A

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: const { logout } = useStore() ์ฒ˜๋Ÿผ ์Šคํ† ์–ด๋ฅผ ํ†ต์งธ๋กœ ๊ตฌ๋…ํ•ด๋ฒ„๋ฆฌ๋ฉด, ์Šคํ† ์–ด ๋‚ด๋ถ€์˜ count๋‚˜ user ๋“ฑ ์–ด๋–ค ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ๊ฐ์ฒด ์ฐธ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜์–ด ์ปดํฌ๋„ŒํŠธ ์ „์ฒด๊ฐ€ ๋‹ค์‹œ ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ์ธก๋ฉด์—์„œ ์ตœ์•…์˜ ์•ˆํ‹ฐํŒจํ„ด ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น ํŽธํ•˜๋‹ค๊ณ  ๋ง‰ ์“ฐ๋ฉด ๋ฆฌ๋ Œ๋”๋ง ํญํƒ„ ๋งž์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ํƒ€๊ฒŸ๋ฌผ๋งŒ ์กฐ์ค€๊ฒฝ์œผ๋กœ ์ •ํ™•ํ•˜๊ฒŒ ์ฐ์œผ์„ธ์š”!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๐Ÿฆ ์˜ํ˜ธ: "ํ•จ์ˆ˜ ํ•˜๋‚˜ ์“ฐ๋ ค๋‹ค ์Šคํ† ์–ด ์ „์ฒด๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๊ฒƒ์€ ๋ฐฐ๊ฐ€ ๊ณ ํ”„๋‹ค๊ณ  ์‹๋ฃŒํ’ˆ ์ฐฝ๊ณ ์งธ ์งŠ์–ด์ง€๋Š” ๊ผด์ž…๋‹ˆ๋‹ค."

Q3. ๐Ÿ‹๏ธโ€โ™‚๏ธ ์˜์ฒ ์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„ (Q&A)

์˜์ฒ ์ด๊ฐ€ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ useAppStore(state => state.theme === 'dark') ๋ฅผ ๋ณต๋ถ™ํ•ด์„œ ์“ฐ๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌ๋ทฐ์—์„œ ์ด๋ฅผ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋นผ๋ผ๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์•˜๋Š”๋ฐ, ์–ด์ฐจํ”ผ ํ•œ ์ค„์งœ๋ฆฌ ์ฝ”๋“œ์ธ๋ฐ ๊ตณ์ด ๋นผ์•ผ ํ• ๊นŒ์š”?

โœ… ์ •๋‹ต: ๋„ค, ๋ฐ˜๋“œ์‹œ ๋นผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. export const useIsDarkMode = () => useAppStore(state => state.theme === 'dark'); ์ฒ˜๋Ÿผ ๋ชจ๋“ˆํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์ง€๊ธˆ ๋‹น์žฅ์€ ํ•œ ์ค„์ด์ง€๋งŒ, ์ถ”ํ›„์— ๋‹คํฌ๋ชจ๋“œ ํŒ๋ณ„ ๋กœ์ง์ด state.theme === 'dark' || state.systemTheme === 'dark' ์ฒ˜๋Ÿผ ๋ณต์žกํ•ด์ง€๋ฉด ํŒŒ์ผ 100๊ฐœ๋ฅผ ์ผ์ผ์ด ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ปค์Šคํ…€ ํ›…์€ ๋ณ€๊ฒฝ์— ๋Œ€ํ•œ ์œ ์ผ๋ฌด์ดํ•œ ๋‹จ์ผ ์ง€์ (Single Point of Truth)์„ ๋งŒ๋“ค์–ด ๋ฐฉ์–ด๋ฒฝ์„ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๋ณต๋ถ™(Copy&Paste)์€ ์ฃ„์•…์ž…๋‹ˆ๋‹ค! ์†๊ฐ€๋ฝ์ด ์กฐ๊ธˆ ํŽธํ•˜์ž๊ณ  ๋ฏธ๋ž˜์˜ ๋‚ด ๋ชฉ์„ ์กฐ๋ฅด์ง€ ๋งˆ์„ธ์š”!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๐Ÿฆ ์˜ํ˜ธ: "์ปดํฌ๋„ŒํŠธ๋Š” ์Šคํ† ์–ด์˜ ์ด๋ฆ„์„ ๋ชฐ๋ผ์•ผ ํ•œ๋‹ค. ์˜ค์ง ์ปค์Šคํ…€ ํ›…์ด๋ผ๋Š” ์›จ์ดํ„ฐ๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์ฃผ๋ฌธํ•˜๋ผ."

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ '๋™์ž‘์€ ํ•˜๋Š”' ์ฝ”๋“œ๊ฐ€ ์™œ '๊ตฌ๋ฆฐ' ์ฝ”๋“œ์ธ์ง€ ๋ผˆ์ €๋ฆฌ๊ฒŒ ๋А๊ผˆ๋‹ค. ๋‚˜๋ฆ„ ์ƒํƒœ๊ด€๋ฆฌ ํˆด ์“ด๋‹ค๊ณ  ์—ฌ๊ธฐ์ €๊ธฐ ์Šคํ† ์–ด๋ฅผ ํ˜ธ์ถœํ•ด๋Œ”๋Š”๋ฐ, ๊ทธ๊ฒŒ ๊ฒฐ๊ตญ ์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ ๋ณ€์ข…์ผ ๋ฟ์ด์—ˆ๋‹ค๋Š” ๋ฆฌ๋“œ ๋‹˜์˜ ํŒฉํญ์— ํ•  ๋ง์ด ์—†์—ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ์ƒ๋‹จ์— const { isDark, userName, logout } = useAuthStore() ๋ผ๊ณ  ์˜ˆ์˜๊ฒŒ ํ›…์œผ๋กœ ๋ฝ‘์•„ ์“ฐ๊ณ  ๋‚˜๋‹ˆ ์ฝ”๋“œ๊ฐ€ ๊ฐ‘์ž๊ธฐ ์‹œ๋‹ˆ์–ด์˜ ํ–ฅ๊ธฐ๋ฅผ ํ’๊ธฐ๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  useEffect ์•ˆ์— setํ•จ์ˆ˜ ๋„ฃ์—ˆ๋‹ค๊ฐ€ ์•ฑ ๋ฌดํ•œ๋ฃจํ”„ ๋‚˜์„œ ์ปดํ“จํ„ฐ ํ„ฐ์งˆ ๋ป”ํ–ˆ๋˜ ํŠธ๋ผ์šฐ๋งˆ๋„ ์ด์ฐธ์— ๊ณ ์ณ์•ผ๊ฒ ๋‹ค. Zustand... ์•Œ๋ฉด ์•Œ์ˆ˜๋ก ๊น๊นํ•˜์ง€๋งŒ ์ง„์งœ ๋งค๋ ฅ ์žˆ๋Š” ๋…€์„์ด๋‹ค.

๐Ÿ’ก "์ „์—ญ ์Šคํ† ์–ด๋Š” ์†์ด ํ›คํžˆ ๋ณด์ด๋Š” ์–ดํ•ญ์ด ์•„๋‹ˆ๋‹ค. ์˜ˆ์œ ํฌ์žฅ์ง€(Custom Hook)๋กœ ๊ฐ์‹ธ๊ณ  ์•ก์…˜๊ณผ ์ƒํƒœ๋ฅผ ๋ถ„๋ฆฌํ•ด ์ปดํฌ๋„ŒํŠธ์— ๋ฐฐ๋‹ฌํ•˜์ž."