FEInterview Prep

react · high priority

React 상태관리 라이브러리 — Zustand · Jotai · Redux Toolkit · TanStack Query

단일 스토어 vs 아톰 vs 서버 상태 — 구독 모델로 풀어 보는 라이브러리 선택

advanced 난이도5시간토스카카오네이버배민당근라인
시작 전
이해도
매우 낮음

학습 개요

탄생 배경

쉬운 설명

복잡한 개념을 실생활 비유로 설명합니다.

주방의 도구 분리

*Redux/Zustand* 는 *공용 식료품 창고* — 모든 재료(상태) 가 한 곳에. *Jotai* 는 *각자 라벨이 붙은 작은 통* 들 — 필요한 통만 꺼내 씀. *Valtio* 는 *스마트 냉장고* — 누가 어떤 칸 문을 열었는지 자동 기억. *TanStack Query* 는 *마트와의 계약* — 우리가 식재료 원본을 가지고 있는 게 아니라, 마트(서버) 에 *언제든 갱신할 수 있게* 약속해 둔 캐시일 뿐. 큰 팀은 보통 *마트 계약(TanStack) + 작은 통(Jotai) 또는 공용 창고(Zustand)* 의 조합으로 운영합니다.

핵심 개념

두 상태의 본질적 차이
클라이언트 상태서버 상태
예시UI 토글, 폼 입력, 모달, 테마API 응답, 사용자 정보, 게시물 목록
소유자브라우저서버 (브라우저는 *복사본* 만 들고 있음)
특성동기, 즉시 반응비동기, 캐싱, *stale 가능*
요구 기능set/get, 구독캐시, 무효화, 백그라운드 재페치, 재시도, 폴링
적합 도구Zustand · Jotai · Valtio · ReduxTanStack Query · SWR · RTK Query · Apollo

안티 패턴 — *서버 응답을 클라이언트 스토어에 통째로 저장*

Redux/Zustand 같은 클라이언트 스토어에 API 응답을 저장하면 *캐싱·무효화·재페치·동기화* 를 모두 *직접 구현* 해야 합니다 (낙관적 업데이트, focus 시 재페치, polling, retry, dedupe…). 결과적으로 *반쯤 만든 React Query* 가 됩니다. 이 모든 게 TanStack Query 에는 *기본 기능* 입니다.

2026 의 합의 — *2 라이브러리 조합*

대부분의 신규 React 앱은 *TanStack Query (서버 상태) + Zustand 또는 Jotai (클라이언트 상태)* 의 두 라이브러리만 씁니다. 이 조합이 *각 도구가 잘하는 영역* 에 집중하게 해주고, 단일 거대 스토어보다 디버깅도 쉽습니다.

실무 적용

어떤 상황에서 사용하는가

커머스 앱 — 게시물 목록(API), 장바구니(클라이언트), 사용자 인증(API + 클라이언트), 모달 토글, 폼 입력. 팀은 5–10인.

어떻게 적용하는가

(1) *서버 상태* 는 모두 *TanStack Query* 로 — 게시물 목록, 사용자 정보 페치, 카탈로그. (2) *장바구니* 는 클라이언트 상태로 *Zustand* — 단순한 add/remove + persist 미들웨어로 localStorage 동기화. (3) *모달 토글, 사이드바 open* 같은 UI 상태는 *Jotai* 로 atom 단위. (4) *폼* 은 react-hook-form 로컬 상태 — 글로벌화하지 않음. (5) *Context* 는 DI 만 — 테마, i18n 같은 *거의 안 변하는* 값. (6) Redux 는 도입하지 않음 — 도메인 복잡도가 그 정도가 아니므로. (7) Zustand 셀렉터 패턴 + `shallow` 가이드를 팀에 문서화. (8) TanStack Query 의 `queryKey` 컨벤션을 정해 무효화 패턴 일관화.

흔한 실수와 안티패턴

  • TanStack Query 자리에 Zustand 로 API 캐싱을 직접 구현해 *반쯤 만든 React Query* 가 됨.
  • Context 한 개에 모든 글로벌 상태를 쌓아 *광범위 리렌더* 발생.
  • Zustand 객체 셀렉터로 매 렌더 새 reference → 무한 루프.
  • Redux Toolkit 을 도메인 복잡도와 무관하게 도입해 보일러플레이트가 코드량 다수.
  • Atom 을 *너무 작게* 쪼개 derived atom 그래프가 복잡해 디버깅 어려움.

흔한 오해

오해

"Zustand 가 Redux 보다 항상 좋다."

교정

복잡 도메인 + 큰 팀에선 Redux Toolkit 의 *액션 일관성·time-travel DevTools* 가 여전히 결정적.

왜 중요

Zustand 는 가벼움 + 자유도가 강점. 자유도 자체가 큰 팀에선 일관성 비용이 됨.

오해

"Context 가 곧 상태 관리 라이브러리다."

교정

Context 는 *DI 도구* 이지 selector 기반 구독 모델이 아니다. 자주 변하는 상태에는 부적절.

왜 중요

Context value 변경은 *모든 구독자* 에게 전파 — selector 가 없어 잘게 쪼갠 구독이 안 됨.

오해

"서버 상태는 결국 Redux/Zustand 에 두면 되지 않나."

교정

TanStack Query 가 기본 제공하는 *캐시·무효화·재페치·dedupe·낙관적 업데이트* 를 직접 구현하면 결국 비슷한 라이브러리를 만들게 됨.

왜 중요

서버 상태 = *서버가 진실의 원본* + *우리는 캐시* 라는 모델. 클라이언트 스토어와 의미가 다름.

오해

"Jotai 와 Recoil 은 거의 동일하다."

교정

아톰 모델은 비슷하나, Recoil 은 *Facebook 측 유지보수가 사실상 정체* 됐고, Jotai 가 표준 자리를 차지.

왜 중요

Jotai 는 React Suspense·SSR 호환이 활발히 갱신, 번들도 더 작음. 신규는 Jotai 권장.

면접 질문

중급토스카카오배민당근

답변 방향 힌트

"보일러플레이트", "DevTools time-travel", "도메인 복잡도".

반드시 언급할 키워드

  • Zustand: 훅 기반, 보일러플레이트 적음, 번들 ~3KB
  • RTK: createSlice 로 보일러 줄였지만 여전히 Provider/store 구성, 번들 ~15KB
  • RTK 강점: time-travel DevTools, 큰 팀에서 패턴 강제, RTK Query 통합
  • Zustand 강점: 작은 도메인·작은 팀에서 빠른 시작, 자유도
  • 도메인 복잡도가 한계점 — Zustand 로 시작 → 필요 시 RTK

예상 꼬리 질문

  • Redux DevTools 의 time-travel 이 *어떤 종류의 버그* 디버깅에 결정적입니까?
  • Zustand store 가 *너무 커지면* 어떤 신호가 보일까요?

자기 점검

클라이언트 상태와 서버 상태를 *2 라이브러리* 로 분리해야 하는 이유 한 문장.

기대 키워드

캐시무효화백그라운드 재페치서버가 진실의 원본

자주 하는 오해

"한 라이브러리에 다 담으면 되지" — 결국 캐시·무효화 시스템을 직접 만들게 된다.

Context API 의 *근본적 한계* 한 가지를 한 문장으로 답하라.

기대 키워드

selector 없음value 변경 = 모든 구독자 리렌더슬라이스 구독 불가

자주 하는 오해

"Context 가 느리다" 가 아니라 *selector 가 없는 게 본질* — 큰 객체를 한 Context 에 담은 *사용 방식* 이 문제.

Zustand 의 객체 셀렉터가 *매 렌더 새 reference* 를 반환할 때 해결책 두 가지는?

기대 키워드

필드별 selectorshallow equalityreselect

자주 하는 오해

"useMemo 로 selector 결과를 메모이즈" — selector 호출 시점이 매 렌더라 useMemo 의 값이 매번 새로 만들어진다.

Redux Toolkit 이 *여전히* 답이 되는 시나리오 두 가지를 답하라.

기대 키워드

복잡 도메인큰 팀time-travel DevTools액션 패턴 강제

자주 하는 오해

"Redux 는 옛날 거" — RTK 는 보일러플레이트를 대부분 제거했고, time-travel 디버깅의 가치가 큰 도메인에서 결정적이다.

학습 자료