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