Zustand 기본 개념과 사용법 정리

짜장킴·2025년 9월 9일

프로젝트

목록 보기
17/38

Zustand란?

  • React 전역 상태 관리 라이브러리
  • Context API보다 간단하고 직관적
  • Provider 없이 상태 관리 가능
  • Redux, Recoil처럼 컴포넌트 간 상태 공유
  • 브라우저 메모리에서만 관리되므로 새로고침 시 초기화됨
  • 서버와 통신하지 않음 -> 서버 데이터를 가져와 캐싱하는 용도로 자주 활용
    - 예) getDashboardList()로 서버에서 받아온 데이터를 Zustand에 저장 후 여러 컴포넌트에서 사용

설치

npm install zustand

기본 구조

  • create : Zustand 스토어 생성 함수 / state와 action 정의
  • state(상태) : 전역으로 관리할 데이터 / UI에서 표시할 데이터
  • action(액션) : 상태를 업데이트하는 함수 (내부에서 set 호출)
  • set : 상태를 업데이트 (React의 setState 느낌)
  • get : 현재 상태 가져오기
import { create } from "zustand";

interface StoreState {
  count: number;
  increase: () => void;
}

export const useCounterStore = create<StoreState>((set, get) => ({
  count: 0, // 상태(state)
  increase: () => set((state) => ({ count: state.count + 1 })), // 액션(action)
}));

set

  • Zustand 스토어 생성 시 콜백에서 제공되는 함수
  • 상태 업데이트에 사용 (useState와 유사하지만 더 간단)
  • 객체 불변성 처리 자동 지원
    - useState에서는 객체 상태를 바꿀 때 불변성 유지가 필수
    - zustand는 새로운 객체를 반환하면 자동으로 상태를 갱신
  • 불변성 = 원본을 직접 수정하지 않고, 새로운 값으로 교체하는 습관
// useState
setState((prev) => ({ ...prev, count: prev.count + 1 }));

// zustand
set({ count: state.count + 1 })

get

  • 현재 상태를 가져올 때 사용
  • 컴포넌트 외부 로직, 이벤트 핸들러 등에서도 상태 읽기 가능
  • 전역 상태이므로 어디서든 접근 가능
getToken: () => get().accessToken,

컴포넌트에서 스토어 사용하기

getState() 사용

  • 컴포넌트 외부 / 이벤트 핸들러 등 UI와 직접 연결되지 않은 로직에서 활용
  • 구독하지 않음 → 상태 변경 시 리렌더링 발생하지 않음
const setAuth = useAuthStore.getState().setAuth;
setAuth(token, userId);

훅처럼 구독하기

  • React 컴포넌트 내에서 상태/액션을 구독
  • 상태 변경 시 리렌더링 발생
const setAuth = useAuthStore((state) => state.setAuth);
const accessToken = useAuthStore((state) => state.accessToken);

구조분해 할당

  • useAuthStore() 전체 구독 → store의 어떤 값이 바뀌든 리렌더링 발생
const { accessToken, setAuth } = useAuthStore(); // 전체 구독

구독이란?

  • 컴포넌트가 특정 상태 변화를 감시하는 것
  • Zustand에서 useAuthStore((state) => state.value)처럼 사용하면 구독 발생
    → 상태가 바뀌면 해당 컴포넌트가 리렌더링됨

로그인 예제

  • 로그인 성공 시 accessToken과 userId를 전역 상태로 관리하기
  • create를 사용해 zustand 전역 상태 스토어를 만들면, React 컴포넌트에서 사용할 수 있는 스토어 훅이 반환됨
import { create } from "zustand";

interface AuthState {
  accessToken: string | null;
  userId: number | null;
  setAuth: (token: string, userId: number) => void;
  clearAuth: () => void;
  getToken?: () => string | null;
}

export const useAuthStore = create<AuthState>((set, get) => ({
  accessToken: null,
  userId: null,

  // 로그인
  setAuth: (token, userId) =>
    set(() => ({
      accessToken: token,
      userId: userId,
    })),

  // 로그아웃
  clearAuth: () =>
    set(() => ({
      accessToken: null,
      userId: null,
    })),

  // 현재 토큰 조회
  getToken: () => get().accessToken,
}));

컴포넌트 사용 예시

function Header() {
  const accessToken = useAuthStore((state) => state.accessToken);
  return <div>{accessToken ? "로그인됨" : "로그인 필요"}</div>;
}

function LoginButton() {
  const setAuth = useAuthStore.getState().setAuth; // 구독X
  const handleLogin = async () => {
    const res = await postLogin();
    setAuth(res.data.token, res.data.user.id);
  };
  return <button onClick={handleLogin}>로그인</button>;
}
profile
프론트엔드 취준생입니다.

0개의 댓글