zustand persist를 이용하여 storage에 전역 상태 저장하기

sumi-0011·2023년 6월 15일
4

🔥 트러블슈팅

목록 보기
2/13
post-thumbnail

시작하기

Next.js 환경에서 전역상태라이브러리 zustand를 사용하고 있었다.
전역 상태를 사용해야 하면서, 새로 고침 후에도 값이 남아 있도록 스토리지에 값을 저장해야했다.

localstorage hook을 만들어서 사용할 수도 있지만,
이왕 zustand를 쓰니, 이를 이용해서 스토리지에 저장하기로 하였다.

zustand의 공식 문서를 확인해보면,
Persist middleware을 이용해 state를 storage에 저장할 수 있다.

storage에 상태를 저장하는 간단한 예시

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

export const useBearStore = create(
  persist(
    (set, get) => ({
      bears: 0,
      addABear: () => set({ bears: get().bears + 1 }),
    }),
    {
      name: 'food-storage', // name of the item in the storage (must be unique)
      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
    }
  )
)

Typescript에서 persist middleware을 사용하기

상태 타입 선언

const StorageKey = 'storage-key';

interface State {
  state: boolean;
  setState: (state: boolean) => void;
}

create 메소드 옆에 타입 추가

공식문서에 나와있는 예시는, javascript 문법으로 typescript의 타입은 알려주지 않았다.

기존에 zustand를 사용하는 방법에서는 (persist middleware 사용 x)
create 옆에 타입을 추가해서 시도해 보았더니 에러만 발생하였다..

에러메시지

'StateCreator<State, [], [["zustand/persist", State]]>' 형식의 인수는 'StateCreator<State, [], []>' 형식의 매개 변수에 할당될 수 없습니다.
'StateCreator<State, [], [["zustand/persist", State]]>' 형식은 '{ $$storeMutators?: [] | undefined; }' 형식에 할당할 수 없습니다.
'$$storeMutators' 속성의 형식이 호환되지 않습니다.
'[["zustand/persist", State]] | undefined' 형식은 '[] | undefined' 형식에 할당할 수 없습니다.

시도해 본 코드

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useStateStore = create<State>(
  persist(
    (set) => ({
      ...
    }),
    {
      name: StorageKey, 
    },
  ),
);

persist 옆에 타입 추가

persist 의 옆에 persist<State>과 같이 타입을 추가하니, 의도한대로 타입을 지정하는데 성공하였다!

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useStateStore = create(
  persist<State>(
    (set) => ({
      state: false,
      setState: (state: boolean) => {
        set({ state });
      },
    }),
    {
      name: StorageKey, 
    },
  ),
);

(10.27 추가) create 옆에 타입 추가!!

이 글을 처음 쓸 당시에는 persist 옆에 타입을 추가했어야했는데, 최근에 다시 시도해보니 create 옆에 타입을 추가하는 방식으로 바뀐것을 확인했습니다.

따라서 밑과 같이 사용하면 됩니다!

interface State {
  bears: number;
  addABear: () => void;
}

export const useBearStore = create<State>(
  persist(
    (set, get) => ({
      bears: 0,
      addABear: () => set({ bears: get().bears + 1 }),
    }),
    {
      name: StorageKey
    }
  )
);

state와 action 분리해서 사용하기

기존에 사용하던 방식대로 state와 action을 분리한 훅을 생성해서, store을 사용하였다.

코드

export const useBooleanState = () => 
useRecommendCategoryStore((state) => state.state);

export const useBooleanStateAction = () =>
  useRecommendCategoryStore((state) => state.setState);

state와 action을 분리한 훅을 사용하면

  • state가 리렌더링 되어도 action에는 영향을 주지 않는다.
  • 실제 store에 접근하지 않고, 간접적으로 정해진 권한만을 가진 hook으로 접근하기 때문에 예기치 못한 동작을 좀 더 줄일수 있다.
    라는 장점이 있다고 생각한다!

전체 코드

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const StorageKey = 'storage-key';

interface State {
  state: boolean;
  setState: (state: boolean) => void;
}

const useStateStore = create(
  persist<State>(
    (set) => ({
      state: false,

      setState: (state: boolean) => {
        set({ state });
      },
    }),
    {
      name: StorageKey, 
    },
  ),
);

export const useBooleanState = () => 
useRecommendCategoryStore((state) => state.state);

export const useBooleanStateAction = () =>
  useRecommendCategoryStore((state) => state.setState);

+session stoarge에 저장하기

zustand persist를 이용해 storage에 값을 저장한다면,
기본적으로 local storage에 값이 저장되게 된다.

만약 session storage에 저장을 원한다면, storage를 임의로 지정해주어야한다.

const useStateStore = create(
  persist<State>(
    (set) => ({
      state: false,

      setState: (state: boolean) => {
        set({ state });
      },
    }),
    {
      name: StorageKey, 
      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used

    },
  ),
);

참고 링크

profile
안녕하세요 😚

0개의 댓글