๐ 03. ๋ค์ค ์คํ ์ด vs ์ฌ๋ผ์ด์ค ํจํด: ์คํ ์ด๋ฅผ ์กฐ๋ฆฝ์์ผ๋ก ์ชผ๊ฐ๊ธฐ
๐ ๊ฐ์
๊ฑฐ๋ํด์ง Zustand ์คํ ์ด๋ฅผ ๊ธฐ๋ฅ๋ณ๋ก ๋ถ๋ฆฌํ๊ณ ์กฐ๋ฆฝํ๋ ๋ง์ดํฌ๋ก ์ํคํ ์ฒ
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ๋น๋ํด์ง ์คํ ์ด๋ฅผ ๊ด์ฌ์ฌ(๋๋ฉ์ธ)๋ณ๋ก ๋ถ๋ฆฌํ๋ ์กฐ๊ฐ(Slice) ํจํด์ ๊ตฌํํ ์ ์๋ค.
- ์ฌ๋ฌ ๊ฐ์ Store๋ฅผ ๋จ๋ฐํ์ ๋์ ์น๋ช ์ ๋จ์ (์ข๋น ์ํ)์ ์ดํดํ๋ค.
- ์ฌ๋ฌ ๊ฐ์ Slice๋ฅผ ํ๋๋ก ๊ฒฐํฉํ๊ณ , Slice ๊ฐ์ ์ํ๋ฅผ ๊ต์ฐจ ์ฐธ์กฐ(ํต์ )ํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ด๋ค.
๐ ๋ชฉ์ฐจ
- ๐ค ๋์ : ์คํ ์ด๊ฐ ๋๋ฌด ๋ฑ๋ฑํด์ก์ด์!
- ๐จ 1. ์ฃผ๋์ด์ ์ค์: ๋ค์ค ์คํ ์ด (Multiple Stores)
- ๐ก 2. ํด๊ฒฐ์ฑ : ์ฌ๋ผ์ด์ค ํจํด (Slice Pattern) ๋์
- โ๏ธ 3. ์ฌ๋ผ์ด์ค ๊ฐ์ ์ฐ์ํ ํต์
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 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: () => { ... },
}));์ฝ๋๊ฐ ๋๋ฌด ๊ธธ์ด์ง๋ฉด ๋น์ฐํ ๋ถ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. ์ด๋ ๊ฐ๋ฐ์๋ค์ ๋ ๊ฐ์ง ๊ฐ๋ฆผ๊ธธ์ ์ญ๋๋ค.
- "๊ทธ๋ฅ
create๋ฅผ ์ฌ๋ฌ ๋ฒ ์จ์ ์คํ ์ด๋ฅผ ์ฌ๋ฌ ๊ฐ ๋ง๋ค์!" (๋ค์ค ์คํ ์ด) - "ํ๋์ ์คํ ์ด ์์์ ๊ธฐ๋ฅ๋ณ๋ก ๋ธ๋ก์ ์กฐ๋ฆฝํ์!" (์ฌ๋ผ์ด์ค ํจํด)
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ์ค๋ฌด ์ํคํ ์ฒ์์๋ ์ ์๋ฅผ ์ํฐ ํจํด, ํ์๋ฅผ ๊ถ์ฅ ํจํด์ผ๋ก ์ฌ๊น๋๋ค. ์ ๊ทธ๋ฐ์ง ๋น๊ตํด๋ด ์๋ค.
๐จ 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]); // ๋ฒ๊ทธ์ ์จ์! ์ฌ์ด๋ ์ดํํธ๋ก ์ต์ง ๋๊ธฐํ
}์ด ๋ฐฉ์์ ์น๋ช ์ ์ ๋๋ค.
- ๋น์ฆ๋์ค ๋ก์ง ํํธํ: ๋ก๊ทธ์์ ์ ์ผ์ด๋๋ ์ผ์ด
TopNavํ์ผ ์์ ์จ์ด๋ฒ๋ฆฝ๋๋ค. - ์ํ ์ฐธ์กฐ ์๋ฌ: ๋์ค์๋ ์คํ ์ด ํ์ผ๋ผ๋ฆฌ ์ต์ง๋ก
import๋ฅผ ํ๋ค๊ฐ ๋ฌดํ ๋ฃจํ ์๋ฌ(Circular Dependency)๋ฅผ ๋ง์ฃผํฉ๋๋ค. - ๋๋ฒ๊น ํฌ: 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)์ฒ๋ผ ๋๋๋, ๋ฌผ๋ฆฌ์ ์ผ๋ก ์ชผ๊ฐ์ง ๋ง๊ณ ๋ฉ์ธ ์คํ ์ด๋ผ๋ ํ๋์ ๋ ๊ณ ํ์ ๋ผ์ ๋ง์ถ์."