Zustand: Redux의 독재를 끝낼 가벼운 영웅
React 생태계에서 "상태 관리(State Management)"는 언제나 뜨거운 감자였습니다. 오랫동안 Redux가 사실상의 표준(De facto standard)으로 군림했지만, 과도한 보일러플레이트 코드와 복잡한 설정은 개발자들을 지치게 만들었습니다. Context API, Recoil, Jotai, MobX 등 수많은 도전자들이 나타났지만, 최근 가장 가파른 성장세를 보이며 개발자들의 마음을 사로잡은 라이브러리가 있습니다. 바로 Zustand입니다. 독일어로 "상태"를 뜻하는 이 작고 귀여운 곰 인형(Zustand의 로고)이 왜 강력한지, 약 3,000자의 깊이 있는 분석으로 파헤쳐 봅니다.
1. 왜 Zustand인가? (심플함의 미학)
Zustand의 가장 큰 미덕은 **단순함(Simplicity)**입니다. Flux 패턴을 따르면서도 Redux처럼 Action, Reducer, Dispatcher, Selector를 분리해서 작성할 필요가 없습니다.
1.1 보일러플레이트 제로
Redux Toolkit(RTK)이 많이 간소화되었다고는 하지만, 여전히 스토어를 설정하고 슬라이스를 만드는 과정이 필요합니다. 반면 Zustand는 create 함수 하나면 끝입니다.
import { create } from 'zustand';
interface BearState {
bears: number;
increasePopulation: () => void;
removeAllBears: () => void;
}
const useStore = create<BearState>((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}));
이 코드가 전부입니다. Provider로 앱을 감쌀 필요도 없습니다. 훅(Hook)을 사용하는 것처럼 컴포넌트 어디서든 useStore를 호출하여 상태를 읽고 업데이트할 수 있습니다.
2. 강력한 성능과 렌더링 최적화
Zustand는 단순히 사용하기 편한 것을 넘어, 성능 면에서도 탁월합니다.
2.1 Selector를 통한 리렌더링 방지
React Context API의 고질적인 문제는 Context 값이 바뀌면 해당 Context를 구독하는 모든 하위 컴포넌트가 불필요하게 리렌더링된다는 것입니다. 이를 막기 위해 React.memo나 useMemo를 난사해야 했습니다.
하지만 Zustand는 기본적으로 Selector 패턴을 지원합니다. 내가 필요한 상태 조각만 구독하면, 다른 상태가 변하더라도 내 컴포넌트는 리렌더링되지 않습니다.
const bears = useStore((state) => state.bears);
위 컴포넌트는 increasePopulation 함수가 바뀌어도 리렌더링되지 않습니다. 오직 bears 숫자 값이 변할 때만 반응합니다. 이는 대규모 애플리케이션에서 엄청난 성능 이득을 가져다줍니다.
2.2 Transient Updates (일시적 업데이트)
애니메이션이나 드래그 앤 드롭처럼 상태가 매 프레임마다 빈번하게 바뀌는 경우, React의 리렌더링 사이클을 거치는 것은 성능 저하의 원인이 됩니다. Zustand는 React 컴포넌트를 리렌더링하지 않고도 상태 구독자에게 직접 변경 사항을 알릴 수 있는 방법을 제공합니다. 이를 통해 60fps의 부드러운 인터랙션을 구현할 수 있습니다.
3. 미들웨어와 확장성
Zustand는 작지만 확장성이 뛰어납니다. 로깅, 불변성 유지(Immer), 영속성(Persist) 등 다양한 미들웨어를 손쉽게 붙일 수 있습니다.
3.1 Redux DevTools 연동
Redux를 그리워할 필요가 없습니다. Zustand는 Redux DevTools와 완벽하게 호환됩니다. 미들웨어 한 줄만 추가하면 시간 여행 디버깅이 가능해집니다.
3.2 Persist Middleware
새로고침을 해도 데이터가 날아가지 않게 하려면 로컬 스토리지에 저장해야 합니다. Zustand의 persist 미들웨어를 사용하면, 어떤 상태를 어디에 저장할지 설정 하나로 자동 관리해 줍니다. SSR 환경에서의 Hydration 이슈까지 우아하게 처리해 주는 것은 덤입니다.
4. 리액트 바깥에서도 사용 가능 (Vanilla JS)
Zustand 스토어는 React 컴포넌트 트리에 종속되지 않습니다. 즉, React 컴포넌트가 아닌 일반 JavaScript 함수나 비동기 로직 내부에서도 스토어에 접근하고 상태를 변경할 수 있습니다. 이는 복잡한 비즈니스 로직을 UI와 완전히 분리하여 순수 함수 형태로 테스트하고 관리할 수 있게 해줍니다.
5. 결론
Zustand는 "상태 관리"라는 본질에 가장 가까운 라이브러리입니다. 배우기 쉽고, 가볍고, 빠르며, React의 철학과 잘 어우러집니다. 물론 프로젝트의 규모가 아주 크고 엄격한 아키텍처가 필요하다면 Redux가 더 나은 선택일 수도 있습니다. 하지만 대부분의 모던 웹 애플리케이션에서 Zustand는 생산성과 성능 두 마리 토끼를 잡을 수 있는 최적의 선택지입니다.