Home Zustand - persist
Post
X

Zustand - persist

Zustand의 persist 미들웨어로 로그인 상태 유지 구현해보자.


persist

Zustandpersist 미들웨어는 전역 상태를 직렬화(serialize) 하여 저장소(localStorage 등)에 저장하고, 이후 앱이 로드될 때 역직렬화(deserialize) 해서 다시 불러옵니다.

페이지를 새로고침해도 상태 유지

Zustand는 redux에 비해 간단하고 직관적인 상태 관리(Store) 라이브러리입니다.


zustand-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;
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.