11. 세 가지 전역 상태 라이브러리의 유사점과 차이점
마지막으로 Zustand, Jotai, Valtio를 비교해 보자!
🔖 1. 세 가지 라이브러리 설명해보기
Zustand
사용법과 Store 모델 측면에서 Redux(or React Redux)와 유사하다. 다만 Redux처럼 action/reducer 구조를 강제하지 않는다.
Redux와는 달리 reducer 함수를 기반으로 하지 않으므로, 보일러플레이트가 적고 러닝 커브가 낮다.
한마디로 Redux보다 Zustand가 자유도가 높다. 뭔가 강제하는 게 적기에 쉽게 느껴진다. (책에서는 '구조에 대한 의견을 제시하지 않는다고 표현)
useStore
훅으로 전역 상태를 가져오고, 간단한setter
를 정의하는 식으로 작업한다.Selector 기반 최적화:
useStore(selector, equalityFn)
형태로 원하는 상태만 구독할 수 있어, 불필요한 리렌더링을 줄일 수 있다.
Jotai
Recoil처럼 atom 단위로 상태를 관리하지만, 별도의 selector API를 쓰지 않는다. 대신 atom을 결합하거나 derived atom으로 만들어 필요한 상태를 구성한다.
필요한 atom만 구독하므로, atom이 바뀌지 않는 한 컴포넌트가 다시 렌더링되지 않는다. Context 설정 없이도 전역 상태를 다룰 수 있다.
여러 작은 atom을 쪼개서 관리하거나, atom을 결합해 복잡한 로직을 만들 수 있다. 그래서 프로젝트 규모가 커져도 비교적 유연한 구조로 유지 가능하다.
그래서 보통 Recoil 썼던 팀들이 Recoil이 저물고 나서 Jotai로 많이들 갈아타는 것 같다.
Valtio
변경 가능한(mutable) 갱신 모델이라는 점에서 MobX와 유사하지만, Valtio는 Proxy를 통해 속성 변경을 자동 감지하는 것이 포인트다.
컴포넌트가 실제로 읽은 속성이 바뀔 때만 재렌더링하도록 해준다. Redux 등에서 요구되는 불변성 관리 없이
state.count++
처럼 직관적으로 작성할 수 있다.장점으로는 배우기 쉽고, 직관적인 코드 작성이 가능하다.
단점으로는 대규모 프로젝트에서 명시적 액션/리듀서 방식에 익숙한 개발자에겐 추적 로직이 얼렁뚱땅 이루어진 것처럼 (매우 추상적으로) 보일 수 있기에 디버깅이 어려울 수 있다.
🔖 2. 세 가지 라이브러리 표로 비교하기
Thanks to GPT (❁´◡`❁)
Zustand
- 단일(또는 소수의) Store 객체
- useStore
훅을 통해 상태/액션 사용
- Redux-like 구조(그러나 reducer/action 강제 X)
- 설정이 매우 간단, 보일러플레이트 적음 - 원하는 상태만 구독(Selector+Equality)
- 자유도가 높고, 러닝 커브가 낮다. - 필요한 부분만 구독 가능 → 불필요 렌더링 ↓
- 구조를 강제하지 않아, 규모가 커지면 설계가 제각각일 수 있음 - DevTools/미들웨어 생태계는 Redux만큼 크지 않음
Jotai
- atom(원자) 단위로 상태 정의
- useAtom
훅으로 atom 읽기/쓰기
- Recoil 유사(Selector 대신 파생 atom 사용)
- 필요한 atom만 읽으므로, 다른 atom이 변해도 영향 X - Context 설정 없이 전역 상태 관리 가능
- 작은 단위 상태를 여러 atom으로 나누고, 조합하는데 유연 - 쉽게 배우고 적용 가능 - atom 간 결합(파생 atom)으로 복잡 로직도 구성 가능
- atom이 너무 많아지면 구조 관리가 복잡 - 공식 DevTools는 다소 제한적 (Recoil만큼 성숙 X)
Valtio
- Proxy 기반
- useSnapshot
훅으로 상태 스냅샷 사용
- state.someProp++
처럼 직접 수정 가능
- 변경 가능한 갱신 방식 - 컴포넌트가 실제 읽은 속성만 업데이트 (자동 추적)
- 매우 직관적이고 자유로운 문법 - 불변성 관리, 코드 간결 - 부분 렌더링 최적화 자동화
- 무분별한 상태 변경 시 추적 비용 증가 가능 - 대규모 프로젝트에서 액션/리듀서 기반이 아니라 디버깅 어려움 - DevTools/미들웨어 생태계 적음
🔖 3. 책을 덮으며
"기본적으로 마이크로 상태 관리는 특정 문제에 대한 올바른 해결책과 라이브러리를 선택하는 것을 수반한다. 마이크로 상태 관리를 하기 위해서는 문제가 무엇이고, 문제에 사용 가능한 해결책이 무엇인지 이해하는 것이 필요하다. 이 책이 개발자들이 올바른 해결책을 찾는 데 도움이 되기를 바란다."
좋은 의사 결정이란
책의 말미에 나오는 이 문장이 이 책을 읽고 적용하는데에 갖추면 좋을 태도인 것 같다.
특정 기술 스택을 도입할 때 "~는 ~라이브러리를 쓴다"는 이유는 별로 좋지 않다. "어디는 ~한 이유로 ~라이브러리를 쓴다, 우리도 ~한 문제가 있으니 ~라이브러리를 써서 해결해보자"가 더 좋은 의사 결정이다.
리팩토링을 하며 든 생각들
기존 코드에서는 모든 상태가 마구잡이로 Context API를 사용했기에 굳이 전역으로 관리하지 않아도 되는 것을 전역으로 관리하고 있었다.
리팩토링을 하며 가장 중점에 둔 것은 어떤 것을 전역으로 관리하고 (Context에 남겨두고) 어떤 것을 지역으로 관리할 것인지 분류하는 것이었다. 처음엔 서비스 단위, 그 다음에는 페이지 단위, 그리고 컴포넌트 단위, 함수 단위, .. 그리고 작은 상태 단위로 코드를 분석하고 동료와 논의하며 하나씩 하나씩 Context로부터 떼어냈다.
Context로부터 의존성을 떼어내고, 복잡한 로직이 단순화되고, 모듈화가 되며 해당 컴포넌트를 어디서든 쓸 수 있게 되었고, 코드 라인을 획기적으로 줄였음에도 기능은 모두 정상적으로 작동되었다. 심지어 불필요한 렌더링을 줄여 깜빡임이나 속도도 줄었다. 근 한 달 넘게 이 부분을 두고 씨름했는데 실타래 풀리듯 문제가 풀리니 뿌듯했다.
이제는 적용할 때
이제는 전역 상태 라이브러리를 사용하며 남겨둔 전역 상태들을 '더 잘' 사용할 일만 남았다.
아직 Zustand를 쓸지 Jotai를 쓸지 결정을 못했는데, 만약 사용하게 된다 해도 우리가 처한 문제에 대응되는(즉, 해결할 수 있는) 라이브러리를 도입해야겠다.
Last updated