๐Ÿ• 03. ๋‹ค์ค‘ ์Šคํ† ์–ด vs ์Šฌ๋ผ์ด์Šค ํŒจํ„ด: ์Šคํ† ์–ด๋ฅผ ์กฐ๋ฆฝ์‹์œผ๋กœ ์ชผ๊ฐœ๊ธฐ

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

๊ฑฐ๋Œ€ํ•ด์ง„ Zustand ์Šคํ† ์–ด๋ฅผ ๊ธฐ๋Šฅ๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ์กฐ๋ฆฝํ•˜๋Š” ๋งˆ์ดํฌ๋กœ ์•„ํ‚คํ…์ฒ˜

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

  • ๋น„๋Œ€ํ•ด์ง„ ์Šคํ† ์–ด๋ฅผ ๊ด€์‹ฌ์‚ฌ(๋„๋ฉ”์ธ)๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ์กฐ๊ฐ(Slice) ํŒจํ„ด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ Store๋ฅผ ๋‚จ๋ฐœํ–ˆ์„ ๋•Œ์˜ ์น˜๋ช…์  ๋‹จ์ (์ข€๋น„ ์ƒํƒœ)์„ ์ดํ•ดํ•œ๋‹ค.
  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ Slice๋ฅผ ํ•˜๋‚˜๋กœ ๊ฒฐํ•ฉํ•˜๊ณ , Slice ๊ฐ„์— ์ƒํƒœ๋ฅผ ๊ต์ฐจ ์ฐธ์กฐ(ํ†ต์‹ )ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šด๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


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

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

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
์Šคํ† ์–ด ๋น„๋Œ€ํ™” ์›์ธ ํŒŒ์•… โ†’ ๋‹ค์ค‘ ์Šคํ† ์–ด ํŒจํ„ด์˜ ์‹คํŒจ ๊ตฌ๊ฒฝ ํŒ์ฝ˜ ๋œฏ๊ธฐ โ†’ ์Šฌ๋ผ์ด์Šค ๋ถ„๋ฆฌ ๋ฐ ๊ฒฐํ•ฉ โ†’ ๊ต์ฐจ ์ฐธ์กฐ ํ•ด๊ฒฐ๋ฒ•

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

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜, ์–ด์ œ ๋ง์”€ํ•˜์‹  ๋Œ€๋กœ ์Šคํ† ์–ด ๋ށ์Šค๋ฅผ ๊นŠ๊ฒŒ ์•ˆ ํŒŒ๋ ค๊ณ  ๋…ธ๋ ฅ ์ค‘์ธ๋ฐ์š”. ๊ทธ๋Ÿฌ๋‹ค ๋ณด๋‹ˆ๊นŒ ์ „์ฒด ์ƒํƒœ๋ฅผ ๋“ค๊ณ  ์žˆ๋Š” useAppStore.ts ํŒŒ์ผ์ด 500์ค„์ด ๋„˜์–ด๊ฐ”์–ด์š”! ์ด๊ฑฐ ๋„๋ฉ”์ธ๋ณ„๋กœ ํŒŒ์ผ ๋ถ„๋ฆฌํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์€๋ฐ... ๊ทธ๋ƒฅ ์Šคํ† ์–ด(create)๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋”ฐ๋กœ ๋งŒ๋“ค๋ฉด ์•ˆ ๋ ๊นŒ์š”?"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ์Šคํ† ์–ด ํŒŒ์ผ์ด ๋ฌด๊ฑฐ์›Œ์ง€๋Š” ๊ฑด ์ข‹์€ ์‹ ํ˜ธ(?)์ž…๋‹ˆ๋‹ค. ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ์ค‘์•™์ง‘์ค‘ํ™”๋˜๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ˆ๊นŒ์š”. ํ•˜์ง€๋งŒ ๋ง์”€ํ•˜์‹  ๋Œ€๋กœ ์ด๋Œ€๋กœ ๋‘๋ฉด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ๋ฌด์ž‘์ • Store๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ์ฐข์–ด๋ฐœ๊ธฐ๋ฉด ์ง„์งœ ์ง€์˜ฅ์ด ํŽผ์ณ์ง‘๋‹ˆ๋‹ค. ์˜ค๋Š˜์€ ์ƒํƒœ๋ฅผ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์ชผ๊ฐœ๋˜, ๊ฐœ๋…์ ์œผ๋กœ๋Š” ํ•˜๋‚˜๋กœ ๋ฌถ์–ด๋‚ด๋Š” '์Šฌ๋ผ์ด์Šค ํŒจํ„ด(Slice Pattern)'์„ ๋ฐฐ์šธ ์ฐจ๋ก€๊ตฐ์š”."

๐Ÿค” ๋„์ž…: ์Šคํ† ์–ด๊ฐ€ ๋„ˆ๋ฌด ๋šฑ๋šฑํ•ด์กŒ์–ด์š”!

Zustand๋กœ ์•ฑ์„ ๊ตฌ์ถ•ํ•˜๋‹ค ๋ณด๋ฉด, ํ•˜๋‚˜์˜ ํŒŒ์ผ ์•ˆ์— ์ธ์ฆ(Auth), ์žฅ๋ฐ”๊ตฌ๋‹ˆ(Cart), UI ํ…Œ๋งˆ(Theme) ๋“ฑ ์˜จ๊ฐ– ์ƒํƒœ๊ฐ€ ๋‹ค ์„ž์ด๋Š” ์ˆœ๊ฐ„์ด ์˜ต๋‹ˆ๋‹ค.

// ๐Ÿฃ ์˜์ฒ : ํŒŒ์ผ ํ•˜๋‚˜์— ๋‹ค ๋•Œ๋ ค๋ฐ•์•˜๋”๋‹ˆ ์ฝ”๋“œ๊ฐ€ 500์ค„์ด ๋„˜์–ด๊ฐ”์–ด์š”...
export const useStore = create((set) => ({
  user: null, // ์œ ์ € ๊ธฐ๋Šฅ
  login: () => { ... },
  
  isDarkMode: false, // UI ๊ธฐ๋Šฅ
  toggleTheme: () => { ... },
  
  cartItems: [], // ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ธฐ๋Šฅ
  addItem: () => { ... },
}));

์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๊ธธ์–ด์ง€๋ฉด ๋‹น์—ฐํžˆ ๋ถ„๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋‘ ๊ฐ€์ง€ ๊ฐˆ๋ฆผ๊ธธ์— ์„ญ๋‹ˆ๋‹ค.

  1. "๊ทธ๋ƒฅ create๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์จ์„œ ์Šคํ† ์–ด๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋งŒ๋“ค์ž!" (๋‹ค์ค‘ ์Šคํ† ์–ด)
  2. "ํ•˜๋‚˜์˜ ์Šคํ† ์–ด ์•ˆ์—์„œ ๊ธฐ๋Šฅ๋ณ„๋กœ ๋ธ”๋ก์„ ์กฐ๋ฆฝํ•˜์ž!" (์Šฌ๋ผ์ด์Šค ํŒจํ„ด)

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


๐Ÿšจ 1. ์ฃผ๋‹ˆ์–ด์˜ ์‹ค์ˆ˜: ๋‹ค์ค‘ ์Šคํ† ์–ด (Multiple Stores)

Zustand๊ฐ€ ๋„ˆ๋ฌด ๊ฐ„๊ฒฐํ•˜๋‹ค ๋ณด๋‹ˆ ๋งŽ์€ ์ฃผ๋‹ˆ์–ด๋“ค์ด ์ด๋Ÿฐ์‹์œผ๋กœ ๊ฐ์ฒด๋ฅผ ๋‚จํŽธ/์•„๋‚ด์ฒ˜๋Ÿผ ์™„์ „ํžˆ ๊ฐˆ๋ผ๋†“์Šต๋‹ˆ๋‹ค.

// store/userStore.ts
export const useUserStore = create((set) => ({ user: null }));
 
// store/cartStore.ts
export const useCartStore = create((set) => ({ cartItems: [] }));

์–ผํ• ๋ณด๋ฉด ์•„์ฃผ ๊น”๋”ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ๋ณ„๋กœ ํŒŒ์ผ์ด ๋‚˜๋‰˜์—ˆ์œผ๋‹ˆ๊นŒ์š”. ํ•˜์ง€๋งŒ **"์œ ์ €๊ฐ€ ๋กœ๊ทธ์•„์›ƒํ•˜๋ฉด ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋„ ๋น„์›Œ์ ธ์•ผ ํ•œ๋‹ค"**๋Š” ๊ธฐํšํŒ€์˜ ์š”๊ตฌ์‚ฌํ•ญ์ด ๋“ค์–ด์˜ค๋Š” ์ˆœ๊ฐ„, ์ง€์˜ฅ๋ฌธ์ด ์—ด๋ฆฝ๋‹ˆ๋‹ค.

โŒ ์•ˆํ‹ฐ ํŒจํ„ด: ๊ฐ•์ œ ๋™๊ธฐํ™” (์ข€๋น„ useEffect์˜ ํƒ„์ƒ)

์„œ๋กœ ๋‚จ๋‚จ์ด ๋œ ์Šคํ† ์–ด๋“ค์€ ์„œ๋กœ์˜ ๋‚ด๋ถ€๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๋ถˆ์Œํ•œ 'UI ์ปดํฌ๋„ŒํŠธ'๊ฐ€ ์ค‘๊ฐ„๋‹ค๋ฆฌ ์—ญํ• ์„ ๋– ๋งก๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

// โš ๏ธ ๋‚˜์œ ์˜ˆ: TopNav ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(์Šคํ† ์–ด ๊ฐ„ ๋™๊ธฐํ™”)์„ ๋– ์•ˆ์Œ
const TopNav = () => {
  const { user, logout } = useUserStore();
  const { clearCart } = useCartStore();
 
  const handleLogout = () => {
    logout();     // 1. ์œ ์ € ์Šคํ† ์–ด ๋ณ€๊ฒฝ
    clearCart();  // 2. ์นดํŠธ ์Šคํ† ์–ด ๋ณ€๊ฒฝ (UI๊ฐ€ ์™œ ์ด๋Ÿฐ ๊ฒƒ๊นŒ์ง€ ์ฑ…์ž„์ ธ์•ผ ํ•˜์ฃ ?)
  };
  
  // ๐Ÿ’€ ๋” ์ตœ์•…์ธ ๊ฒฝ์šฐ:
  useEffect(() => {
    if (!user) clearCart(); 
  }, [user]); // ๋ฒ„๊ทธ์˜ ์˜จ์ƒ! ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋กœ ์–ต์ง€ ๋™๊ธฐํ™”
}

์ด ๋ฐฉ์‹์€ ์น˜๋ช…์ ์ž…๋‹ˆ๋‹ค.

  1. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํŒŒํŽธํ™”: ๋กœ๊ทธ์•„์›ƒ ์‹œ ์ผ์–ด๋‚˜๋Š” ์ผ์ด TopNav ํŒŒ์ผ ์•ˆ์— ์ˆจ์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
  2. ์ˆœํ™˜ ์ฐธ์กฐ ์—๋Ÿฌ: ๋‚˜์ค‘์—๋Š” ์Šคํ† ์–ด ํŒŒ์ผ๋ผ๋ฆฌ ์–ต์ง€๋กœ import๋ฅผ ํ•˜๋‹ค๊ฐ€ ๋ฌดํ•œ ๋ฃจํ”„ ์—๋Ÿฌ(Circular Dependency)๋ฅผ ๋งˆ์ฃผํ•ฉ๋‹ˆ๋‹ค.
  3. ๋””๋ฒ„๊น… ํ—ฌ: Redux DevTools๋ฅผ ๋‹ฌ์•„๋„, ์Šคํ† ์–ด๊ฐ€ ์ฐข์–ด์ ธ ์žˆ์–ด์„œ ์•ฑ ์ „์ฒด์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ '๋น„๋””์˜ค ๋Œ๋ ค๋ณด๋“ฏ' ํ•œ๋ˆˆ์— ๊ฐ์‹œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๐Ÿฆ ์˜ํ˜ธ์˜ ์กฐ์–ธ: "์˜์ฒ  ๋‹˜, ์šฐ๋ฆฌ ํ”„๋กœ๋•ํŠธ์ฒ˜๋Ÿผ '์œ ์ € ์ƒํƒœ'๋‚˜ 'UI ์ƒํƒœ'๊ฐ€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋“ฑ ์•ฑ ๊ณณ๊ณณ์— ์–ฝํžˆ๋Š” ๊ฑฐ๋ฏธ์ค„ ๊ตฌ์กฐ๋ผ๋ฉด, ์ƒํƒœ๋ฅผ ๊ฒฐ์ฝ” ๋‚จ๋‚จ์ฒ˜๋Ÿผ ์ฐข์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ๋ชธํ†ต(Single Truth)์„ ์œ ์ง€ํ•œ ์ฑ„ ํŒ”๋‹ค๋ฆฌ๋ฅผ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."


๐Ÿ’ก 2. ํ•ด๊ฒฐ์ฑ…: ์Šฌ๋ผ์ด์Šค ํŒจํ„ด (Slice Pattern) ๋„์ž…

Redux ์‹œ์ ˆ๋ถ€ํ„ฐ ๋‚ด๋ ค์˜จ ๊ทผ๋ณธ ์žˆ๋Š” ์•„ํ‚คํ…์ฒ˜์ธ Slice(์กฐ๊ฐ) ํŒจํ„ด์ด ์ •๋‹ต์ž…๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ๋ณ„ ์กฐ๊ฐ(Slice)์„ ๋ณ„๋„์˜ ํŒŒ์ผ์ด๋‚˜ ํ•จ์ˆ˜๋กœ ๋…๋ฆฝ์‹œํ‚ค๋˜, ๋งˆ์ง€๋ง‰์— ๋ฉ”์ธ ์Šคํ† ์–ด(useBoundStore) ๋‹จ ํ•œ ๊ณณ์—์„œ ํ•ฉ์น˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

Step 1: ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ (์กฐ๊ฐ ๋‚ด๊ธฐ)

๊ฐ Slice๋Š” Zustand๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํŠน๋ณ„ํ•œ ์ œ๋„ค๋ฆญ์ธ StateCreator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ cartSlice๋Š” ์ž๊ธฐ๊ฐ€ ๋งก์€ ์ผ๋ฐฉ๋งŒ ๋ฌต๋ฌตํžˆ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

// features/cart/cartSlice.ts
import { StateCreator } from 'zustand';
 
export interface CartSlice {
  cartItems: string[];
  addItem: (item: string) => void;
  clearCart: () => void; // ๐Ÿฃ ์˜์ฒ : ์นดํŠธ ๋น„์šฐ๋Š” ๋Šฅ๋ ฅ์น˜๊ตฐ์š”!
}
 
// ๐Ÿฆ ์˜ํ˜ธ: ์ด ํ•จ์ˆ˜๋Š” ์•„์ง 'Store'๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ์ € '์ƒํƒœ ์กฐ์ž‘ ์„ค๋ช…์„œ'์— ๋ถˆ๊ณผํ•ด์š”.
export const createCartSlice: StateCreator<CartSlice> = (set) => ({
  cartItems: [],
  addItem: (item) => set((state) => ({ cartItems: [...state.cartItems, item] })),
  clearCart: () => set({ cartItems: [] }), 
});

์œ ์ € ์Šฌ๋ผ์ด์Šค๋„ ๋˜‘๊ฐ™์ด ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

// features/user/userSlice.ts
import { StateCreator } from 'zustand';
 
export interface UserSlice {
  user: string | null;
  login: (name: string) => void;
}
 
export const createUserSlice: StateCreator<UserSlice> = (set) => ({
  user: null,
  login: (name) => set({ user: name }),
});

Step 2: ๋ฉ”์ธ ์Šคํ† ์–ด ์กฐ๋ฆฝ (Bound Store)

์ด์ œ ํฉ์–ด์ง„ ํŒŒ์ผ๋“ค์„ ์ค‘์•™ ํ†ต์ œ์†Œ ํด๋”(store/)์˜ useBoundStore.ts๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”์ธ๋“œ(Bound) ์Šคํ† ์–ด๋ž€, ๋ชจ๋“  ์Šฌ๋ผ์ด์Šค๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ '๋ฌถ์ธ' ์Šคํ† ์–ด๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

// store/useBoundStore.ts
import { create } from 'zustand';
import { UserSlice, createUserSlice } from '../features/user/userSlice';
import { CartSlice, createCartSlice } from '../features/cart/cartSlice';
 
// 1. ํƒ€์ž… ํ•ฉ์น˜๊ธฐ (Intersection Type)
// ๐Ÿฃ ์˜์ฒ : ์—ฌ๊ธฐ์„œ ํƒ€์ž…๋“ค์„ '+' ๊ธฐํ˜ธ์ฒ˜๋Ÿผ(&) ํ•ฉ์น˜๋Š”๊ตฐ์š”!
export type StoreState = UserSlice & CartSlice;
 
// 2. Zustand ๋ณธ์ฒด(create) ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•˜๊ธฐ
// ๐Ÿฆ ์˜ํ˜ธ: set, get ์ธ์ž๋ฅผ ๊ฐ๊ฐ์˜ Slice ํ•จ์ˆ˜์— ๊ทธ๋Œ€๋กœ ํ† ์Šค(Toss)ํ•ฉ๋‹ˆ๋‹ค.
export const useBoundStore = create<StoreState>()((...a) => ({
  ...createUserSlice(...a),
  ...createCartSlice(...a),
}));

UI ์ปดํฌ๋„ŒํŠธ๋Š” ์˜ค์ง ์ด useBoundStore ํ•˜๋‚˜๋งŒ importํ•ด์„œ ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์ผ ์Šคํ† ์–ด์˜ ์žฅ์ (์•ˆ์ •์„ฑ, ํ†ตํ•ฉ ๋””๋ฒ„๊น…)๊ณผ ๋‹ค์ค‘ ์Šคํ† ์–ด์˜ ์žฅ์ (์ฝ”๋“œ ๋ชจ๋“ˆํ™”)์„ ์™„๋ฒฝํžˆ ํก์ˆ˜ํ•œ ์•„ํ‚คํ…์ฒ˜์ž…๋‹ˆ๋‹ค!


โš’๏ธ 3. ์Šฌ๋ผ์ด์Šค ๊ฐ„์˜ ์šฐ์•„ํ•œ ํ†ต์‹ 

์ด์ œ ์•ž์„œ ๋‹ค์ค‘ ์Šคํ† ์–ด(Multiple Stores) ํŒŒํŠธ์—์„œ ์‹คํŒจํ–ˆ๋˜, "๋กœ๊ทธ์•„์›ƒํ•˜๋ฉด ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋„ ๊ฐ™์ด ์ดˆ๊ธฐํ™”๋œ๋‹ค" ์ฝ”๋“œ๋ฅผ ์Šฌ๋ผ์ด์Šค ํŒจํ„ด ์•ˆ์—์„œ ํ•ด๊ฒฐํ•ด ๋ณผ ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

Zustand์˜ useState ์•ˆ์—๋Š” ์‚ฌ์‹ค set ๋ง๊ณ ๋„ ์ˆจ๊ฒจ์ง„ get ๋ณ€์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”์ธ๋“œ ์Šคํ† ์–ด๋กœ ํ•ฉ์ณ์ง„ ์ƒํƒœ์—์„œ๋Š” get()์„ ํ˜ธ์ถœํ•˜๋ฉด ๋‹ค๋ฅธ ์Šฌ๋ผ์ด์Šค์˜ ์กด์žฌ๋ฅผ ์ธ์ง€ํ•˜๊ณ  ๋ช…๋ น์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// features/user/userSlice.ts
import { StateCreator } from 'zustand';
import { StoreState } from '../../store/useBoundStore'; // ๐Ÿ’ก ์•—! ์ „์ฒด ํƒ€์ž…์„ ๊ฐ€์ ธ์˜ค๋„ค์š”?
 
export interface UserSlice {
  user: string | null;
  login: (name: string) => void;
  logout: () => void;
}
 
// โœจ ์ข‹์€ ์˜ˆ: Slice ๋‚ด๋ถ€์—์„œ ๊ต์ฐจ ์ฐธ์กฐ (get)
// ๐Ÿฆ ์˜ํ˜ธ: StateCreator ์ œ๋„ค๋ฆญ์ด <์ „์ฒด์Šคํ† ์–ด, ๋ฏธ๋“ค์›จ์–ด1, ๋ฏธ๋“ค์›จ์–ด2, ํ˜„์žฌ์Šฌ๋ผ์ด์Šค> ์ˆœ์„œ๋กœ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค.
export const createUserSlice: StateCreator<
  StoreState, // 1. ์ „์ฒด ์Šคํ† ์–ด ๋ชจ๋ธ (get()์œผ๋กœ ๋‹ค๋ฅธ ์Šฌ๋ผ์ด์Šค ์ฐธ์กฐ ํ•„์ˆ˜)
  [],         // 2. ์ถ”๊ฐ€ ๋ฏธ๋“ค์›จ์–ด ๋“ฑ (์—†์œผ๋ฉด ๋นˆ ๋ฐฐ์—ด)
  [],         // 3. ์ถ”๊ฐ€ ๋ฏธ๋“ค์›จ์–ด ๋“ฑ (์—†์œผ๋ฉด ๋นˆ ๋ฐฐ์—ด)
  UserSlice   // 4. ์ด ์Šฌ๋ผ์ด์Šค ๋ณธ์ธ์˜ ํƒ€์ž… ์ง€์ •
> = (set, get) => ({
  user: null,
  login: (name) => set({ user: name }),
  logout: () => {
    set({ user: null });          // ๋‚ด ๋ฐฉ(User)์ฒญ์†Œ ์™„๋ฃŒ!
    get().clearCart();            // ๋‹ค๋ฅธ ๋ฐฉ(Cart) ๋ถˆ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ๊บผ๋ฒ„๋ฆฌ๊ธฐ! โœ…
  },
});

๋„ˆ๋ฌด ์•„๋ฆ„๋‹ต์ง€ ์•Š๋‚˜์š”? TopNav ๊ฐ™์€ UI ์ปดํฌ๋„ŒํŠธ๋Š” ๊ทธ์ € logout() ํ•˜๋‚˜๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด UI ๋ฐ–์œผ๋กœ ์™„๋ฒฝํžˆ ๋น ์ ธ๋‚˜์™€ ์ˆœ์ˆ˜ํ•˜๊ฒŒ Store ๋‚ด๋ถ€์—์„œ๋งŒ ํ†ต์‹ ์ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

๐ŸŒŸ ๋””์ž์ธ ์›์น™:
๋‹ค๋งŒ Slice ๊ฐ„ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋„ˆ๋ฌด ๋†’์•„์ง€๋ฉด ์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. "๋กœ๊ทธ์•„์›ƒ ์‹œ ์นดํŠธ ๋น„์šฐ๊ธฐ" ์ •๋„์˜ ๋‹จ๋ฐฉํ–ฅ ํ๋ฆ„์€ ์ข‹์ง€๋งŒ, ์นดํŠธ ์Šฌ๋ผ์ด์Šค๊ฐ€ ๋‹ค์‹œ ์œ ์ € ์Šฌ๋ผ์ด์Šค๋ฅผ ๊ฑด๋“œ๋ฆฌ๋Š” ์‹์˜ ํƒ๊ตฌ(Ping-Pong) ๊ฒฐํ•ฉ์€ ์ ˆ๋Œ€ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


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

Q1. ์—ฌ๋Ÿฌ ๊ฐœ์˜ create ์Šคํ† ์–ด๋ฅผ ๋งˆ๊ตฌ์žก์ด๋กœ ๋งŒ๋“ค์—ˆ์„ ๋•Œ(๋‹ค์ค‘ ์Šคํ† ์–ด) ๋ฐœ์ƒํ•˜๋Š” ๊ฐ€์žฅ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

  • A) ํด๋” ๊ตฌ์กฐ๊ฐ€ ์ง€์ €๋ถ„ํ•ด์ง„๋‹ค.
  • B) ๊ฐ ์Šคํ† ์–ด ๊ฐ„์˜ ์ƒํƒœ๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ปดํฌ๋„ŒํŠธ ๋ ˆ๋ฒจ์— ์–ต์ง€ ์ฝ”๋“œ๋ฅผ ์งœ์•ผ ํ•˜๊ณ , ์ˆœํ™˜ ์ฐธ์กฐ ์—๋Ÿฌ๊ฐ€ ์ผ์–ด๋‚  ํ™•๋ฅ ์ด ๋†’๋‹ค.
  • C) ์„ฑ๋Šฅ์ด ๋‹จ์ผ ์Šคํ† ์–ด๋ณด๋‹ค ๋А๋ฆฌ๋‹ค.
  • D) ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

โœ… ์ •๋‹ต: B

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

  • ์›๋ฆฌ ์„ค๋ช…: ์ƒํƒœ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฑด ์ข‹์ง€๋งŒ ๋ฌผ๋ฆฌ์ ์ธ ๊ทธ๋ฆ‡(create) ์ž์ฒด๋ฅผ ์ชผ๊ฐœ๋ฒ„๋ฆฌ๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ํŒŒ์ผ ๊ฐ„ ๋ฌธ๋งฅ์ด ๋‹จ์ ˆ๋ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ์ค‘๊ฐ„์—์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ useEffect๋กœ ๋‘ ์Šคํ† ์–ด๋ฅผ ์ž‡๋Š” ์ง•๊ฒ€๋‹ค๋ฆฌ ๋…ธ๋ฆ‡์„ ๊ฐ•์ œ๋ฐ›์œผ๋ฉฐ ๋ฒ„๊ทธ์˜ ์˜จ์ƒ์ด ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๊ธฐ๋Šฅ๋งˆ๋‹ค useFooStore, useBarStore ๋งŒ๋“ค๋‹ค๊ฐ€ ๋กœ๋”ฉ ์ƒํƒœ ๊ณต์œ ํ•˜๋ ค๋‹ค ๋ฐค์ƒˆ์‹  ์  ์žˆ์ฃ ? ๊ทธ๊ฒŒ ๋‹ค์ค‘ ์Šคํ† ์–ด์˜ ํ•จ์ •์ž…๋‹ˆ๋‹ค."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๐Ÿฆ ์˜ํ˜ธ: "์ž‘์€ ํ†ฑ๋‹ˆ๋ฐ”ํ€ด ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๊ทธ์น˜๋ฉด ๊ฐ์ž ๋”ฐ๋กœ ๋†‰๋‹ˆ๋‹ค. ํ†ฑ๋‹ˆ๋ฅผ ๋‚˜๋ˆ  ๊นŽ๋”๋ผ๋„ ํ•˜๋‚˜์˜ ์‹œ๊ณ„ํŒ(Bound Store) ์œ„์— ์˜ฌ๋ ค์•ผ ์ •ํ™•ํ•œ ์‹œ๋„ˆ์ง€๋ฅผ ๋‚ด๋Š” ๋ฒ•์ด์ฃ ."

Q2. ์Šฌ๋ผ์ด์Šค(Slice) ๊ฐ„ ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ์ƒ๋Œ€๋ฐฉ ์Šฌ๋ผ์ด์Šค์˜ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ ค๋ฉด ์–ด๋–ค ๋ฐฉ๋ฒ•์„ ์จ์•ผ ํ•˜๋‚˜์š”?

  • A) ์ƒ๋Œ€ ์ฝ”๋“œ๋ฅผ ํŒŒ์ผ ์ƒ๋‹จ์— import ํ•ด์„œ ์ง์ ‘ create ์ธ์Šคํ„ด์Šค๋ฅผ ์กฐ์ž‘ํ•œ๋‹ค.
  • B) UI์ชฝ ์ƒ์œ„ ํŽ˜์ด์ง€์—์„œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— Context Provider๋ฅผ ์”Œ์›Œ ํ•จ์ˆ˜๋ฅผ ๋ฌผ๋ ค๋ฐ›๋Š”๋‹ค.
  • C) StateCreator๋ฅผ ๋งŒ๋“ค ๋•Œ ์ธ์ž๋กœ ๋ฐ›์•„์˜ค๋Š” get() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ „์ฒด ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•œ๋‹ค.
  • D) Redux ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ฌด์กฐ๊ฑด ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.

โœ… ์ •๋‹ต: C

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

  • ์›๋ฆฌ ์„ค๋ช…: ์Šฌ๋ผ์ด์Šค ์ƒ์„ฑ ํ•จ์ˆ˜(StateCreator)๋Š” ๋ฌถ์—ฌ์ง„ ์ „์ฒด ์Šคํ† ์–ด(StoreState)์˜ ํƒ€์ž… ์ปจํ…์ŠคํŠธ ์œ„์—์„œ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด๋ถ€์˜ get() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋‹ค๋ฅธ ์Šฌ๋ผ์ด์Šค์˜ ์ƒํƒœ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ณ , set()์„ ํ†ตํ•ด ์ˆ˜์ •๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ์Šฌ๋ผ์ด์Šค๋ฅผ ์กฐ๊ฐ๋ƒˆ๋‹ค๊ณ  ์„œ๋กœ ์†Œํ†ต์ด ๋Š๊ธฐ๋Š” ๊ฒŒ ์•„๋‹ˆ์—์š”. ํ†ฑ๋‹ˆ๋ฐ”ํ€ด๋“ค์€ get์ด๋ผ๋Š” ์ถ•์œผ๋กœ ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๐Ÿฆ ์˜ํ˜ธ: "๋‹ค๋ฅธ ๋ถ€์„œ(Slice) ์„œ๋ฅ˜๊ฐ€ ํ•„์š”ํ•  ๋•Œ ๊ฐ€์žฅ ๋น ๋ฅธ ์ถœ์ž…๋ฌธ์€ get()์ž…๋‹ˆ๋‹ค."

Q3. [์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„]
์ด๋ฒˆ ์‹ ๊ทœ ํ”„๋กœ์ ํŠธ์—์„œ PM ์˜์ˆ˜๋‹˜์ด "์œ ์ € ์ธ์ฆ ์ •๋ณด, ๋กœ์ปฌ ํผ ์ž„์‹œ ์ €์žฅ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ๊ฐ ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•˜๊ณ  ์‹ถ์€๋ฐ์š”. Zustand ์“ฐ๊ธฐ๋กœ ํ–ˆ์ฃ ?" ๋ผ๊ณ  ์š”๊ตฌํ•˜์…จ์Šต๋‹ˆ๋‹ค. ๋งŒ์ผ ์˜์ฒ ์ด๊ฐ€ ๋‹ค์ค‘ ์Šคํ† ์–ด 3๊ฐœ๋ฅผ ์ƒ์„ฑํ–ˆ๊ณ  ์˜ํ˜ธ๊ฐ€ 1๊ฐœ์˜ ์Šฌ๋ผ์ด์Šค ํŒจํ„ด ๋ฌถ์Œ ์Šคํ† ์–ด๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค๋ฉด, ๋ฏธ๋“ค์›จ์–ด(persist) ์—ฐ๋™ ์‹œ ์ฝ”๋“œ ๋ณต์žก๋„ ๋ฉด์—์„œ ์–ด๋–ค ๊ทน๋ช…ํ•œ ์ฐจ์ด๊ฐ€ ๋‚ ๊นŒ์š”?

โœ… ์ •๋‹ต: ๋‹ค์ค‘ ์Šคํ† ์–ด๋Š” 3๊ฐœ์˜ ์Šคํ† ์–ด์— ๊ฐ๊ฐ ๊ฐœ๋ณ„์ ์œผ๋กœ persist ๋ฏธ๋“ค์›จ์–ด ์ฝ”๋“œ๋ฅผ ๊ฐ์‹ธ์ค˜์•ผ ํ•˜์ง€๋งŒ, ์Šฌ๋ผ์ด์Šค ํŒจํ„ด์€ ํ•ฉ์ณ์ง„ ๋ฉ”์ธ useBoundStore ํ•œ ๊ณณ์— ๋‹จ ํ•œ ๋ฒˆ๋งŒ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ž…ํžˆ๋ฉด 3๊ฐœ ์Šฌ๋ผ์ด์Šค ๋ชจ๋‘์— ์˜์†์„ฑ์ด ์ผ๊ด„ ์ ์šฉ๋œ๋‹ค.

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

  • ์›๋ฆฌ ์„ค๋ช…: ๋ฏธ๋“ค์›จ์–ด(์ €์žฅ์†Œ ์—ฐ๋™, ๋””๋ฒ„๊ทธ, ๋กœ๊น… ๋“ฑ)๋Š” ์Šคํ† ์–ด์˜ ํŒŒ์ดํ”„๋ผ์ธ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์›(Single Source of Truth)์„ ์œ ์ง€ํ•˜๋ฉด ํ™•์žฅ์ด ๋ฌด์ฒ™ ์‰ฝ์Šต๋‹ˆ๋‹ค. BoundStore ํ•œ ๋ฒˆ๋งŒ ์„ ์–ธํ•ด์ฃผ๋ฉด ๊ทธ ์•„๋ž˜์— ์กฐ๋ฆฝ๋œ ๋ชจ๋“  ๋ชจ๋“ˆ์ด ๊ณตํ†ต ๋ฏธ๋“ค์›จ์–ด์˜ ์ˆ˜ํ˜œ๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๋ฏธ๋“ค์›จ์–ด 3๋ฒˆ ๋ณต๋ถ™ํ•˜๋Š” ๊ฑฐ ์€๊ทผํžˆ ํ”ผ๊ณคํ•˜์ง€ ์•Š์œผ์…จ์–ด์š”? ํ•œ ๋ฐฉ์— ๋๋‚ด๋Š” ๊ฒŒ ๊ณ ์ˆ˜์˜ ๊ธธ์ž…๋‹ˆ๋‹ค."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๐Ÿฆ ์˜ํ˜ธ: "๋‹จ์ผ ๋ผˆ๋Œ€(BoundStore)๋ฅผ ๊ตฌ์ถ•ํ•˜๋ฉด ๊ฐ‘์˜ท(๋ฏธ๋“ค์›จ์–ด)์„ ํ•œ ๋ฒŒ๋งŒ ๋งž์ถฐ ์ž…์–ด๋„ ์˜จ๋ชธ์ด ๋ฐฉ์–ด๋ฉ๋‹ˆ๋‹ค."

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

์™€. ์˜ค๋Š˜ ๊ธฐ์กด 500์ค„์งœ๋ฆฌ useAppStore.ts ์ฝ”๋“œ๋ฅผ ์Šฌ๋ผ์ด์Šค๋กœ ์ชผ๊ฐฐ๋‹ค!
์ฒ˜์Œ์—” "๊ทธ๋ƒฅ create ์—ฌ๋Ÿฌ ๋ฒˆ ์“ฐ๋ฉด ๋˜๋Š” ๊ฑฐ ์•„๋ƒ?" ๋ผ๊ณ  ๋ฐ˜ํ•ญํ–ˆ๋‹ค๊ฐ€, ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋ณด์—ฌ์ฃผ์‹  '์ข€๋น„ useEffect ๋‚˜๋น„ํšจ๊ณผ'๋ฅผ ๋ณด๊ณ  ์†Œ๋ฆ„์ด ๋‹์•˜๋‹ค.

๊ธฐ๋Šฅ์„ ์ฐข์„ ๋•Œ๋Š” ์™„์ „ํžˆ ๋ฐ•์‚ด์„ ๋‚ด๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ ˆ๊ณ  ๋ธ”๋ก์ฒ˜๋Ÿผ ๋ถ„ํ•ดํ–ˆ๋‹ค๊ฐ€, useBoundStore๋ผ๋Š” ๋„“์€ ํŒ์— ๋‹ค์‹œ ๊ผญ๊ผญ ๊ฝ‚์•„ ๋„ฃ๋Š” ๊ฑฐ์˜€๊ตฌ๋‚˜! ์ œ์ผ ์งœ๋ฆฟํ–ˆ๋˜ ๊ฑด get().clearCart()๋กœ ์ชผ๊ฐœ์ง„ ๋‚จ์˜ ์Šฌ๋ผ์ด์Šค ๊ธฐ๋Šฅ๊นŒ์ง€ ์Šคํ† ์–ด ๋‚ด๋ถ€์—์„œ ๊ณ ์ƒํ•˜๊ฒŒ ์ปจํŠธ๋กคํ•  ๋•Œ์˜€๋‹ค. ์–ด์„คํ”„๊ฒŒ UI ์ปดํฌ๋„ŒํŠธ์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋™๊ธฐํ™”ํ•˜๋˜ ์ง“์€ ์ด์ œ ์˜์›ํžˆ ์•ˆ๋…•์ด๋‹ค!

๋‚ด์ผ์€ ํ‡ด๊ทผํ•˜๊ณ  ๋ชจ์ฒ˜๋Ÿผ ์น˜๋งฅ ํ•œ ์ž” ์‹œ์›ํ•˜๊ฒŒ ๊ฑธ์น˜๋ฉฐ, ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌ๋œ ์Šคํ† ์–ด ํด๋”๋ฅผ ๊ฐ์ƒํ•ด๋ด์•ผ๊ฒ ๋‹ค ๐Ÿ—๐Ÿบ!

๐Ÿ’ก "์ƒํƒœ๋ฅผ ์กฐ๊ฐ(Slice)์ฒ˜๋Ÿผ ๋‚˜๋ˆ„๋˜, ๋ฌผ๋ฆฌ์ ์œผ๋ก  ์ชผ๊ฐœ์ง€ ๋ง๊ณ  ๋ฉ”์ธ ์Šคํ† ์–ด๋ผ๋Š” ํ•˜๋‚˜์˜ ๋ ˆ๊ณ ํŒ์— ๋ผ์›Œ ๋งž์ถ”์ž."