Zustand vs Jotai: 실무에서 Zustand를 선택한 이유와 실제 사용 후기
이번 포스팅에서는 실무에서 상태 관리 도구로 어떤 걸 선택할지 고민했던 과정을 정리해보려 합니다. 특히 Zustand와 Jotai 두 라이브러리를 비교하면서 실제 프로젝트에 어떤 기준으로 도입했는지, 사용하면서 느낀 점은 어땠는지를 중심으로 다룰 예정입니다.
개요
- 상태 관리 도구 선택 기준
- 코드 비교
- Zustand 선택 배경
- 실사용 중 느낀 점
- 실사용 팁
- 결론 및 회고
- 참고 자료
1. 상태 관리 도구 선택 기준
당시 진행하던 프로젝트는 유저 액션이 많고 페이지 간 상태 공유가 빈번한 대시보드 성격의 서비스였습니다. 초기엔 Context API를 사용했지만, 상태가 점점 복잡해지면서 관리가 어려워졌고 결국 전역 상태 관리 도구의 도입이 필요해졌습니다.
Zustand와 Jotai는 둘 다 가볍고 React와 잘 어울리는 상태 관리 도구지만, 방식은 꽤 달랐습니다. 저는 아래 기준을 중심으로 비교했습니다.
Jotai 특징
- Recoil에서 영감을 받은 구조로 atom 단위의 상향식 상태 구성
- selector 없이 atom만으로 파생 상태 구성 가능
- 리렌더링 최적화를 위한 설계 (Context 단점 보완)
- 타입스크립트와의 호환성과 간결한 API 제공
Zustand 특징
- Redux에 가까운 중앙 집중식 구조 (top-down)
- selector를 이용한 수동 렌더링 최적화 가능
- Devtools, middleware, persist 등 실무 기능 지원
- 매우 작은 크기 (약 2.9kB)
2. 코드 비교
기본적인 사용 예제를 비교해 보면 구조의 차이를 확실히 알 수 있습니다.
Jotai 예시
import { useAtom, atom } from 'jotai'
const countAtom = atom(0)
const Counter = () => {
const [count] = useAtom(countAtom)
return <div>count: {count}</div>
}
Zustand 예시
import { create } from 'zustand'
const useStore = create((set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
}))
const Counter = () => {
const { count, inc } = useStore()
return <div>{count}<button onClick={inc}>+1</button></div>
}
3. Zustand 선택 배경
Jotai는 구조가 심플하고 atom 중심이라 빠르게 쓰기 좋았지만, 상태가 많아질수록 흐름을 파악하기 어려웠습니다. 특히 액션 로직이 퍼져 있거나 atom 간 의존성이 많아지면 디버깅도 번거로워졌고요.
반면 Zustand는 store 단위로 상태를 묶을 수 있어서 전체 구조 파악이 쉬웠고, selector를 활용해 리렌더링도 효과적으로 제어할 수 있었습니다. Context API와 병행해 도메인별 store를 구성했더니 성능상 이점도 있었고요.
4. 실사용 중 느낀 점
- Zustand는 하나의 store로 전체 프로젝트에서 상태가 공유되기 때문에, 여러 페이지에서 사용하는 상태라면 reset 시점을 명확히 관리해주는 것이 중요합니다. 이는 Provider를 기준으로 상태 범위를 나눌 수 있는 Context API와 비교되는 부분입니다.
5. 실사용 팁
Devtools
,immer
,persist
를 store마다 설정하게 되면 반복되는 코드량이 많아질 수 있습니다. 그래서create
를 래핑하는 공통 유틸 함수를 만들어 해당 함수만 사용하도록 보일러플레이트를 구성했습니다.
보일러플레이트 코드 참고: https://velog.io/@apparatus1/zustand
💡Tip: .eslintrc
에서 아래와 같이 커스텀 룰을 설정하면, 프로젝트 내부 규칙으로 강제할 수 있습니다.
"no-restricted-imports": [
"error",
"paths": [
{
"name": "zustand",
"importNames": ["create", "createStore"],
"message": "프로젝트에 있는 create, createStore를 사용해 주세요."
}
]
]
- store는 작게 쪼개서 모듈화 하는 게 좋습니다. 커지면 추적이 어려워져요.
- 비동기 로직은 react-query 등으로 분리해 관리하는 게 깔끔합니다.
- selector 사용 시
shallow
비교 함수를 함께 써주면 리렌더링 방지에 효과적입니다.
참고: Zustand 리렌더링 방지 공식 가이드
6. 결론 및 회고
결국 중요한 건 도구 자체의 성능보다 팀과 프로젝트의 성격에 얼마나 잘 맞느냐인 것 같습니다. Zustand는 실무에서 쓰기 편했고, 확장성과 성능 모두 만족스러웠습니다.
Jotai도 좋은 도구이고 작은 규모의 프로젝트에서는 훨씬 빠르게 적용 가능하겠지만, 당시 우리 팀의 상황에는 Zustand가 더 현실적인 선택이었습니다.
상태관리 도구를 고려 중이라면 참고가 되었으면 좋겠습니다. 감사합니다:)