본문 바로가기

상태 관리 라이브러리 소개

Prop Drilling과 공유 상태 문제를 이해하고 Context·Redux Toolkit·Zustand·Jotai의 모델과 적용 규모를 비교합니다.

앞 절에서는 React 핵심 개념(컴포넌트, JSX, state, props)을 학습하며 선언형 UI 기초를 다졌습니다.

특히 useState로 컴포넌트 내부 데이터를 관리하고, props로 부모-자식 간 데이터를 전달하는 방식을 확인했습니다.

하지만 애플리케이션의 규모가 커지고, 여러 컴포넌트가 동시에 동일한 데이터를 공유하거나 업데이트해야 하는 상황이 발생하면 문제가 복잡해지기 시작합니다.

예를 들어, 웹사이트의 상단 내비게이션 바에 표시되는 로그인 사용자 이름과, 프로필 페이지에 표시되는 사용자 이름이 동일한 데이터를 공유해야 할 때를 생각해봅시다.

공유 상태가 있다는 사실만으로 Prop Drilling인 것은 아닙니다. 그 값을 쓰지 않는 중간 컴포넌트까지 여러 단계로 props를 전달해야 하는 상황을 Prop Drilling이라고 합니다.

컴포넌트가 적을 때는 props 전달만으로도 충분합니다. 중간 전달 경로가 길어져서 어떤 화면이 값을 쓰는지 추적하기 어려워지는지 먼저 확인합니다.

또한, 여러 컴포넌트에서 동일한 데이터를 독립적으로 관리하다 보면 데이터 불일치가 발생하기 쉽습니다.

이 문제를 해결하고 애플리케이션 상태를 일관되고 예측 가능하게 관리하기 위해 상태 관리 라이브러리(State Management Library)가 등장했습니다.

이번 장에서는 상태 관리 라이브러리의 필요성과 주요 라이브러리의 특징/작동 방식을 간략히 정리합니다.

공유 상태 갱신과 구독 전파 루프 — 상태 추적

의도·reducer·store·selector 비교·렌더/생략에서 저장 상태와 구독 결과가 실제로 달라진다. 작은 업데이트 예로 값 변경과 렌더 생략을 구분할 수 있다.

공유 상태 갱신과 구독 전파 루프

DATA-FLOW · B99 WEB FUNDAMENTALS

공유 상태 갱신과 구독 전파 루프

Zustand처럼 selector를 구독하는 외부 store의 개념 예시다. 사용자 의도로 상태를 갱신한 뒤 선택한 값의 비교 결과에 따라 해당 구독자의 렌더를 예약한다.

공유 상태 갱신과 구독 렌더 루프 UI 의도가 update rule을 통해 단일 store를 갱신하고 selector 비교 결과가 바뀐 구독자만 렌더한 뒤 다음 의도로 돌아가는 루프 UI 의도click · input · effectupdate ruleaction · reducer단일 store새 상태 저장·알림selector 비교이전 파생값 ↔ 새 값값 변경?해당 구독자 렌더React가 필요 DOM 반영렌더 생략동일 참조 / 값YESNONEXT INTENT · STORE IS THE SOURCE OF TRUTH
공유 상태 갱신과 구독 렌더 루프일곱 노드와 일곱 경로: 의도·규칙·store·selector·변경 판단을 거쳐 YES는 구독자 렌더, NO는 이 store 구독의 렌더 생략으로 갈라진다. 렌더 경로는 다음 의도로 복귀한다. YESNO UI 의도click · input · effectupdate ruleaction · reducer단일 store새 상태 저장·알림selector 비교이전 파생값 ↔ 새 값값 변경?해당 구독자 렌더React가 필요한DOM 반영렌더 생략동일 참조 / 값

변경 없음 분기는 이 store 구독으로 인한 렌더 예약을 생략한다. props·Context 등 다른 원인으로 렌더될 수 있다. Context와 Jotai의 atom 모델은 이 selector 예시와 갱신 방식이 다르다. 두 결과 모두 다음 사용자 의도를 기다린다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
관계도의 보충 설명
원도표의 전체 관계·조건·설명
구분내용
원도표 노드·간선·영역 라벨React가 필요한 DOM 변경을 반영
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
현재 구독 결과store: 현재 snapshot / selector: 소비자별 결과두 consumer는 각 selector 결과를 기억한다같은 store도 선택한 값은 다를 수 있다
action 적용store: 다음 snapshot / selector: 다시 계산reducer가 새 store snapshot을 만든다전이 규칙이 현재 state와 action으로 다음 state를 만든다
변한 선택만 구독 갱신변경 selector: 갱신 요청 / 같은 selector: 이 구독 요청 건너뜀선택 결과가 달라진 구독 경로만 렌더를 요청한다별도 부모 렌더 경로까지 막는 보장은 아니다

공유 상태 갱신과 구독 전파 루프 — 상태 추적

의도·reducer·store·selector 비교·렌더/생략에서 저장 상태와 구독 결과가 실제로 달라진다. 작은 업데이트 예로 값 변경과 렌더 생략을 구분할 수 있다.

공유 상태 갱신과 구독 전파 루프

DATA-FLOW · B99 WEB FUNDAMENTALS

공유 상태 갱신과 구독 전파 루프

Zustand처럼 selector를 구독하는 외부 store의 개념 예시다. 사용자 의도로 상태를 갱신한 뒤 선택한 값의 비교 결과에 따라 해당 구독자의 렌더를 예약한다.

공유 상태 갱신과 구독 렌더 루프 UI 의도가 update rule을 통해 단일 store를 갱신하고 selector 비교 결과가 바뀐 구독자만 렌더한 뒤 다음 의도로 돌아가는 루프 UI 의도click · input · effectupdate ruleaction · reducer단일 store새 상태 저장·알림selector 비교이전 파생값 ↔ 새 값값 변경?해당 구독자 렌더React가 필요 DOM 반영렌더 생략동일 참조 / 값YESNONEXT INTENT · STORE IS THE SOURCE OF TRUTH
공유 상태 갱신과 구독 렌더 루프일곱 노드와 일곱 경로: 의도·규칙·store·selector·변경 판단을 거쳐 YES는 구독자 렌더, NO는 이 store 구독의 렌더 생략으로 갈라진다. 렌더 경로는 다음 의도로 복귀한다. YESNO UI 의도click · input · effectupdate ruleaction · reducer단일 store새 상태 저장·알림selector 비교이전 파생값 ↔ 새 값값 변경?해당 구독자 렌더React가 필요한DOM 반영렌더 생략동일 참조 / 값

변경 없음 분기는 이 store 구독으로 인한 렌더 예약을 생략한다. props·Context 등 다른 원인으로 렌더될 수 있다. Context와 Jotai의 atom 모델은 이 selector 예시와 갱신 방식이 다르다. 두 결과 모두 다음 사용자 의도를 기다린다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.

관계도의 보충 설명

원도표의 전체 관계·조건·설명
구분내용
원도표 노드·간선·영역 라벨React가 필요한 DOM 변경을 반영
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
현재 구독 결과store: 현재 snapshot / selector: 소비자별 결과두 consumer는 각 selector 결과를 기억한다같은 store도 선택한 값은 다를 수 있다
action 적용store: 다음 snapshot / selector: 다시 계산reducer가 새 store snapshot을 만든다전이 규칙이 현재 state와 action으로 다음 state를 만든다
변한 선택만 구독 갱신변경 selector: 갱신 요청 / 같은 selector: 이 구독 요청 건너뜀선택 결과가 달라진 구독 경로만 렌더를 요청한다별도 부모 렌더 경로까지 막는 보장은 아니다

Prop Drilling 문제와 상태 관리의 필요성

React와 같은 컴포넌트 기반 프레임워크에서 데이터는 기본적으로 부모에서 자식으로 props를 통해 전달됩니다.

이 방식은 간단한 애플리케이션에서는 효과적이지만, 다음과 같은 경우에 한계에 부딪힙니다.

Prop Drilling (Prop 지옥)
  • 컴포넌트 트리의 깊이가 깊어질수록, 데이터를 필요로 하지 않는 중간 컴포넌트들까지도 props를 전달해야 하는 상황이 발생합니다.
  • 이는 코드를 지저분하게 만들고, 어떤 데이터가 어디로 전달되는지 추적하기 어렵게 만들며, 리팩토링 시에도 큰 부담이 됩니다.
// 데이터 전달 경로를 나타낸 개념도이며 실행할 JSX가 아닙니다.
// 예시: Prop Drilling
<App>
  <Header user={user}> // user 데이터를 Header에 전달
    <Navbar user={user}> // Navbar는 user를 필요로 하지만 Header는 필요 없음
      <Avatar user={user} /> // Avatar는 user를 필요로 함
    </Navbar>
  </Header>
  <Main />
</App>
여러 컴포넌트 간의 상태 공유 및 동기화 어려움
  • 서로 다른 계층에 위치한 컴포넌트들이 동일한 상태를 공유하고 업데이트해야 할 때, props와 callback 함수를 사용하는 방식으로는 구현이 복잡해지고 오류가 발생하기 쉽습니다.
  • 예를 들어, 장바구니 담기 버튼이 있는 상품 상세 컴포넌트와 총 상품 개수를 표시하는 장바구니 아이콘 컴포넌트가 멀리 떨어져 있을 때, 두 컴포넌트가 동일한 장바구니 상태를 참조하고 업데이트해야 합니다.

이러한 문제들을 해결하기 위해, 애플리케이션 전역 또는 특정 도메인의 공유 상태(Shared State)를 중앙에서 관리하는 메커니즘이 필요해졌습니다.

이것이 바로 상태 관리 라이브러리의 핵심 역할입니다.


상태 관리 라이브러리의 기본 개념

상태 관리 라이브러리는 다양한 모델을 사용합니다. 아래는 중앙 store와 구독을 사용하는 대표적인 흐름이며, 뒤에서 Context·atom 기반 모델도 구분합니다.

  • 중앙 집중식 저장소 (Centralized Store): store 모델은 그 저장소에 맡긴 공유 상태를 모아 관리합니다. 컴포넌트의 모든 지역 상태까지 한곳에 넣을 필요는 없습니다.
  • 상태 변경 규칙: 상태는 정해진 규칙(Action, Mutation, Reducer 등)에 의해서만 변경될 수 있습니다. 이는 상태 변경의 예측 가능성을 높이고 디버깅을 용이하게 합니다.
  • 단방향 데이터 흐름: UI의 이벤트가 상태 변경을 요청하고, 구독은 선택한 상태의 변경을 UI에 연결합니다. 어떤 컴포넌트를 다시 렌더할지는 구독 범위와 선택 값의 비교 방식 등에 따라 달라집니다.

주요 상태 관리 라이브러리 소개

상태를 저장하기 전에 원천과 범위를 나눈다

STATE OWNERSHIP

상태를 저장하기 전에 원천과 범위를 나눈다

먼저 파생 가능한 값인지 확인하고, 남은 상태의 원천과 공유 범위를 판단한다. 도구는 필요와 성능을 확인하며 선택한다.

  1. 기존 값에서 계산할 수 있는가?

    예: 필터 결과·합계는 렌더 중 순수하게 계산한다. 비싼 계산은 측정한 뒤 메모화를 검토한다. 별도 상태로 중복 저장하지 않는다.

  2. 원본이 서버에 있는가?

    예: 요청·로딩·오류·재검증을 관리한다. 화면 간 캐시 공유가 필요하면 SWR·TanStack Query 같은 query cache를 검토한다. 단순 요청에 도구가 항상 필요한 것은 아니다.

  3. 다른 컴포넌트와 공유하는가?

    아니요: 필요한 컴포넌트에 local state를 둔다. 예: 가장 가까운 공통 소유자로 올리고 props·callback으로 전달하는 방식부터 살핀다.

  4. 공유 범위와 갱신 비용은 어떤가?

    트리 안의 공통 값은 Context(+ useReducer)로 전달할 수 있다. 선택적 구독·복잡한 갱신 규칙이 필요하면 Redux Toolkit·Zustand 같은 store나 Jotai의 atom 모델을 검토한다.

원본은 한곳에, 파생값은 계산으로

서버 응답과 편집 중인 폼 초안은 수명과 책임이 다르다. 공유 상태를 만들기 전에 어떤 값이 원본이며 언제 갱신·폐기되는지 정한다.

갱신 빈도만으로 Context와 store를 강제 분류하지 않는다. Context의 전달 범위, 구독할 값, 팀의 갱신 규칙과 실제 렌더 비용을 함께 비교한다.

프론트엔드 생태계에는 다양한 상태 관리 라이브러리들이 존재하며, 각각의 특징과 장단점을 가지고 있습니다.

여기서는 가장 널리 사용되거나 주목받는 라이브러리들을 소개합니다.

Redux

새 Redux 코드는 공식 권장 방식인 Redux Toolkit으로 작성합니다. configureStore는 store 설정을, createSlice는 상태별 갱신 규칙과 action 생성을 돕습니다. 기존 Redux 개념은 그대로 유지하면서 수작업 설정을 줄입니다. Redux 공식 안내를 참고합니다.

Redux는 React와 가장 오랫동안, 그리고 널리 함께 사용되어 온 상태 관리 라이브러리입니다.

예측 가능한 상태 컨테이너(Predictable State Container)를 지향하며, Flux 아키텍처 패턴을 기반으로 합니다.

  • 핵심 원칙

    단일 진실 원천 (Single source of truth): Redux에 맡긴 상태는 하나의 저장소(Store)의 상태 트리에 둡니다. 앱의 모든 지역 상태나 별도로 관리하는 서버 데이터까지 Redux에 넣으라는 의미는 아닙니다.

    상태는 읽기 전용 (State is read-only): 상태를 직접 변경할 수 없으며, 반드시 액션(Action)을 디스패치(Dispatch)해야만 변경할 수 있습니다.

    순수 함수로 변경 (Changes are made with pure functions): 상태를 변경하는 로직은 리듀서(Reducer)라는 순수 함수를 통해 이루어집니다.

    리듀서는 이전 상태와 액션을 받아 새로운 상태를 반환합니다. Redux Toolkit의 createSlice 안에서는 Immer가 제공하는 임시 draft에 state.count += 1처럼 작성할 수 있습니다. Immer가 새 불변 상태를 만들기 때문이며, store에서 읽은 원본을 어디서나 직접 수정해도 된다는 뜻은 아닙니다.

  • 장점
    • 예측 가능한 상태 변화: 엄격한 규칙으로 상태 변화를 예측하고 추적하기 용이합니다.
    • 강력한 디버깅 도구: Redux DevTools를 통해 상태 변화를 시간여행(Time-travel Debugging)하며 디버깅할 수 있습니다.
    • 확장성: 미들웨어(Middleware)를 통해 비동기 처리, 로깅 등 다양한 기능을 추가할 수 있습니다 (예: Redux Thunk, Redux Saga).
  • 단점
    • 배워야 할 개념: store·action·reducer·구독의 역할을 이해해야 합니다. 초기 Redux 예제의 수작업 보일러플레이트를 Redux Toolkit에도 그대로 적용해 비교하지 않습니다.
    • 작은 애플리케이션에는 과할 수 있음: 간단한 애플리케이션에는 오버헤드가 될 수 있습니다.

Redux 개념 흐름 View (React Component) -> Action Dispatch -> Middleware (Optional) -> Reducer -> Store (State Update) -> View (Re-render)

Zustand

Zustand는 store 생성과 선택한 값의 구독을 제공하는 상태 관리 라이브러리입니다.

React Hooks 기반의 가벼운 API를 제공합니다.

  • 특징
    • 간결한 API: create로 store를 만들고 set으로 갱신 규칙을 작성하며 selector로 필요한 값을 고릅니다.
    • 보일러플레이트 최소화: 상태 정의와 구독이 매우 직관적입니다.
    • 스토어 구독 기반: set으로 상태를 갱신하고 구독자에게 알립니다. React에서는 선택한 상태가 바뀌었을 때 렌더를 갱신하도록 구성합니다.
    • React 외부에서도 사용 가능: React 외의 순수 JavaScript 환경에서도 사용할 수 있습니다.
  • 장점
    • 작은 시작 예제: store와 구독을 적은 설정으로 연결할 수 있습니다. 실제 렌더 비용은 구독 범위와 갱신 방식으로 확인합니다.
    • 작은 번들 크기: 비교적 가벼운 편이지만, 앱에 포함되는 크기는 사용하는 기능과 번들 구성에 따라 달라집니다.
    • 유연성: 다양한 패턴과 함께 사용할 수 있습니다.
  • 단점
    • 명확한 규칙 부재: Redux처럼 엄격한 규칙을 강제하지 않으므로, 팀 내에서 상태 관리 규칙을 명확히 정해야 합니다.

Jotai

Jotai는 Atom을 조합해 공유 상태와 파생 상태를 구성하는 경량 상태 관리 라이브러리입니다.

"상태는 원자적(atomic)이다"라는 철학을 따릅니다.

  • 특징
    • Atom 기반: 상태의 최소 단위를 Atom으로 정의합니다.
    • 간결하고 원자적: 작은 상태 단위를 독립적으로 정의하고 필요한 컴포넌트에서 조합합니다.
    • TypeScript 친화적: 타입 추론이 잘 되도록 설계되었습니다.
  • 장점
    • 필요한 상태 단위의 구독: 사용하는 atom을 중심으로 컴포넌트를 연결합니다. 번들 크기는 실제 빌드에서 확인합니다.
    • 유연성: 다양한 사용 사례에 적용 가능.
    • 파생 상태 구성: 다른 atom을 읽어 계산하는 atom을 만들 수 있습니다. 같은 합계를 별도 원본으로 저장할 필요를 줄입니다.

React Context API (+ useReducer)

Context는 상태 자체를 저장하는 별도 store가 아닙니다. 부모의 useState 또는 useReducer가 상태를 관리하고 Provider가 그 값을 전달합니다. React 자체적으로 제공하는 Context API는 props를 일일이 전달하지 않고도 컴포넌트 트리를 통해 데이터를 공유할 수 있는 방법을 제공합니다.

useReducer 훅과 함께 사용하면 Redux와 유사한 패턴으로 복잡한 상태 관리가 가능합니다.

  • 장점
    • 별도 라이브러리 설치 불필요: React에 내장되어 있어 추가 의존성이 없습니다.
    • Props Drilling 해결: 중간 컴포넌트가 props를 넘겨받을 필요 없이 필요한 컴포넌트에서 직접 데이터를 구독할 수 있습니다.
  • 단점
    • 전역 상태의 변화: Provider의 이전 값과 다음 값을 Object.is로 비교해 달라지면 그 Context를 읽는 하위 컴포넌트가 새 값을 받으며 렌더합니다. Context를 읽지 않는 모든 자식이 반드시 Context 때문에 렌더한다는 뜻은 아닙니다. 전달할 값을 관심사별로 나누고 실제 비용을 측정합니다.
    • 분리된 스토어 관리 어려움: 관심사별 Context를 나눌 수 있지만 Provider 구성과 각 값의 변경 책임을 팀에서 정해야 합니다.
    • Redux DevTools와 같은 강력한 디버깅 도구 부재: 상태 변화 추적이 상대적으로 어렵습니다.

활용: 전역적으로 필요한 테마 설정, 인증 정보 등 자주 변경되지 않거나 컴포넌트 트리를 따라 깊게 전달되는 데이터에 적합합니다.

복잡한 비즈니스 로직이나 빈번한 상태 업데이트가 필요한 경우에는 Redux Toolkit, Zustand, Jotai 같은 전용 라이브러리가 더 적합할 수 있습니다.

상태 관리 도구를 고를 때는 상태의 범위를 먼저 나누면 판단이 쉬워집니다.


상태 관리 전략 정리

상태 관리 도구를 고를 때는 라이브러리 이름보다 상태가 머무는 범위와 갱신 빈도를 먼저 나눠야 합니다.

Redux Toolkit, Zustand, Jotai, Context API와 useReducer는 상태 범위와 갱신 규칙에 따라 선택지가 됩니다.

상태의 범위와 변경 규칙을 함께 보면 어떤 관리 방식을 고를지 빠르게 판단할 수 있습니다.

사례: 장바구니 값의 원본을 나누기

상품 목록은 서버가 원본이고, 현재 펼친 상품 설명은 한 화면에서만 쓰는 값입니다. 장바구니의 상품 ID와 수량은 여러 화면이 함께 쓰는 클라이언트 상태라고 가정합니다. 서버에 저장하는 장바구니라면 원본과 동기화 책임도 다시 판단해야 합니다.

값첫 관리 위치이유
상품 목록·가격 응답요청 결과, 필요하면 query cache서버 갱신·로딩·오류·재검증을 함께 다룸
설명 펼침 여부해당 컴포넌트의 local state다른 화면과 공유할 필요가 없음
장바구니 상품 ID·수량공통 부모, 필요하면 공유 store두 화면이 같은 원본을 읽고 갱신함
총수량장바구니에서 계산같은 정보를 별도 state로 복제하면 불일치 가능

공유 store를 쓸 경우의 최소 흐름은 다음과 같습니다. 코드에 사용하는 Zustand는 예제 프로젝트에 별도로 설치해야 하며, 이 예제에는 서버 저장 기능이 없습니다. Zustand create 공식 문서를 참고합니다.

import { create } from 'zustand';

const useCartStore = create((set) => ({
  quantities: { mouse: 1, keyboard: 2 },
  addOne: (id) => set((state) => ({
    quantities: {
      ...state.quantities,
      [id]: (state.quantities[id] ?? 0) + 1,
    },
  })),
}));

export function CartCount() {
  const total = useCartStore((state) =>
    Object.values(state.quantities).reduce((sum, quantity) => sum + quantity, 0)
  );
  return <span>장바구니 {total}개</span>;
}

export function AddMouseButton() {
  const addOne = useCartStore((state) => state.addOne);
  return <button onClick={() => addOne('mouse')}>마우스 담기</button>;
}

초기 총수량은 1 + 2 = 3입니다. 버튼을 누르면 mouse가 2인 새 quantities 객체를 만들고 총수량은 2 + 2 = 4가 됩니다. CartCount가 고른 값은 숫자이므로 이 값의 변화를 비교할 수 있습니다. selector가 매번 새 객체나 배열을 만들면 참조와 안정성 규칙도 고려해야 합니다. 같은 객체를 직접 수정한 뒤 반환하는 방식은 피합니다.

연습: 도구보다 상태 범위부터 선택하기

1. 부모와 두 자식에서만 쓰는 체크박스 값이 있습니다. Prop Drilling 없이 한 단계로 전달됩니다. 공유 라이브러리를 반드시 도입해야 할까요?

풀이 확인

필수는 아닙니다. 공통 부모가 값을 갖고 두 자식에 값과 callback을 전달하면 같은 원본을 유지할 수 있습니다. 먼저 이 방식으로 해결하고, 공유 경로·갱신 규칙·실제 비용이 복잡해졌을 때 다른 도구를 비교합니다.

2. 위 장바구니에서 초기 수량은 마우스 1, 키보드 2입니다. 마우스 담기를 두 번 누른 뒤 총수량과 저장할 원본은 무엇일까요?

풀이 확인

수량은 { mouse: 3, keyboard: 2 }이고 총수량은 3 + 2 = 5입니다. store에는 수량을 원본으로 두고 총수량은 계산합니다. 총수량을 별도로 저장하면 수량을 갱신할 때 그 값도 빠짐없이 갱신해야 하므로 불일치 가능성이 늘어납니다.

3. Context Provider에서 매번 { theme: 'dark' }를 새로 만듭니다. 문자열이 같으니 Context 값도 같다고 볼 수 있을까요? atom 모델은 어떤 점이 다를까요?

풀이 확인

새 객체는 이전 객체와 Object.is 비교 결과가 다릅니다. 해당 Context를 읽는 컴포넌트가 새 값을 받습니다. 필요한 전달 값과 생성 방식을 확인하고 최적화는 측정 후 판단합니다. React Context 공식 설명을 참고합니다. Jotai의 atom 모델은 작은 상태 단위와 파생 atom을 조합하므로, 모든 도구를 하나의 중앙 store·selector 흐름으로 설명하지 않습니다.