React에서 상태 관리는 언제나 중요한 고민거리다.
그동안 계속 Redux를 사용해왔지만,
복잡한 보일러플레이트와 러닝커브 때문에 다른 대안을 찾기도 합니다.
오늘은 그런 대안 중 하나인 Zustand에 대해 소개하고, 왜 Zustand가 요즘 주목받고 있는지, Redux와 비교해서 어떤 점이 더 좋은지 알아보자.
Zustand(독일어로 "상태"라는 뜻)는 Poimandres 팀에서 만든 작고 빠르며 직관적인 상태 관리 라이브러리이다.
Redux처럼 action, reducer, dispatch 같은 복잡한 구조가 필요 없다.
단일 파일로 상태 관리 가능하며, 코드 양도 매우 적다.
Context 없이도 전역 상태를 사용할 수 있어서 코드가 더 간결해진다.
필요한 상태만 구독할 수 있기 때문에 성능 최적화가 자연스럽게 된다.
복잡한 아키텍처 없이도 빠르게 상태 공유가 가능하므로 중소 규모 프로젝트에 특히 적합하다.
실제로 회사에서 앱 소개용 웹페이지를 소규모로 구축하게 되면서 프론트엔드 개발을 담당하게 되었다. 무료 이용권 등록, 앱스토어 이동 등 비교적 단순한 기능 위주였지만, UI 상의 상태 관리가 필요한 부분이 있어 Zustand를 도입해 실무에 적용해보았다. 🤓
| 항목 | Redux | Zustand |
|---|---|---|
| 사용 복잡도 | 높음 (action, reducer, dispatch 필요) | 낮음 (create 한 줄로 끝) |
| 보일러플레이트 | 많음 | 거의 없음 |
| Provider 필요 여부 | 필수 | 불필요 |
| 러닝 커브 | 높음 | 매우 낮음 |
| Devtools 지원 | 잘됨 | 지원됨 (옵션 설정 필요) |
| 미들웨어 | 공식적으로 제공 | 조합 가능 (persist, devtools 등) |
// store.js
import { create } from 'zustand';
const useCounterStore = create((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}));
export default useCounterStore;
// App.jsx
import useCounterStore from './store';
export default function App() {
const { count, increase, decrease } = useCounterStore();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increase}>+1</button>
<button onClick={decrease}>-1</button>
</div>
);
}
Zustand는 persist 미들웨어를 통해 localStorage에 상태를 저장할 수 있다.
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
token: '',
setToken: (token) => set({ token }),
}),
{
name: 'auth-storage', // localStorage key 이름
}
)
);
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
const useStore = create(
devtools((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
}), { name: 'CounterStore' })
);
Zustand는 복잡한 상태 관리 없이도 충분히 강력한 전역 상태를 구성할 수 있게 해줍니다.
Redux가 너무 무겁거나, 작은 프로젝트에서 빠르게 전역 상태를 다뤄야 할 때
Zustand는 매우 훌륭한 선택이다.