Zustand는 10줄로 Redux를 대체하는 무료 State Manager를 제공합니다
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 기반 가변 업데이트를 결합합니다.
기능 비교
| Feature | Zustand | Redux Toolkit | Jotai | Recoil |
|---|---|---|---|---|
| 번들 크기 | ~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을 제공합니다.