2023. 12. 15.leey00nsu

zustand persist 커스텀하기


Zustand persist 커스텀하기

전역상태관리로 zustand를 사용할 때, 해당 상태를 브라우저 저장소에 저장하고 싶을 때가 있습니다.

zustand에서는 이를 위해 zustand/middleware로 persist를 제공합니다.

zustand 공식 문서에 예시와 함께 자세히 설명되어 있습니다.

로컬,세션 스토리지에 저장하기

useBearStore
export const useBearStore = create(
  persist(
    (set, get) => ({
      bears: 0,
      addABear: () => set({ bears: get().bears + 1 }),
    }),
    {
      name: 'food-storage', // 스토리지의 이름
      storage: createJSONStorage(() => sessionStorage), // 사용할 저장소
      partialize: (state) => ({
        canvasName: state.bears,
      }),
    },
  ),
)

만든 storepersist로 감싼 후, 어떤 스토리지를 사용할 지 설정합니다.

기본으로는 localStorage가 사용되고, sessionStorage로 바꿀 수 있습니다.

이때 partialize 속성으로 원하는 상태만을 persist 하도록 할 수 있습니다.

커스텀 스토리지에 저장하기

기본적으로 로컬스토리지와 세션스토리지는 용량에 제한이 있습니다.

더 큰 용량을 저장하고 싶을 경우 브라우저 저장소 중 하나인 IndexedDB에 저장할 수 있습니다.

이 경우, indexedDB 라이브러리인 idb-keyval 을 사용해 구현할 수 있습니다.

useBoundStore
import { get, set, del } from "idb-keyval";
 
const storage: StateStorage = {
  getItem: async (name: string): Promise<string | null> => {
    return (await get(name)) || null;
  },
  setItem: async (name: string, value: string): Promise<void> => {
    await set(name, value);
  },
  removeItem: async (name: string): Promise<void> => {
    await del(name);
  },
};
 
export const useBoundStore = create(
  persist(
    (set, get) => ({
      bears: 0,
      addABear: () => set({ bears: get().bears + 1 }),
    }),
    {
      name: "food-storage",
      storage: createJSONStorage(() => storage),
    }
  )
);

사용법은 크게 다르지 않으며, 해당 커스텀 스토리지를 선언해준 후 사용합니다.

persist에 debounce 추가하기

나만의 수야 수호 만들기 프로젝트에서, 캔버스의 히스토리를 저장하도록 하였습니다.

이때 사용자가 캔버스와 상호작용할때마다 persistset이 동작하기 때문에 너무 많은 요청이 발생합니다.

따라서, debounce를 추가하면 상호작용이 끝난 후의 캔버스의 상태를 저장하도록 해 요청의 수를 줄일 수 있습니다.

사용되는 debounce 함수는 직접 구현하거나 lodash 등의 유틸 라이브러리를 사용할 수 있습니다.

storage debounce
// 커스텀 IndexdDB 스토리지
const IDBstorage: StateStorage = {
  getItem: async (name: string): Promise<string | null> => {
    return (await get(name)) || null;
  },
  setItem: debounce(async (name: string, value: string): Promise<void> => {
    await set(name, value);
  }, 1000),
  removeItem: async (name: string): Promise<void> => {
    await del(name);
  },
};

2025. leey00nsu All Rights Reserved.

GitHub