Zustand는 10줄로 Redux를 대체하는 무료 State Manager를 제공합니다

발행: (2026년 3월 30일 AM 03:10 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

Redux는 간단한 카운터조차도 액션, 리듀서, 셀렉터, 미들웨어, 그리고 수십 줄에 달하는 보일러플레이트 코드를 요구하는 경우가 많습니다.
Zustand는 동일한 기능을 단 몇 줄만으로 제공하며, 액션, 리듀서, 디스패처, Provider 컴포넌트가 전혀 필요 없습니다.

Minimal API

  • 몇 줄만으로 스토어를 만들고 어디서든 사용할 수 있습니다.
  • 보일러플레이트가 없습니다—액션, 리듀서, 컨텍스트 제공자가 필요 없습니다.
  • <Provider> 래퍼가 필요 없습니다—그냥 훅을 import 하면 됩니다.
  • TypeScript‑first—즉시 완전 타입 지원됩니다.
  • 내장 미들웨어 지원 (persist, devtools, immer, subscriptions).
  • gzipped 약 1 KB (Redux Toolkit의 약 30 KB와 비교).

설치

npm install zustand

Counter Example

import { create } from 'zustand';

interface CounterStore {
  count: number;
  increment: () => void;
  decrement: () => void;
  reset: () => void;
}

const useCounter = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

// Any component can import and use the store directly
function Counter() {
  const count = useCounter((state) => state.count);
  const increment = useCounter((state) => state.increment);

  return (
    <div>
      Count: {count}
      <button onClick={increment}>+</button>
    </div>
  );
}

<Provider> 래퍼가 필요 없고, connect()도 필요 없으며, useSelector/useDispatch도 필요 없습니다.

인증 스토어와 영속성

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

interface User {
  id: string;
  name: string;
  email: string;
}

interface AuthStore {
  user: User | null;
  token: string | null;
  login: (email: string, password: string) => Promise;
  logout: () => void;
  isAuthenticated: () => boolean;
}

const useAuth = create()(
  persist(
    (set, get) => ({
      user: null,
      token: null,

      login: async (email, password) => {
        const { user, token } = await api.login(email, password);
        set({ user, token });
      },

      logout: () => set({ user: null, token: null }),

      isAuthenticated: () => get().token !== null,
    }),
    { name: 'auth-storage' } // Persists to localStorage
  )
);

Todo 스토어 (Async Actions)

import { create } from 'zustand';

interface Todo {
  id: string;
  title: string;
  done: boolean;
}

interface TodoStore {
  todos: Todo[];
  loading: boolean;
  fetchTodos: () => Promise;
  addTodo: (title: string) => Promise;
  toggleTodo: (id: string) => void;
}

const useTodos = create((set, get) => ({
  todos: [],
  loading: false,

  fetchTodos: async () => {
    set({ loading: true });
    const todos = await api.getTodos();
    set({ todos, loading: false });
  },

  addTodo: async (title) => {
    const todo = await api.createTodo({ title });
    set((state) => ({ todos: [...state.todos, todo] }));
  },

  toggleTodo: (id) => {
    set((state) => ({
      todos: state.todos.map((t) =>
        t.id === id ? { ...t, done: !t.done } : t
      ),
    }));
  },
}));

비동기 로직은 순수 async/await 로 작성되었습니다; createAsyncThunk 나 추가 미들웨어가 필요하지 않습니다.

미들웨어 스택 예시

import { create } from 'zustand';
import { devtools, persist, immer } from 'zustand/middleware';

interface Store {
  items: string[];
  addItem: (item: string) => void;
}

const useStore = create()(
  devtools(
    persist(
      immer((set) => ({
        items: [],
        addItem: (item) =>
          set((state) => {
            state.items.push(item); // Immer makes this mutable update safe
          }),
      })),
      { name: 'my-store' }
    )
  )
);

Redux DevTools 지원, localStorage 지속성, 그리고 Immer 기반 가변 업데이트를 결합합니다.

기능 비교

FeatureZustandRedux ToolkitJotaiRecoil
번들 크기~1 KB~30 KB~3 KB~20 KB
보일러플레이트최소보통최소보통
Provider 필요아니오
비동기 처리내장createAsyncThunk내장Selectors
DevTools내장플러그인내장플러그인
학습 곡선~5 분~1시간~15 분~30 분
TypeScript 지원우수좋음우수좋음

TL;DR

Zustand는 zero ceremony를 원하는 개발자를 위한 상태‑관리 솔루션입니다: 작은 번들, Provider 없음, 리듀서 없음, 그리고 간단하고 type‑safe API. Redux가 프로젝트에 무겁게 느껴진다면, Zustand는 성능을 희생하지 않는 lightweight alternative을 제공합니다.

0 조회
Back to Blog

관련 글

더 보기 »