์ฌ์ด๋ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ Zustand๋ฅผ ์ด์ฉํด๋ณด์๋ค.
Introduction - Zustand
How to use Zustand
zustand.docs.pmnd.rs
pmndrs/zustand: ๐ป React์์ ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ ํ์ํ
GitHub - pmndrs/zustand: ๐ป Bear necessities for state management in React
๐ป Bear necessities for state management in React. Contribute to pmndrs/zustand development by creating an account on GitHub.
github.com
์ ์ฒซ๋ฒ์งธ ๋ฌธ์๋ก ๋ค์ด๊ฐ๋ณด๋ฉด ๊ท์ฌ์ด ๊ณฐ๋์ด๊ฐ ๋์ค๊ณ , one up ์ ํด๋ฆญํ๋ฉด ์ซ์๊ฐ ์ฌ๋ผ๊ฐ๋ค.
zustand ์์ ๋ก ๋ง๋ค์ด ๋์ ๊ฒ ๊ฐ๋ค.
Zustand๋
๋ ์ผ์ด๋ก '์ํ'๋ผ๋ ๋ป์ด๊ณ ,
์ ์ญ ์ํ๋ฅผ ๊ฐ๋จํ๊ฒ ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
Redux๋ณด๋ค ํจ์ฌ ๊ฐ๋ณ๊ณ ๋ณด์ผ๋ฌํ๋ ์ดํธ(๋ฐ๋ณต์ ์ผ๋ก ์์ฑํด์ผ ํ๋ ํ ๊ฐ์ ์ฝ๋)๋ ์ ๋ค.
Redux๋ฅผ ์ค์ ๋ก ์ฌ์ฉํด๋ณธ ๊ฒฝํ์ ์์ง๋ง,
๊ฐ๋จํ ์ฐพ์๋ณธ ๊ฒฐ๊ณผ, Zustand ๋์ Redux๋ฅผ ์ผ๋ค๋ฉด ํ์ผ ์๋ ๋๊ณ , ์์ฑํด์ผํ ์ฝ๋๋๋ ๋ ๋ง์์ ๊ฒ ๊ฐ๋ค๋ ์ ์ ๋๋ ์ ์ ์์๋ค.
Zustand ์ค์น
Zustand๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์ค์น๋ฅผ ์งํํด์ผ ํ๋ค.
npm install zustand
์ ์ฒด ์ฝ๋
๋ด๊ฐ ์ฌ์ฉํ ์ด ์ฝ๋๋ ์ด๋ ๊ฒ ๋๋ฉฐ, ๋ฐ์์ ํ๋ ํ๋ ์์ธํ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค.
import {create} from "zustand";
import {persist} from "zustand/middleware";
interface UserState {
token: string | null;
expiresAt: number | null;
user_id: string | null;
nickname: string | null;
email: string | null;
provider: string | null;
setUser: (user: Partial<Omit<UserState, 'setUser' | 'resetUser'>>) => void;
resetUser: () => void;
}
export const useUserStore = create<UserState>()(
persist(
(set) => ({
token: null,
expiresAt: 0,
user_id: null,
nickname: null,
email: null,
provider: null,
setUser: (user) =>
set((state) => ({
...state,
...user,
})),
resetUser: () =>
set({
token: null,
expiresAt: 0,
user_id: null,
nickname: null,
email: null,
provider: null,
}),
}),
{
name: "user-storage",
}
)
);
์ฝ๋ ๋ถ์
interface UserState
interface UserState {
token: string | null;
expiresAt: number | null;
user_id: string | null;
nickname: string | null;
email: string | null;
provider: string | null;
setUser: (user: Partial<Omit<UserState, 'setUser' | 'resetUser'>>) => void;
resetUser: () => void;
}
UserState๋ผ๋ interface๋ฅผ ๋ง๋ค์ด์คฌ๋ค.
์ฌ์ฉ์ ๊ด๋ จ ๊ฐ๋ค์ ๋ฐ๋ก ๋ค๋ฃจ๋ ๊ฒ๋ณด๋ค ํ๋์ ๊ฐ์ฒด๋ก ๊ด๋ฆฌํ๋ฉด ๋ ํจ์จ์ ์ด๋ผ ์ด๋ ๊ฒ ๋ง๋ค์๋ค.
๋ณ์๋ง ์ ์ํ๋ ๊ฒ ์๋๋ผ ํจ์๋ ๊ฐ์ด ์ ์ํ๋ค (setUser, resetUser)
์ด์ ๊ฐ ์ฝ๋๋ฅผ ๋ฏ์ด๋ณด์!
token: string | null;
expiresAt: number | null;
user_id: string | null;
nickname: string | null;
email: string | null;
provider: string | null;
์ํ ๊ด๋ฆฌํ ๋ ์ฐ์ด๋ ์ฌ์ฉ์ ์ํ์ ํ์ ์ ์ ์ํ ๊ฒ์ด๋ค.
string | null : string์ด๋ null์ด ๊ฐ๋ฅํ ํ์ ์ด๋ผ๋ ๋ป์ด๋ค.
setUser: (user: Partial<Omit<UserState, 'setUser' | 'resetUser'>>) => void;
โ Omit<UserState, 'setUser' | 'resetUser'> : userState ํ์ ์์ setUser๋ resetUser๋ฅผ ์ ์ธํจ(Omit)
Omit ์ ์์ด๋ก ์๋ตํ๋ค, ์ ์ธํ๋ค ๋ผ๋ ์๋ฏธ์ด๋ค.
Omit<T, K>๋ก ์ฌ์ฉ์ ํ๋ ๊ฒ์ด๋ฉฐ, ํ์ T์์ ํค K๋ฅผ ์ ์ธํ ํ์ ์ ๋ง๋๋ ๋๊ตฌ์ด๋ค.
์์์ T๋ UserState์ด๊ณ ,
K๋ 'setUser' | 'resetUser' ์ธ ๊ฒ์ด๋ค.
์ด Omit์ TypeScript์๋ง ์๋ ์ ํธ๋ฆฌํฐ ํ์ ์ด๋ค.
โก Partial<...>
T์ ๋ชจ๋ ์์ฑ์ ์ ํ์ (optional)์ผ๋ก ๋ง๋ ๋ค.
์ฆ, ๋ชจ๋ ํ๋๊ฐ ์์ด๋ ๋๊ณ , ์ผ๋ถ๋ง ์์ด๋ ๋๋ค๋ ๋ป์ด๋ค.
useUserStore.getState().setUser({ nickname: "์ ์งฑ๊ตฌ" });
์๋ฅผ ๋ค์ด ์์ ๊ฐ์ ์์ผ๋ก ์ ์ฒด๋ฅผ ๋ณ๊ฒฝํ ํ์ ์์ด ๋๋ค์๋ง(ํ์ํ ๋ถ๋ถ๋ง) ๋ณ๊ฒฝํ ์ ์๋ค.
create<UserState>()(...) & persist(...)
import {create} from "zustand";
import {persist} from "zustand/middleware";
export const useUserStore = create<UserState>()(
persist(
(set) => ({
...
}),
{
name: "user-storage", // ๋ก์ปฌ์คํ ๋ฆฌ์ง ํค ์ด๋ฆ
}
)
);
์ผ๋จ create์ persist๋ ์ฌ์ฉํ๊ธฐ ์ ์ ๋ฐ๋์ธ import ํด์ฃผ์ด์ผ ํ๋ค.
create
create()๋ Zustnad์์ store๋ฅผ ์์ฑํ๋ ํจ์์ด๋ค.
์ ์ญ ์ํ๋ฅผ ์ ์ํ๊ณ , ๊ทธ ์ํ๋ฅผ ์ฝ๊ฑฐ๋ ์์ ํ ์ ์๋ ํจ์๋ฅผ ๋ง๋๋ ์ญํ ์ ํ๋ค.
persist
persist() ๋ localstorage์ ์ํ๋ฅผ ์ ์ฅํด์ฃผ๋ ๋ฏธ๋ค์จ์ด์ด๋ค.
์๋ก๊ณ ์นจํด๋ ์ ์ ์ ๋ณด๊ฐ ์ฌ๋ผ์ง์ง ์๋๋ค.
์๋์ผ๋ก localstorage.setItem('user-storage', ์ํ) ์ ๊ฐ์ ๋์์ ์ฒ๋ฆฌํด์ค๋ค.
๋ฏธ๋ค์จ์ด(Middleware)๋?
๋ ๊ฐ์ ๋ฌด์ธ๊ฐ ์ฌ์ด์์ ์ค๊ฐ ์ญํ ์ ํ๋ ์ฝ๋๋ฅผ ๋งํ๋ค.
Zustand์์๋ ์คํ ์ด๋ฅผ ์์ฑํ ๋ ๊ธฐ๋ฅ์ ํ์ฅํด์ฃผ๋ ํจ์๋ฅผ ๋ฏธ๋ค์จ์ด๋ผ๊ณ ์ผ์ปซ๊ณ ,
persist ๋ฏธ๋ค์จ์ด๋ ์ํ๋ฅผ ์๋์ผ๋ก localStorage์ ์ ์ฅํ๊ณ ๋ถ๋ฌ์ค๊ฒ ๋์์ฃผ๋ ๊ฒ์ด๋ค.
setUser
setUser: (user) =>
set((state) => ({
...state,
...user,
})),
set()์ Zustand์์ ๋ด๋ถ์ ์ผ๋ก ์ ๊ณตํ๋ ์ํ ์ ๋ฐ์ดํธ ํจ์์ด๋ค.
...state: ๊ธฐ์กด ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํด ์ฌ์ฉํจ.
...user: ์๋ก ์ ๋ฌ๋ ๊ฐ์ผ๋ก ์ํ๋ฅผ ๋ฎ์ด์ฐ๊ธฐ ์ํด ์ฌ์ฉํจ.
setUser({ userId: "userId123" }) ์ผ๋ก ํ๋ค๋ฉด userId๋ง ๋ณ๊ฒฝ๋๊ณ , ๋๋จธ์ง ์ํ ๊ฐ๋ค์ ๊ทธ๋๋ก ์ ์ง๋๋ค.
resetUser
resetUser: () =>
set({
token: null,
refreshToken: null,
expiresAt: 0,
user_id: null,
nickname: null,
email: null,
provider: null,
}),
Zustand์ ์ํ๋ฅผ ์ด๊ธฐํ ํ๋ ํจ์์ด๋ค.
์ ๊ธ๊ณผ ๊ฐ์ ์์ ์์ ๋ณดํต ๋ก๊ทธ์์์ด๋ ํํดํ ๋ ์ฌ์ฉํ๋ค.
๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๊ธฐ
Login.tsx์์ ์ ์ฅํ user ์ ๋ณด๋ฅผ MyPage.tsx์์ ๋ฐ๊ณผ ๊ฐ์ด ๊ฐ์ ธ์ฌ ์ ์๋ค.
const userId = useUserStore((state) => state.user_id); // ID ๊ฐ์ ธ์ค๊ธฐ
๊ทธ๋ฌ๋ฉด ์๋ฌธ์ ์ ๊ธฐํ ์ ์๋ค.
๊ทธ๋ฅ localStorage.getItem์ผ๋ก ๊ฐ์ ธ์ค๋ฉด ๋๋๋ฐ, ์ ๊ตณ์ด ์ ๋ ๊ฒ ๊ฐ์ ธ์ค๋์ง?
์๋ํ๋ฉด localStorage.getItem์ ์๋ ๋ฆฌ๋ ๋๋ง์ด ์ ๋์ง๋ง,
Zustand๋ ์๋ ๋ฆฌ๋ ๋๋ง์ด ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ฅผ ๋ค์ด ํค๋์ ๋ด ๋๋ค์(header.tsx)์ด ์๊ณ , ๋ด ์ ๋ณด๋ฅผ ์์ ํ๋ ๋ชจ๋ฌ(MyPageModal.tsx)์์ ๋๋ค์์ ์์ ํ๋ค๊ณ ์น์.
์์ ํจ๊ณผ ๋์์ ํค๋์ ๋ชจ๋ฌ์ ์๋ ๋๋ค์์ด ๋๊ธฐํ(๋ณ๊ฒฝ)๋์ด์ผ ํ๋ค.
Zustand๋ฅผ ์ด์ฉํ๋ฉด ์ํ๊ฐ ๋ณ๊ฒฝ๋์๋ง์ ์ํ๋ฅผ ๊ตฌ๋ ์ค์ธ ์ปดํฌ๋ํธ๋ค์ด ์๋์ผ๋ก ๋ฆฌ๋ ๋๋ง๋๊ธฐ ๋๋ฌธ์ ๋ ์ปดํฌ๋ํธ ๋ชจ๋ ์ฆ์ ๋ณ๊ฒฝ๋ ๋๋ค์์ ๋ฐ์ํ ์ ์๋ ๊ฒ์ด๋ค.
๋ฐ๋ฉด, localStorage๋ ๋จ์ํ ๊ฐ์ ์ ์ฅํ๋ ๊ณต๊ฐ์ด๊ณ , ๊ฐ์ด ๋ฐ๋์ด๋ React๋ ๊ทธ๊ฑธ ๊ฐ์งํ์ง ๋ชปํ๋ค.
๋ฐ๋ผ์ ๋๋ค์์ ๋ฐ๊พผ๋ค๋ฉด ๋ชจ๋ฌ์์๋ ๋ณ๊ฒฝ๋ ๋๋ค์์ด ๋ณด์ด์ง๋ง, ํค๋์๋ ์ด์ ๋๋ค์ ๊ทธ๋๋ก์ผ ์ ์๋ค.
์ถ๊ฐ์ ์ผ๋ก ์ ๋ณด๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฒ์ ์ด๋ฐ ์์ผ๋ก ์์ฑํ๋ฉด ๋๋ค.
useUserStore.getState().setUser({
nickname: response.data.nickname,
email: response.data.email
});