[React] Zustand의 persist

반응형

 

 

프론트 페이지에서 새로고침이 되어도 값이 변경되지 않아야 하는 전역 정보를 다룰 때에는 어떻게 하면 좋을까?

예를 들어, 로그인의 경우 accessToken의 수명이 다할 때까지 어딘가에 반드시 저장이 되어 있어야 한다.

 

이를 위해, Zustand의 persist를 사용해 보았다.

 

 

 

 

Zustand 의 Persist란?


persist 미들웨어는 Zustand를 사용하여 스토어의 상태를 로컬 스토리지(localStorage) 또는 다른 저장소에 유지(persist)하기 위해 사용된다. 이 미들웨어는 Zustand 상태를 지정된 저장소에 저장하고, 애플리케이션 초기화 시 저장된 상태를 복원한다.

 

persist의 사용 방식

 

persist는 두 가지 주요 매개변수를 받는다:

  1. config: Zustand 상태와 상태 변경 함수들을 정의한 StateCreator.
    • 예: setAuthData, setAuthToken, clearAuth와 같은 상태 변경 함수들이 포함된 콜백 함수.
  2. options: 상태를 저장하고 복원하기 위한 설정 옵션.
    • 저장소 이름, 스토리지 메커니즘(localStorage 등), 직렬화/역직렬화 방법 등을 지정.

persist를 사용한 코드 예시

import { StateCreator } from "zustand";
import { PersistOptions, createJSONStorage, persist } from "zustand/middleware";
import { createWithEqualityFn } from "zustand/traditional";
import { useShallow } from "../hooks/useShallow";

// AuthStoreType 정의
type AuthStoreType = {  
  loginId?: string; // 로그인 ID
  accessToken?: string; // 액세스 토큰
  refreshToken?: string; // 리프레시 토큰
  remember?: boolean; // 로그인 기억 여부

  // 상태 변경 함수들
  setAuthData: (data: Partial<AuthStoreType>) => void; // JSON 데이터로 상태 설정
  setAuthToken: (accessToken: string, refreshToken: string) => void;
  clearAuth: () => void; // 상태 초기화
  ...
};

// persist 미들웨어 타입 정의
type PersistAuthStoreType = (
  config: StateCreator<AuthStoreType>,
  options: PersistOptions<AuthStoreType>
) => StateCreator<AuthStoreType>;

// Zustand 스토어 생성
export const store = createWithEqualityFn<AuthStoreType>(
  (persist as PersistAuthStoreType)(
    (set) => ({
      // 상태 변경 함수들 구현
      setAuthData: (data) => set((state) => ({ ...state, ...data })),
      setAuthToken: (accessToken, refreshToken) =>
        set({ accessToken, refreshToken }),      
      clearAuth:() => set({ accessToken: undefined })       
    }),
    {
      name: "admin-auth", // 로컬 스토리지 키 이름
      storage: createJSONStorage(() => localStorage), // 로컬 스토리지에 상태 저장
    }
  )
);
 
 

config와 options 값

1. config

  • config는 StateCreator 타입이며, 위의 코드에서 Zustand 상태 정의와 상태 변경 함수들을 포함한다.
  • 위 코드에서 config는 (set) => ({ ... })로 작성된 콜백 함수 이다.
 (set) => ({
      // 상태 변경 함수들 구현
      setAuthData: (data) => set((state) => ({ ...state, ...data })),
      setAuthToken: (accessToken, refreshToken) =>
        set({ accessToken, refreshToken }),      
      clearAuth:() => set({ accessToken: undefined })       
    })

 

2. options

  • options는 PersistOptions<AuthStoreType> 타입이며, 상태를 저장/복원하기 위한 설정이다.
  •  코드에서 options는 다음과 같이 정의되었다.
{
  name: "admin-auth", // 로컬 스토리지에 저장될 키 이름
  storage: createJSONStorage(() => localStorage), // 저장소를 localStorage로 설정
}

 

options 세부 설명

  1. name
    • 로컬 스토리지에 저장되는 키 이름을 지정한다.
    • 여기서는 "admin-auth"로 설정되어, 로컬 스토리지에 이 키로 상태가 저장됩니다.
  2. storage
    • 상태를 저장할 스토리지 메커니즘을 정의한다.
    • createJSONStorage(() => localStorage)를 사용하여 **localStorage**를 스토리지로 사용하였다.
    • localStorage 외에도 sessionStorage, IndexedDB, 커스텀 스토리지를 사용할 수 있다.
  3. serialize와 deserialize (옵션에는 없지만, 기본값으로 처리됨)
    • persist 미들웨어는 상태를 JSON 형태로 저장하므로, 기본적으로 JSON의 stringify와 parse를 사용해 직렬화/역직렬화를 수행한다.

동작 방식

  1. 애플리케이션 시작 시:
    • admin-auth라는 키로 로컬 스토리지에서 저장된 값을 가져온다.
    • 만약 저장된 값이 있으면, Zustand 스토어 상태를 복원한다.
  2. 상태 변경 시:
    • set 함수가 호출되면, 상태가 변경되고 해당 상태는 로컬 스토리지에 직렬화되어 저장된다.

실제 동작 예시

1. setAuthToken을 호출하여 상태를 업데이트한다.

 

store.getState().setAuthToken("newAccessToken", "newRefreshToken");

 

이렇게 상태가 변경된 후, 로컬 스토리지에 다음 데이터들이 저장된다.

{
  "accessToken": "newAccessToken",
  "refreshToken": "newRefreshToken",
  ...
}

 

 

2. 애플리케이션 재시작 시 admin-auth키를 읽고, 저장된 상태를 스토어에 복원한다.

 

 


 

정리

  • config: 상태와 상태 변경 함수들을 정의하는 콜백 함수.
  • options: 저장소 이름(name)과 스토리지(storage) 설정을 포함한 옵션 객체.
  • persist의 역할: 상태를 로컬 스토리지 또는 지정된 스토리지에 저장하고, 초기화 시 복원하여 데이터 유실을 방지.
반응형