
ํญ์ ๋๋ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์๋ zustand๊ฐ ๊ฐ์ฅ ํธํ๋ค๊ณ ์๊ฐํ์๋ค. ์ฅ์ ๋ฐ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ๋ฐ ์ ๋ฆฌ๋์ค๋ ์ฐจ์ด๊ฐ ์๋๊ฑธ๊น? ๋ผ๊ณ ์๊ฐํ๋ ค๋ ์ฐฐ๋ ๋ด๊ฐ ์๊ณ ์๋ zustand๋ ๊ทธ๋ฅ ์๋ฌด๊ฒ๋ ์๋์๋ค๋ ๊ฒ์ ์๊ฒ๋์๋ค.
๊ทธ๋์ ์ ๋ง ๊ฐ๋ฒผ์ด ์ ์ญ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ ์์๋๋ฐ ์ต๊ทผ ํ๋ก์ ํธ์์ ์ํ๊ด๋ฆฌ๋ฅผ deepํ๊ฒ ์ฌ์ฉํ๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ์๊ฒผ๋ค.
provider ๊ทธ๊น์ด๊บผ ํ์๋ ํด?
๐ซข ํ ํ๋ฉด์ ๋์ผํ ์ข ๋ฅ์ ์ปดํฌ๋ํธ ์ฌ๋ฌ ๊ฐ๊ฐ ๋์์ ๋ ๋๋ง๋๊ณ , ๊ฐ๊ฐ ๋ ๋ฆฝ์ ์ธ ์ํ๋ฅผ ์ ์งํด์ผ ํ ๋ (์: ์ฌ๋ฌ ๊ฐ์ ๋์ผํ ์์ ฏ, ์ฌ๋ฌ ๊ฐ์ ๋์ ํธ์ง ์ฐฝ)๊ฐ Provider๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋ฆฝ์ ์ธ ์คํ ์ด ์ธ์คํด์ค๋ฅผ ์ ๊ณตํ๊ณ ์ํ๋ฅผ ๊ฒฉ๋ฆฌํด์ผ ํ๋ค. ์ด๋ฐ ๊ฒฝ์ฐ ๊ธํค ์คํ ์ด๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋ฐ์ดํฐ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉํ์ง ๋ชปํ๊ณ , ๋นํจ์จ์ ์ธ ์ฝ๋๊ฐ ๋๋ ๊ฒ์ด๋ค.
์ด Provier๊ฐ ์กด์ฌํ์ง ์๋๋ค๋ ์ ์ด Zustand์ ์ฅ์ ์ด์ ์ต๋ ๋จ์ ์ธ ๊ฒ ๊ฐ๋ค. ๊ทธ๋์ ๊ฐ๋จํ ํ๋ก์ ํธ์์๋ zustand๊ฐ ํจ์ฌ ์ ๋ฆฌํ๊ณ , ๊ฐํธํ๋ค๊ณ ๋๋ผ๋ ๊ฒ์ด๊ณ , redux ๊ฐ์ ๊ฒฝ์ฐ๋ ๋น๊ต์ ๋ณต์กํ๊ณ ๊ฐ๊ฐ์ ๋ ๋ฆฝ์ ์ธ ์ํ๊ด๋ฆฌ๋ฅผ ํด์ผ ํ๋ ๊ฒฝ์ฐ ๋ ์ ํฉํ๋ค๊ณ ๋๊ปด์ง๋ ๊ฒ ๊ฐ๋ค.
์์ง jotai๋ recoil์ ์ฌ์ฉํด๋ณธ์ ์ด ์์ด์ ์์ฝ๋ค ใ
๊ทธ๋ ๋ค๋ฉด ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ ~~ โ๏ธ
๊ณผ์ฐ zustand์์๋ Provider๋ฅผ ์ ํ ์ฌ์ฉํ ์๊ฐ ์์๊น?
์์ง zustand์์ ์ง์ ์ ์ผ๋ก Provider๋ฅผ ์ ๊ณตํด์ฃผ๊ณ ์์ง๋ ์์ง๋ง ๋ฆฌ์กํธ์ context์ ํจ๊ป ์ด๋ค๋ฉด ๊ฐ๋ฅ ํ๋ค!!!
import React, { createContext, useContext, useState } from 'react';
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';
// 1. ์คํ ์ด ์์ฑ ๋ก์ง ์ ์ (Provider ๋ด์์ ์ง์ ์ฌ์ฉ)
// (์์: ๊ฐ๋จํ ์นด์ดํฐ ์คํ ์ด)
const createCounterStore = () => createStore((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
// 2. React Context ์์ฑ
// ํ์
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ReturnType<typeof createCounterStore> | null ๋ก ํ์
์ง์
const StoreContext = createContext(null);
// 3. Provider ์ปดํฌ๋ํธ
export const CounterStoreProvider = ({ children }) => {
// useState์ ์ด๊ธฐ๊ฐ ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง์ดํธ๋ ๋
// createCounterStore()๊ฐ ๋ฑ ํ ๋ฒ๋ง ํธ์ถ๋์ด ์คํ ์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์ํ๋ก ์ ์ฅํฉ๋๋ค.
const [store] = useState(() => createCounterStore());
// ์์ฑ๋ ์คํ ์ด ์ธ์คํด์ค๋ฅผ Context Provider์ value๋ก ์ ๋ฌ
return (
<StoreContext.Provider value={store}>
{children}
</StoreContext.Provider>
);
};
// 4. Context๋ฅผ ์ฌ์ฉํ๋ ์ปค์คํ
ํ
export const useCounterStore = (selector) => {
// useContext๋ก Provider๊ฐ ์ ๊ณตํ ์คํ ์ด ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
const store = useContext(StoreContext);
if (!store) {
// Provider๋ก ๊ฐ์ธ์ ธ ์์ง ์์ผ๋ฉด ์๋ฌ ๋ฐ์
throw new Error('Cannot find CounterStoreProvider. Make sure to wrap the component tree.');
}
// Zustand์ useStore ํ
์ Context๋ก๋ถํฐ ์ป์ ์คํ ์ด์ selector๋ฅผ ์ ๋ฌํฉ๋๋ค.
return useStore(store, selector);
};
// --- ์ฌ์ฉ ์์ ---
// Counter ์ปดํฌ๋ํธ (์ํ ์ฌ์ฉ)
function CounterDisplay() {
// ์ปค์คํ
ํ
์ ํตํด ์ํ ๊ฐ ์ ๊ทผ
const count = useCounterStore((state) => state.count);
return <span>Count is: {count}</span>;
}
// Controls ์ปดํฌ๋ํธ (์ํ ๋ณ๊ฒฝ ํจ์ ์ฌ์ฉ)
function CounterControls() {
// ์ปค์คํ
ํ
์ ํตํด ์ํ ๋ณ๊ฒฝ ํจ์ ์ ๊ทผ
const increment = useCounterStore((state) => state.increment);
const decrement = useCounterStore((state) => state.decrement);
return (
<div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
// App ์ปดํฌ๋ํธ (Provider๋ก ๊ฐ์ธ๊ธฐ)
export default function App() {
return (
<div>
<h2>Counter 1 (Independent)</h2>
{/* Provider๋ก ๊ฐ์ธ์ฃผ๋ฉด ์ด ํ์ ์ปดํฌ๋ํธ๋ค์ ์ฒซ ๋ฒ์งธ ์คํ ์ด ์ธ์คํด์ค๋ฅผ ์ฌ์ฉ */}
<CounterStoreProvider>
<CounterDisplay />
<CounterControls />
</CounterStoreProvider>
<hr />
<h2>Counter 2 (Independent)</h2>
{/* ์๋ก์ด Provider๋ ์๋ก์ด ์คํ ์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์ฌ์ฉ */}
<CounterStoreProvider>
<CounterDisplay />
<CounterControls />
{/* ์ด Provider ์์ ๋ ๋ค๋ฅธ CounterDisplay๋ฅผ ๋ฃ์ด๋ ๋์ผํ ์คํ ์ด ๊ณต์ */}
<CounterDisplay />
</CounterStoreProvider>
</div>
);
}
๋์ถฉ๋ด๋ ์๋นํ ๋ณต์กํ๊ธด ํ์ง๋ง, ๊ทธ๋๋ ๋ง์ ์ฌ์ฉ์ ํด๋ณด๋ฉด ์์ฃผ ๋ณต์กํ์ง๋ง์ ์๋ค. ์ด๋ฐ์์ผ๋ก zustand๋ฅผ ์ํฉ์ ๋ง์ถฐ ์ฌ์ฉ์ ํ๋ค๋ฉด ๊ฐ๋จํ๊ฒ ์ฌ์ฉํ๋ฉด์๋, ๋ ๋ฆฝ์ ์ธ ์ํ๊ด๋ฆฌ๋ฅผ ๊ตฌํ ํ ์ ์๋ ์์ฃผ ์ข์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
Zustand์์ useShallow(๋๋ shallow ํจ์)๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฅ ํฐ ์ด์ ๋ ์ํ๊ฐ ์
๋ฐ์ดํธ๋์์ ๋, ์์(shallow) ๋น๊ต๋ก ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ง์์ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์๋ ๊ธฐ๋ฅ์ด๋ค.
๋ณดํต Zustand๋ก ์ํ๋ฅผ ๊ฐ์ ธ์ฌ ๋,
const { increase, decrease } = useCountStore(
useShallow((state) => ({
increase: state.increase,
decrease: state.decrease,
}))
)ใ
์ด๋ฐ ์์ผ๋ก ๋ง์ง๋ง์ shallow ๋๋ useShallow ๊ฐ์ ๊ฑธ ๋ถ์ด๋ฉด, ์ด์ ์ ๋ด๊ฐ ๊ตฌ๋ ํ ๊ฐ๋ค์ด๋ ์๋ก ๊ตฌ๋ ํ ๊ฐ๋ค์ด ์์ ๋น๊ต(===)๋ก ๋ฐ๋์๋์ง ์ ๋ฐ๋์๋์ง๋ฅผ ์ฒดํฌ๋ฐ๋์ง ์์๋ค๋ฉด ํด๋น React ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํ์ง ์์.
์ฆ, ์ฌ๋ฌ ๊ฐ์ ์ํ๋ฅผ ํ๊บผ๋ฒ์ ๊ฐ์ ธ์๋ ์ค์ ๋ก๋ ์ ๋ถ ๊ฐ์ฒด/๋ฐฐ์ด ๋ ํผ๋ฐ์ค๊ฐ ๋ฐ๋์ง ์์๋ค๋ฉด ๋ฆฌ๋ ๋๋ง์ ๊ฑด๋๋ฐ๊ฒ๋์ด, ์ฌ์ฉ์ ์ ์ฅ์์ ๋ ๋ถ๋๋ฌ์ด ์ฑ๋ฅ์ ์ป์ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์ ๋ฆฌํ์๋ฉด, useShallow๋ Zustand ์คํ ์ด์์ ์ํ ์ฌ๋ฌ ๊ฐ๋ฅผ ๋์์ ๊ตฌ๋
ํ ๋, ์ด ๊ฐ๋ค์ด ์์ ๋น๊ต๋ก ๋ณํ์ง ์์๋ค๋ฉด ๋ฆฌ๋ ๋๋ฅผ ์ ํ๊ฒ ๋ค๋ ๋ก์ง์ ์ ์ฉํ๊ธฐ ์ํ ๋น๊ต ํจ์๋ฅผ ์ ๊ณตํด์ฃผ๋ ๊ฒ์ด๋ค.