React
๋ฅผ ์ฌ์ฉํ๋ฉด์ ํ์์ ์ผ๋ก ํจ๊ป ์๊ฐํด์ผํ๋๊ฒ์ state management library
์ด๋ค. ์ต๊ทผ๋ค์ด ๋ค์ํ ์ ํ์ง๋ค์ด ์กด์ฌํด์ก์ง๋ง ํ๋๋ ๋น์ฐํ Redux
๋ฅผ ์ฌ์ฉํด์ ์ํ๊ด๋ฆฌ๋ฅผ ํ๋๋๊ฐ ์์๋ค. ํ์ง๋ง ์์ฆ์ ํ๋ฆ์ hook
์ด ๋ฑ์ฅํ๋ฉด์ ๋ชจ๋ ๊ฒ์ด ๋ฐ๋์๋ค๊ณ ํด๋ ๋ฌด๋ฐฉํ๋ค. ๊ธฐ์กด์ ์๋ฃจ์
์ด ํจ์ฌ ์ฌ์ฉํ๊ธฐ ์ฝ๊ฒ ๋ณํํ๊ณ ์๋ก์ด ์๋ฃจ์
์ด ํ์ํ๊ธฐ๋ ํ๋ค.

๊ทธ ์ค ํ๋๊ฐ ์ค๋ ์๊ฐํ Zustand
์ด๋ค. Zustand
๋ global state
๋ฅผ ์ ์ฅํ๊ณ select
ํ๋ ๊ฐ๋จํ api
๋ฅผ ์ ๊ณตํ๋ library
์ด๋ค. ๊ฐ๋ณ๊ณ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ state manager
๋ผ๊ณ ํ ์ ์๊ฒ ๋ค. (๊ฐ์ธ์ ์ผ๋ก๋ useReducer
๋ฅผ ๋ ๋ผ์ด๋
ผ ํํ๋ผ๊ณ ์๊ฐํ๋ค)

Zustand
๋ ๋ค์ํ library
์ ๊ฒฐํฉํด์ ์ฌ์ฉํ ์ ์๋ค. immutable
ํ ๋ณํ๋ฅผ ์ฝ๊ฒ ์ปจํธ๋กคํ๊ธฐ ์ํด immer์ ํจ๊ป ์ฌ์ฉํ ์๋ ์๊ณ , ๋ค์ํ middleware๋ฅผ ์ ๊ณตํด์ฃผ์ด ํธ์๋ฅผ ์ ๊ณตํ๋ค. ๋ค์์ zustand์ ๋ช๊ฐ์ง ์ฅ์ ์ด๋ค.
- ํน์
library
์ ์ฎ์ด์ง ์๊ณ ๋ค์ํlibrary
๋ฅผ ๊ฒฐํฉํด ์ฌ์ฉํ ์ ์๋ค. - ํ๋์ ์ค์
store
๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ฉด์,state
๋ฅผ ์ ์/์ฌ์ฉํ๋๊ฒ์ด ๋จ์ํ๋ค. - ๋์์ ์ดํดํ๊ธฐ ์ํด ์์์ผ ํ๋ ์ฝ๋๋์ด ๋งค์ฐ ์ ๋ค.
rendering optimization
์ ํ๊ธฐ๊ฐ ์ฝ๋ค<Provider />
๋ก ๊ฐ์ธ์ฃผ์ง ์๊ณ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค
How to use the Zustand?
store
๋ฅผ ๋ง๋ค๋๋ create
ํจ์๋ฅผ ์ด์ฉํ์ฌ state
์ action
์ ์ ํฌํ ์ ์๋ค. ๊ทธ๋ฌ๋ฉด hook
์ return
ํ๋ ํํ์ด๋ค.
import { create } from "zustand";
export const useBookStore = create(
(set) => ({
amount: 30,
title: "Alice wonderland",
actions: {
addAmount: (value: number) =>
set((state) => ({ amount: state.amount + value })),
},
})
);
component
๋จ์์ state
๋ฅผ ๊บผ๋ด๊ธฐ ์ํด์๋ selector
๋ฅผ ์ ๋ฌํด์ฃผ์ด์ผํ๋ค. selector
๋ฅผ ๋ช
์ํ์ง ์์ผ๋ฉด store
์ ์ฒด๋ฅผ return
ํ๊ฒ๋๋ค.
import useBookStore from './bookStore';
const BookShelf = () => {
const amount = useBookStore(state => state.amount);
const { addAmount } = useBookActions(state => state.actions);
return (
<>
<h1>{amount}</h1>
<button onClick={() => addAmount(10)}>Change the amount</button>
</>
)
};
selector
์์ object
๋ฅผ ๋ฐํํ๋ ค๋ ๊ฒฝ์ฐ, shallow compare
๋ฅผ ์ฌ์ฉํ์ฌ re-render
๋ฅผ ์ค์ด๋๋ก option
์ ๋ฃ์ด์ค ์ ์๋ค (์ต์ ํ ๊ธฐ๋ฅ)
import { shallow } from 'zustand/shallow';
// only re-renders when either amount or title changes
const { amount, title } = useBookStore(
(state) => ({ amount: state.amount, title: state.title }),
shallow
);
๋ค์ํ middleware
๋ฅผ ์์ฝ๊ฒ ์ ์ฉํ ์ ์๋ค. ํนํ react
์๋ ๋์์๋ immer
, devtool
์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ด ๊ฐ๋จํ๋ค. (local storage
, session storage
๋ฅผ ์ง์ํ๋ middleware
๋ ์์ฝ๊ฒ ์ฌ์ฉ๊ฐ๋ฅํ๋ค)
import { immer } from "zustand/middleware/immer"; // npm install immer ํ์
import { devtools } from "zustand/middleware";
const useBookStore = create(
devtools( // devtool ์ ์ฉ
immer((set) => ({ // immer ์ ์ฉ
amount: 30,
title: "Alice wonderland",
actions: {
addAmount: (value: number) =>
set((state) => ({ amount: state.amount + value })),
subAmount: (value: number) =>
set((state) => ({ amount: state.amount - value })),
changeTitle: (value: string) =>
set((state) => ({ title: state.title + value })),
},
}))
)
);
Zustand
๋ Context API
์ฌ์ฉ์ ์ง์ํ๊ณ closure
๋ฅผ ํ์ฉํ์ฌ store
๋ด๋ถ์ํ๋ฅผ ๊ด๋ฆฌํ๋ค. (closure
๋ ๊ฐ๋จํ ๋งํด์ ํจ์๊ฐ ์ ์ธ๋ ๋น์์ ์ฃผ๋ณ ํ๊ฒฝ์ ๊ธฐ์ตํ๋ ๊ฒ์ด๋ค ์ฐธ๊ณ ). ๋ฐ๋ผ์ zustand store hook
์ ํธ์ถํ์ฌ ๋ฆฌํด๋ useHook
์ ์ด๋ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋๋ผ๋ ๊ฐ์ store
๋ฅผ ๋ฐ๋ผ๋ณด๊ฒ ๋๋ค.
store
๋ฅผ ์์ฑํ๋ ํจ์๋ฅผ ํธ์ถํ ๋ closure
๋ฅผ ํ์ฉํ๊ธฐ ๋๋ฌธ์ ์ํ์ ๋ณ๊ฒฝ, ์กฐํ, ๊ตฌ๋
๋ฑ์ interface
๋ฅผ ํตํด์ store
๋ฅผ ๋ค๋ฃจ๊ณ ์ค์ ์ํ๋ ์๋์น ์๊ฒ ๋ณ๊ฒฝ๋๋ ๊ฒ์ ๋ง์ ์ ์๋ค.
๋ง์น๋ฉฐ
ํ์ฌ๊น์ง๋ Redux
๋ฅผ ์๋์ ์ผ๋ก ๋ง์ด ์ฌ์ฉํ๊ณ ์๊ณ , Redux
๋ฅผ ์ ์ธํ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋น์ท๋น์ทํ ์ ์ ์จ์ ๋ณด์ด๊ณ ์๋ค. ํ์ง๋ง ์ ์ ์ฌ์ฉ์ด ์ฝ๊ณ ์ฝ๋๋์ด ๊ฐ์ํ ๋๋ ์ถ์ธ๋ก ๋ณํํ๊ณ ์๋๊ฒ ๊ฐ๊ธฐ๋ ํ๋ค. ๊ฐ๋จํ๊ณ ํธ๋ฆฌํ์ง๋ง ๋ค์ํ ์ต์
๊ณผ ์ด์ ์ด ์๊ธฐ ๋๋ฌธ์ ์ข ๋ ๊ณต๋ถํด๋ด์ผ ๊ฒ ๋ค.
- https://blog.axlight.com/posts/steps-to-develop-global-state-for-react/
- https://github.com/pmndrs/zustand
- https://ui.toast.com/posts/ko_20210812
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures