Skip to content

Zustand 세팅 ​

Zustand는 가볍고 직관적인 전역 상태 관리 라이브러리이다.
주로 클라이언트(UI) 상태를 중앙에서 관리하기 위해 사용한다.

왜 Zustand를 쓸까? ​

클라이언트 상태의 어려움 ​

React에서 전역 상태를 단순히 Props + Context로만 관리하면 다음 문제가 자주 발생한다.

  • 여러 컴포넌트 간 상태 공유가 복잡해짐 (Props Drilling)
  • Context 남용 시 불필요한 리렌더링 증가
  • 상태 로직이 UI 컴포넌트에 흩어짐
  • 상태 구조가 커질수록 관리가 어려워짐
  • 디버깅이 어려움 (어디서 무엇이 바뀌었는지 추적이 힘듦)

이런 문제를 간결하고 실용적인 방식으로 해결해 주는 것이 Zustand다.


Zustand를 안 쓰면 무엇을 직접 해야 할까? ​

아래를 직접 구현해야 한다:

  • 전역 상태 저장소 설계
  • 상태 변경 로직 중앙화
  • 불필요한 리렌더링 방지
  • 상태 변경 추적(디버깅)
  • 상태 지속(persist) 로직
  • 상태 구조 확장 시 리팩토링

가능하지만, 앱이 커질수록 상태 구조가 복잡해지고 유지보수가 어려워진다.


언제 Zustand를 도입하면 좋을까? ​

다음과 같은 경우 도입을 권장한다:

  • 여러 화면에서 공통으로 사용하는 UI 상태가 있는 경우
    (로그인 정보, 테마, 사이드바 열림 여부, 모달 상태 등)
  • Redux가 과하게 무겁다고 느껴질 때
  • Context API의 성능 이슈가 걱정될 때
  • 상태 구조는 단순하지만 전역 공유가 필요한 경우
  • 보일러플레이트 없이 빠르게 상태를 관리하고 싶을 때

설치 ​

bash
npm install zustand

기본 설정 ​

실제 프로젝트에서는 src/store/ 폴더에 상태를 모아 관리하는 것이 좋다.

1. 기본 Store 예시 ​

typescript
// src/store/ui.ts
import { create } from 'zustand';

interface UIState {
  isSidebarOpen: boolean;
  toggleSidebar: () => void;
  setSidebar: (open: boolean) => void;
}

export const useUIStore = create<UIState>((set) => ({
  isSidebarOpen: false,

  toggleSidebar: () =>
    set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),

  setSidebar: (open) => set({ isSidebarOpen: open }),
}));

2. 컴포넌트에서 사용 ​

tsx
import { useUIStore } from '@/store/ui';

function SidebarToggle() {
  const { isSidebarOpen, toggleSidebar } = useUIStore();

  return (
    <button onClick={toggleSidebar}>{isSidebarOpen ? '닫기' : '열기'}</button>
  );
}

3. 필요한 상태만 구독하기 (성능 최적화) ​

전체 상태를 가져오면 불필요한 리렌더링이 발생할 수 있다.

tsx
// 나쁜 예: 전체 상태 구독 (모든 변경에 리렌더링)
const { isSidebarOpen, toggleSidebar } = useUIStore();

// 좋은 예: 필요한 값만 선택적 구독
const isSidebarOpen = useUIStore((state) => state.isSidebarOpen);

권장 방식: selector로 필요한 값만 선택해서 구독한다.


4. Persist(로컬 저장) 설정 (선택) ​

새로고침 후에도 상태를 유지하고 싶다면:

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

interface AuthState {
  token: string | null;
  setToken: (token: string) => void;
  clearToken: () => void;
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      token: null,
      setToken: (token) => set({ token }),
      clearToken: () => set({ token: null }),
    }),
    {
      name: 'auth-storage',
    }
  )
);
  • 보통 인증 정보, 테마, 사용자 설정 등은 persist를 사용하고, 서버 데이터는 TanStack Query에 맡기는 것이 권장된다.

Zustand의 장단점 ​

장점 ​

  • 매우 가볍고 빠름
  • 보일러플레이트가 거의 없음
  • React와 자연스럽게 결합
  • Context보다 성능적으로 유리
  • Redux보다 훨씬 단순

단점 ​

  • 대규모 상태 구조에서는 설계가 중요
  • 상태가 많아지면 폴더 구조 정리가 필요
  • 미리 규칙을 정하지 않으면 혼잡해질 수 있음

언제 Zustand를 안 써도 될까? ​

  • 컴포넌트 내부 상태만 필요한 경우 (useState로 충분)
  • 상태 공유가 거의 없는 작은 앱
  • 서버 상태가 대부분인 프로젝트(TanStack Query로 충분한 경우)

Zustand vs TanStack Query ​

ZustandTanStack Query
용도클라이언트 상태서버 상태
예시UI 상태, 사용자 설정API 응답 데이터
캐싱수동 관리자동 캐싱
리페칭없음자동 리페칭

TanStack Query는 서버 상태, Zustand는 UI 상태를 담당한다.
두 도구를 역할에 맞게 분리하면 코드가 훨씬 단순해진다.
즉, 서버 데이터는 Query, 화면 상태는 Store로 명확히 구분한다.