Zustand
Reference
상태관리의 필요성
리액트 애플리케이션은 규모가 커질수록 상태(state) 관리가 복잡해진다.
대표적인 문제점들은 다음과 같다:
- Props Drilling: 하위 컴포넌트까지 불필요하게 props를 전달해야 함
- 복잡한 상태 흐름: 여러 곳에서 상태를 읽고 수정하면서 추적이 어려워 짐
- 컴포넌트 간 의존성 증가: 컴포넌트가 상태에 강하게 묶여 결합도가 커짐
- Context API의 한계: 전역 공유는 가능하지만, 성능 이슈와 리렌더링 문제가 발생하기 쉬움
예시: 댓글 생성 시 필요한 상태 흐름
- 입력값 초기화
- 로딩 상태 처리
- 새 댓글 목록 반영
상태가 여러 컴포넌트에 흩어져 있다 보면, 어디서 어떤 상태가 바뀌는지 추적하기 어렵다.
Zustand 소개
Zustand란?
- 독일어로 상태라는 의미
- 2019년, Poimandres 팀에서 개발
- React의 Hooks와 자연스럽게 통합되는 경량 상태 관리 라이브러리
기본 사용 예시
jsx
import { create } from "zustand";
const useStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
function Counter() {
const { count, increment } = useStore();
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>+1</button>
</div>
);
}
create
함수로 상태와 업데이트 함수를 정의- 컴포넌트에서는
useStore
훅을 호출해 바로 사용 가능 - 상태와 로직이 한 곳에 모여 관리가 단순해진다.
Zustand 장점
- 간결함: 보일러플레이트 코드가 거의 없음
- 낮은 러닝커브: 훅 기반 API라 쉽게 학습 가능
- 유연한 구조: 작은 프로젝트부터 큰 프로젝트까지 적용 가능
- Provider 불필요: Context Provider 없이 전역 상태 사용 가능
jsx
function DeepComponent() {
const count = useStore((state) => state.count);
return <div>{count}</div>;
}
- 리렌더링 최적화: 필요한 상태만 선택적으로 구독 가능
jsx
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
// count가 변해도 increment는 다시 렌더링되지 않음
- TypeScript 지원 - 필요한 상태만 선택적으로 구독 가능
다른 라이브러리와의 비교
라이브러리 | 특징 | 복잡도 | 러닝커브 |
---|---|---|---|
Redux | 보일러플레이트 많음 | 높음 | 높음 |
Context API | 내장 기능, 성능 이슈 있음 | 중간 | 중간 |
Jotai / Recoil | 원자 기반 상태, 의존성 표현 | 중간 | 중간 |
Zustand | 간결하고 직관적, 성능 최적화 | 낮음 | 낮음 |
왜 Zustand를 배워야 할까?
- 복잡한 상태 관리 문제를 간결하고 직관적으로 해결
- React 프로젝트에 바로 적용 가능
- 러닝 커브가 낮아 학습 효율이 높음
Redux vs Zustand
- Redux: 액션, 리듀서, 미들웨어 등 구조가 무겁고 복잡
- Zustand: 단일 스토어, 훅 기반, 직관적인 API → 한 줄로 상태와 업데이트 로직을 정의할 수 있음jsx
// Zustand는 한 줄로 상태 + 함수 구성 create (() => ({ count: 0, inc: () => {...} }))