React 상태관리 비교: Redux Toolkit·Zustand·Recoil
Redux Toolkit, Zustand, Recoil의 설계 철학과 사용 사례, 코드 예제, 장단점을 한눈에 비교한 상태관리 선택 참고자료
목차
개요
React 애플리케이션에서 상태관리는 성능과 개발 효율에 직접적인 영향을 준다. 여기서는 Redux Toolkit, Zustand, Recoil을 초보자도 이해하기 쉽게 비교한다. 각 라이브러리의 철학, 사용 편의성, 코드 예제, 장단점을 중심으로 설명한다.
핵심 개념 비교
설계 철학
- Redux Toolkit: 예측 가능한 상태 흐름과 불변성, 중앙 저장소를 통한 명시적 업데이트.
- Zustand: 간단한 전역 상태 저장소, 함수형 API로 최소한의 보일러플레이트.
- Recoil: React와의 긴밀한 통합, 원자(atom) 단위의 상태 분리와 파생 상태(selector)를 강조.
학습 곡선
- Redux Toolkit: 개념이 많아 초기 진입 장벽이 있으나 공식 툴링과 패턴이 명확함.
- Zustand: API가 단순해 빠르게 적용 가능.
- Recoil: React 철학을 따르지만 atom/selector 개념에 익숙해져야 함.
성능과 재렌더링 제어
- Redux Toolkit: selector 최적화가 필요하며, 큰 전역 상태를 잘게 나누는 설계 권장.
- Zustand: 구독 기반으로 필요한 값만 재렌더링되어 성능 이점이 큼.
- Recoil: atom 단위로 구독하므로 세밀한 제어가 가능하다.
간단한 사용 예제
Redux Toolkit 예제
slice와 store를 만들고 컴포넌트에서 사용한다.
import { configureStore, createSlice } from '@reduxjs/toolkit'
import { Provider, useSelector, useDispatch } from 'react-redux'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1 },
decrement: state => { state.value -= 1 }
}
})
const store = configureStore({ reducer: counterSlice.reducer })
// 컴포넌트
function Counter() {
const value = useSelector(state => state.value)
const dispatch = useDispatch()
return (
<div>
<button onClick={() => dispatch(counterSlice.actions.decrement())}>-</button>
<span>{value}</span>
<button onClick={() => dispatch(counterSlice.actions.increment())}>+</button>
</div>
)
}
Zustand 예제
Zustand는 저장소를 간단히 정의하고 훅을 통해 상태에 접근한다.
import create from 'zustand'
const useStore = create(set => ({
count: 0,
inc: () => set(state => ({ count: state.count + 1 })),
dec: () => set(state => ({ count: state.count - 1 }))
}))
function Counter() {
const { count, inc, dec } = useStore()
return (
<div>
<button onClick={dec}>-</button>
<span>{count}</span>
<button onClick={inc}>+</button>
</div>
)
}
Recoil 예제
Recoil은 atom과 selector로 상태를 구성하고 useRecoilState로 연결한다.
import { atom, selector, useRecoilState, RecoilRoot } from 'recoil'
const countState = atom({ key: 'count', default: 0 })
const doubleState = selector({ key: 'double', get: ({ get }) => get(countState) * 2 })
function Counter() {
const [count, setCount] = useRecoilState(countState)
const double = useRecoilValue(doubleState)
return (
<div>
<button onClick={() => setCount(c => c - 1)}>-</button>
<span>{count} (double: {double})</span>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
)
}
언제 어떤 것을 선택할까
프로젝트 성격과 팀 역량에 따라 선택 기준이 달라진다.
- 규모가 크고 복잡한 비즈니스 로직, 타임트래블 디버깅, 미들웨어가 필요하면 Redux Toolkit 추천.
- 빠르게 개발하고 보일러플레이트를 최소화하려면 Zustand가 유리. 특히 작은 앱이나 부분 상태 관리에 적합.
- React 내에서 세밀한 상태 분리와 파생 상태가 많다면 Recoil이 적합. 컴포넌트 수준 상태와 전역 상태를 자연스럽게 섞을 수 있다.
마이그레이션 관점
redux toolkit 대체를 고민할 때는 상태 경계, 액션 흐름, 미들웨어 의존성을 먼저 점검한다. 간단한 상태와 로직만 있다면 Zustand로 옮기는 비용이 낮다. 반대로 많은 미들웨어나 복잡한 리듀서가 얽혀 있으면 단계적 리팩토링이 필요하다.
실무 고려사항
- 팀 경험: 익숙한 도구가 생산성에 유리하다.
- 테스팅: 각 라이브러리별로 테스트 스타일이 다르므로 기존 테스트를 고려해야 한다.
- 도구 생태계: Redux는 미들웨어와 DevTools가 풍부하고, Recoil은 React 실험적 기능과 결합된 사용 사례가 많다.
요약
간단히 말해, Redux Toolkit은 강력한 패턴과 성숙한 생태계를 제공한다. Zustand는 최소한의 코드로 빠르게 상태를 관리할 수 있고, recoil 상태관리 예제에서 보듯이 Recoil은 React 철학에 맞춘 세밀한 상태 분리와 파생 상태 관리를 지원한다. 프로젝트의 요구사항과 팀 역량을 바탕으로 적절한 도구를 선택하면 된다.