Zustand의 persist 미들웨어로 로그인 상태 유지 구현해보자.
persist
Zustand의 persist 미들웨어는 전역 상태를 직렬화(serialize) 하여 저장소(localStorage 등)에 저장하고, 이후 앱이 로드될 때 역직렬화(deserialize) 해서 다시 불러옵니다.
페이지를 새로고침해도 상태 유지
Zustand는 redux에 비해 간단하고 직관적인 상태 관리(Store) 라이브러리입니다.
기본 예시
persist(...) 미들웨어를 통해 상태를 저장합니다.
기본적으로 localStorage를 사용하지만, 다른 storage도 지정할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface User {
id: string;
nickname: string;
...
}
interface AuthState {
user: User | null;
setUser: (user: User | null) => void;
logout: () => void;
}
const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user }),
logout: () => set({ user: null }),
}),
{
name: "auth-storage", // localStorage key
}
)
);
옵션 설명
- name : 저장소 키 이름
- storage : 저장소 종류 설정
- partialize : 저장할 상태만 선택
- version : 상태 구조 변경 시 마이그레이션 지원용 버전 지정
- migrate : 이전 버전 상태를 새 버전 상태로 변환하는 함수
- merge : 복원 시 기존 상태와 병합 방식 커스터마이징
- skipHydration : 서버 사이드에서 hydration을 건너뛸지 여부 (SSR 대응용)
sessionStorage 적용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface User {
id: string;
nickname: string;
...
}
interface AuthState {
user: User | null;
setUser: (user: User | null) => void;
logout: () => void;
}
const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user }),
logout: () => set({ user: null }),
}),
{
name: "auth-storage",
storage: () => sessionStorage, // 여기서 storage를 커스터마이징
}
)
);
merge 사용
merge(persistedState, currentState) => finalState
merge 옵션을 사용해 저장소로부터 불러온 상태와 기본 초기 상태를 병합하는 방식을 직접 정의합니다.
persistedState: 저장소에서 불러온 저장된 상태currentState: create 시점의 기본 상태return: 병합 결과로 사용할 최종 상태
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface User {
id: string;
nickname: string;
// ... 필요한 필드 추가
}
interface AuthState {
user: User | null;
setUser: (user: User | null, expiresAt?: number) => void;
logout: () => void;
expiresAt: number | null;
}
const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
setUser: (user, expiresAt) => set({ user, expiresAt: expiresAt ?? null }),
logout: () => set({ user: null, expiresAt: null }),
expiresAt: null,
}),
{
name: "auth-storage",
storage: () => sessionStorage,
merge: (persistedState: any, currentState: AuthState): AuthState => {
const now = Date.now();
const restored = persistedState?.state;
if (!restored) return currentState;
if (restored.expiresAt && now > restored.expiresAt) {
// 만료 시간이 지났다면 초기화된 상태 반환
return {
...currentState,
user: null,
expiresAt: null,
};
}
// 유효한 만료 시간이라면 복원된 상태 유지
return {
...currentState,
...restored,
};
},
}
)
);
export default useAuthStore;
