🐻 React + Zustand로 간단하고 가벼운 전역 상태 관리하기
🧭 들어가며
React 프로젝트를 하다 보면 props drilling을 피하고 전역에서 상태를 관리할 필요가 생깁니다.
하지만 Redux는 너무 무겁고, Context API는 비동기 처리나 구조가 복잡한 경우엔 번거롭습니다.
그럴 때 대안으로 떠오르는 게 바로 Zustand입니다.
Zustand는 “곰”이라는 뜻의 독일어로, React에서 가볍고 직관적인 전역 상태 관리 솔루션이에요.
여러 프로젝트를 진행하다 보면 기능 구현과, UI 디자인을 우선으로 개발하다 보니
어떤 상태관리 라이브러리를 사용할지 고민 하기보다, 무지성으로 Redux만을 사용하곤 했습니다.
하지만 최근 졸업 롤링 페이퍼 플랫폼 프로젝트를 진행할 때 Zustand를 처음으로 프로젝트에 사용하였습니다.
사용 해본 결과는 진작 애용할걸 이라는 생각이 들 정도로,
Redux에 비해 아주 간편하고 편리했습니다.
이번 포스팅을 통해 Zustand에 대해 다시 한 번 알아보고 정리하는 시간을 가져보겠습니다.
⚡ 왜 Zustand인가?
비교 항목 | Redux | Context API | Zustand |
코드 복잡도 | 높음 | 보통 | 매우 낮음 |
미들웨어 | 필요함 (thunk 등) | 직접 구현 필요 | 내장 지원 (persist 등) |
러닝 커브 | 높은 편 | 낮은 편 | 매우 낮음 |
DevTools 지원 | 있음 | 없음 | 있음 |
비동기 지원 | 직접 구현 | 별도 처리 필요 | 훅 내부 async 가능 |
🚀 Zustand 설치
npm install zustand
# 또는
yarn add zustand
🧪 기본 사용 예제
✅ 상태 정의 (store.ts)
import { create } from 'zustand';
interface CounterState {
count: number;
increase: () => void;
decrease: () => void;
}
export const useCounterStore = create<CounterState>((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}));
✅ 컴포넌트에서 사용
import React from 'react';
import { useCounterStore } from './store';
export default function Counter() {
const { count, increase, decrease } = useCounterStore();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increase}>+1</button>
<button onClick={decrease}>-1</button>
</div>
);
}
📌 핵심 포인트:
- useCounterStore()를 호출하면 상태에 접근 가능
- 내부에서 set()을 호출해 상태를 간단히 업데이트
🧠 더 복잡한 상태: 객체 + 비동기 처리
interface UserState {
user: { name: string } | null;
fetchUser: () => Promise<void>;
}
export const useUserStore = create<UserState>((set) => ({
user: null,
fetchUser: async () => {
const res = await fetch('/api/user');
const data = await res.json();
set({ user: data });
},
}));
const Profile = () => {
const { user, fetchUser } = useUserStore();
useEffect(() => {
fetchUser();
}, []);
if (!user) return <p>Loading...</p>;
return <p>안녕하세요, {user.name}님!</p>;
};
💾 상태 영속화(persist) 예제
Zustand는 middleware도 매우 간단하게 붙일 수 있습니다.
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface ThemeStore {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
export const useThemeStore = create(
persist<ThemeStore>(
(set) => ({
theme: 'light',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
}),
{
name: 'theme-storage', // localStorage 키 이름
}
)
);
✅ 이렇게 하면 새로고침해도 테마 상태가 유지됩니다.
느좋 홈페이지라면 다 있는 다크/라이트 모드 기능 구현에 용이합니다..!
⚙️ 상태 최적화 (선택적 subscribe)
Zustand는 사용하는 상태만 선택적으로 subscribe할 수 있어 성능도 좋아요.
const count = useCounterStore((state) => state.count); // count만 렌더링 트리거
Redux나 Context는 상태 변경 시 관련 없는 컴포넌트도 리렌더링되지만, Zustand는 부분 선택이 가능해서 가볍고 빠릅니다.
🔍 Devtools 사용법
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
export const useStore = create(
devtools((set) => ({
// 상태 정의
}))
);
→ Chrome에서 Zustand Devtools 확장 프로그램을 사용하면 확인 가능!
✅ 마무리
Zustand는 다음과 같은 경우에 딱입니다:
- Redux가 너무 복잡하게 느껴질 때
- Context API로는 성능이 부족하거나 비동기가 번거로울 때
- 빠르게 전역 상태를 관리하고 싶은 프로젝트
React + Zustand 조합은 작지만 강력하고,
실제 상업용 프로젝트나 사이드 프로젝트에서 매우 유용합니다.
📚 참고 자료