React state vs Zustand: 전역 상태가 필요한 기준

··설명 약 8분·작성: 해비·블로그 소개

React 로컬 state와 Context·Zustand 전역 상태는 어떤 기준으로 선택하나요?

상태 관리 도구는 값의 사용 범위와 원본을 기준으로 고릅니다. 한 컴포넌트의 입력은 로컬 state, 여러 화면의 공유 UI는 Context나 Zustand를 검토하고 서버 데이터는 따로 구분합니다.

본문 근거: zustand.docs.pmnd.rs

이 기술만 따로 실행하기

설치 없이 메모장으로 진행하는 설계 실습입니다. 이전 프로젝트는 필요하지 않습니다.

새 검색 화면의 상태 위치 정하기

입력 상황: 입력 중 검색어는 SearchBox만 사용하고, 선택한 상품 ID는 목록과 상세가 공유하며, 가격은 API 응답입니다. 이미 앱에 테마 Context가 있습니다.

먼저 각 상태의 소유자와 이유를 직접 적은 뒤 다음 해설과 비교하세요.

분류 결과와 이유 확인

입력 중 검색어 → SearchBox useState. 선택 ID → 공통 부모 또는 Zustand(화면 간 공유가 필요할 때). 가격 원본 → 서버 데이터 캐시. 테마 → 기존 Context 유지.

자가 확인: 선택 ID를 목록에서만 쓰게 요구사항을 바꾸면 Zustand가 필수일까요? 로컬 state로 범위를 줄일 수 있습니다.

완료 기준: 상태마다 원본이 한 곳이고, 사용 범위·수명·저장 여부를 설명하는 분류표를 작성했습니다. 실제 앱 이전이나 렌더링 검증을 뜻하지 않습니다.

로컬 state가 기본인 이유

상태는 가능한 한 사용하는 곳과 가까운 위치에 두는 것이 이해하기 쉽습니다.

예를 들어 검색 input에 입력 중인 문자열이 해당 컴포넌트에서만 필요하다면 useState가 가장 단순합니다.

const [keyword, setKeyword] = useState('');

이 값을 굳이 전역 store로 옮기면 다른 파일을 열어야 상태 변경 흐름을 추적할 수 있습니다.

따라서 기본 원칙은 다음과 같습니다.

한 컴포넌트 또는 가까운 하위 컴포넌트만 사용
→ useState 우선

Context는 언제 사용하는가

Context는 트리 아래 여러 컴포넌트에 같은 값을 전달할 때 유용합니다.

예를 들어 다음 값은 Context가 자연스러울 수 있습니다.

  • 테마
  • 언어
  • 인증 정보
  • 라이브러리 Provider
  • 특정 화면 트리 전체가 공유하는 값

하지만 Context에 상태와 변경 로직이 계속 추가되면 Provider가 커질 수 있습니다.

TaskProvider
├─ tasks
├─ selectedTaskId
├─ modal
├─ filter
├─ theme
├─ dispatch
└─ 여러 helper

이때 모든 값이 같은 변경 주기와 같은 구독 범위를 갖는 것은 아닙니다.

Zustand가 필요한 신호

다음 질문에 여러 번 “예”라고 답한다면 Zustand를 검토할 수 있습니다.

  1. 서로 멀리 떨어진 컴포넌트가 같은 상태를 사용하는가?
  2. props를 여러 단계 전달하고 있는가?
  3. 상태 변경 함수를 여러 컴포넌트가 함께 사용하는가?
  4. 특정 상태만 선택적으로 구독하고 싶은가?
  5. 새로고침 후 일부 상태를 복원해야 하는가?
  6. 상태 변경 흐름을 devtools로 확인하고 싶은가?

예를 들어 다음 값은 Zustand 후보입니다.

selectedTaskId
isTaskModalOpen
taskFilter
sidebarOpen
userUiPreferences

서버 상태는 왜 다른가

API 응답은 단순한 전역 변수와 다릅니다.

서버 데이터에는 다음 개념이 붙습니다.

  • 언제 가져왔는가
  • 다시 가져와야 하는가
  • 요청 중인가
  • 실패했는가
  • 캐시가 유효한가
  • 수정 후 어떤 쿼리를 갱신해야 하는가

그래서 서버 원본 데이터는 TanStack Query가 담당하는 편이 자연스럽습니다.

API tasks
→ TanStack Query


selectedTaskId
→ Zustand

두 라이브러리를 함께 사용할 때 가장 중요한 규칙은 원본을 중복 저장하지 않는 것입니다.

상태 선택 기준

useState

다음 조건이면 우선 useState를 사용합니다.

  • 컴포넌트 내부에서만 사용
  • 짧은 생명주기
  • 공유 필요 없음
  • 새로고침 후 유지 필요 없음

Context

다음 조건이면 Context를 검토합니다.

  • 트리 전체가 같은 값을 읽음
  • 값의 변경 빈도가 높지 않음
  • Provider 구조 자체가 의미 있음

Zustand

다음 조건이면 Zustand를 검토합니다.

  • 여러 화면에서 같은 클라이언트 상태 공유
  • 선택 구독 필요
  • action을 store에 모으고 싶음
  • persist나 devtools가 필요함

잘못된 기준: “전역이면 Zustand”

전역이라는 이유만으로 store에 넣으면 안 됩니다.

예를 들어 modal이 앱 전체에 하나 있다고 해도 실제로 특정 페이지에서만 사용한다면 해당 페이지 내부 state가 더 단순할 수 있습니다.

중요한 것은 범위와 생명주기입니다.

학습 완료 체크리스트

  • useState를 기본 선택으로 둘 수 있습니다.
  • Context와 Zustand의 차이를 설명할 수 있습니다.
  • 서버 상태를 Zustand에 중복 저장하면 안 되는 이유를 설명할 수 있습니다.
  • 공유 범위와 생명주기로 상태 위치를 결정할 수 있습니다.

선택 실습: 할 일 앱에 적용하기

TaskFlow는 작업을 추가·완료·삭제하는 할 일 앱입니다. 이 선택 실습에서는 여러 화면이 공유하는 상태를 관리합니다.

TaskFlow 상태를 세 그룹으로 나눕니다.

선택: 연재 프로젝트와 전체 자료

연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.

TaskFlow 프로젝트 적용

TaskFlow 직접 실행

docs/state-ownership.md에서 tasks·filter·선택·모달·입력의 소유자를 나눕니다. 전체 앱은 계속 Context로 동작합니다.

예제 크게 보기 (새 탭)

중간부터 시작하는 전체 실습 파일

기존 React Context TaskFlow의 작업 목록·추가·완료·삭제 화면을 이어서 확장합니다.

이 글의 시작본은 직전 프로젝트 단계 완료본 전체입니다. 이미 직전 단계를 마쳤다면 그 폴더를 이어 쓰셔도 됩니다. Node.js 24.x와 npm을 사용하며 패키지 버전은 package-lock.json에 고정했습니다.

시작본에서 직접 바꾸기

개념·경계 설계 단계라 실행 앱을 바꾸지 않고 ZIP의 문서를 직접 작성·비교합니다.

  1. docs/state-ownership.md — 이전 단계에 없던 파일을 만듭니다.

변경 이유와 결과: docs/state-ownership.md에서 tasks·filter·선택·모달·입력의 소유자를 나눕니다. 전체 앱은 계속 Context로 동작합니다.

완료본의 docs/changes.patch에서 시작·완료 차이를, docs/current-step.md에서 파일별 적용 위치를 확인할 수 있습니다. 아래 전체 파일을 같은 경로에 반영한 다음 실행하세요.

docs/state-ownership.md 전체 파일

# 상태 소유권

| 값 | 소유자 | 저장 |
|---|---|---|
|입력 문자열|TaskForm|아니오|
|tasks|현재 Context, 06부터 Zustand|메모리만|
|필터·선택|04·05부터 Zustand|10부터 선택 저장|
|모달|05부터 Zustand|아니오|

완료 확인

  1. 시작본은 직전 완료본과 동일한 ZIP입니다. 압축을 푼 뒤 npm ci로 잠금 파일의 의존성을 설치합니다.
  2. npm run dev 실행 후 터미널의 localhost 주소를 엽니다.
  3. docs/state-ownership.md에서 tasks·filter·선택·모달·입력의 소유자를 나눕니다. 전체 앱은 계속 Context로 동작합니다.
  4. npm run typecheck와 npm run build가 성공하면 다음 프로젝트 단계로 갑니다.

이 체크포인트는 TypeScript 검사·프로덕션 빌드와 실제 소스를 사용한 JSDOM/상태 시나리오 검증을 통과했습니다. 별도 실제 브라우저 화면 검수는 이 파일 검증 범위에 포함하지 않았습니다.

아래 부분 예제는 핵심 변경을 설명합니다. 실행에 필요한 전체 파일과 보조 타입·API는 위 완료본에서 확인하세요.

이어서 만들기: Zustand 학습 로드맵: store·action·selector·persist 순서의 적용 결과에서 시작합니다. 아래 체크포인트 이름은 단계 식별자이며 다운로드 파일명이나 Git 명령이 아닙니다.

TaskForm 임시 입력·selectedTaskId·서버 데이터 후보를 한 줄씩 분류표에 기록합니다. 아직 실행 코드를 이전하지 않으며 이 분류표가 이번 단계 결과물입니다.

이전 체크포인트

zustand-step-01-roadmap

현재 상태

TaskFlow에는 reducer + Context 구조가 있고, 앞으로 어떤 상태를 Zustand로 옮길지 후보만 정리된 상태입니다.

이번 목표

TaskFlow 상태를 세 그룹으로 나눕니다.

로컬 UI 상태
클라이언트 전역 상태
서버 상태 후보

상태 분류

TaskForm 입력값
→ 로컬 state


selectedTaskId
→ Zustand


isTaskModalOpen
→ Zustand


filter
→ Zustand


tasks
→ 현재 로컬 학습 데이터이므로 Zustand 이전 실습
→ 서버 API 도입 후에는 TanStack Query 원본으로 변경

상태 타입 초안

아직 Zustand를 설치하지 않습니다. 앞으로 만들 store의 상태 모양만 먼저 정리합니다.

type TaskFilter = 'all' | 'active' | 'completed';


type TaskUiState = {
  selectedTaskId: string | null;
  isTaskModalOpen: boolean;
  filter: TaskFilter;
};

이렇게 타입을 먼저 정리하면 다음 회차에서 store를 만들 때 상태의 책임이 명확해집니다.

Before / After

Before
상태가 필요해질 때마다 Context 또는 useState에 추가


After
로컬 state / Zustand / 서버 상태의 경계를 먼저 결정

현재 확장 단계

✓ 1. 전체 로드맵
→ 2. 상태 경계 결정
○ 3. Store 구조 설계
○ 4. Zustand 설치
○ 5. state 이전
○ 6. action 이전
○ 7. selector
○ 8~15. 최적화·persist·실무 구조

완료 체크포인트

zustand-step-02-boundary

다음 학습

다음 글에서는 Zustand의 store, state, action 구조를 reducer + Context와 비교하고 실제 설치 전에 useTaskStore의 모양을 설계합니다.

다음 글: Zustand란? React 상태 관리 선택 기준과 기본 Store

공식 문서

글 소개·학습 준비·전체 목차
React와 Zustand: 전역 상태 선택 기준 표지

Zustand · 2 / 15 · 필수 학습 · 상태 경계

React에서 상태 관리 도구를 선택할 때 가장 먼저 해야 할 일은 라이브러리를 고르는 것이 아니라 상태의 범위를 구분하는 것입니다.

useState로 충분한 값을 Zustand에 넣으면 store가 불필요하게 커지고, 반대로 여러 컴포넌트가 공유해야 하는 값을 로컬 state로만 관리하면 props 전달과 상태 동기화가 복잡해집니다.

이번 글에서는 React state, Context, Zustand를 어떤 기준으로 나누는지 설명하고, 하단에서는 TaskFlow의 상태를 실제로 분류합니다.

이번 글에서 배우는 내용

  • 로컬 state가 적합한 경우
  • Context가 적합한 경우
  • Zustand가 적합한 경우
  • 서버 상태를 Zustand와 분리하는 이유
  • 전역 상태를 판단하는 질문
  • TaskFlow 상태 분류

목차

이 글이 도움이 되었나요?

조회 중

읽음 기록

읽음 — / 전체 15개 · 필수 14개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기