Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

co-cherry

상태 관리 전략 비교 본문

React

상태 관리 전략 비교

co-cherry 2026. 3. 22. 22:43

https://ko.react.dev/learn/managing-state

 

State 관리하기 – React

The library for web and native user interfaces

ko.react.dev

 

Context API

컴포넌트 트리 전체에 데이터를 전달하는 React 내장 기능

별도의 라이브러리 없이 React가 기본 제공하는 전역 상태 도구 

Props drilling 없이 하위 컴포넌트에서 상태를 구독할 수 있다. 

 

장점

  • 설치 불필요(React 내장)
  • createContext → Provider → useContext 세 가지만 알면 바로 쓸 수 있음 
  • 다크모드 테마, 로그인 유저 정보, 언어 설정처럼 앱 전체에서 읽기만 하는 값(자주 변하지 않음)에 사용하기 좋음

단점

  • context 전체를 통째로 구독하는 탓에 사용하지 않는 state가 바뀌어도 해당 context를 구독 중이면 무조건 리렌더링 발생
  • 리렌더 문제를 피하기 위해 context를 쪼개면 Provider 중첩 지옥 발생
  • 비동기 처리를 위한 내장 패턴이 없어 직접 구현해야 함 

 

 

[예시] 

createContext로 Context를 생성하고 Provider로 감싸 useContext로 가져와 사용한다. 

// 1. Context 생성
const ThemeCtx = createContext();

// 2. Provider로 감싸기
function App() {
  const [theme, setTheme] = useState('dark');
  return (
    <ThemeCtx.Provider value={{theme, setTheme}}>
      <Child />
    </ThemeCtx.Provider>
  );
}

// 3. 하위 컴포넌트에서 사용
function Child() {
  const {theme} = useContext(ThemeCtx);
  return <div>{theme}</div>;
}

 

 

https://ko.legacy.reactjs.org/docs/context.html

 

Context – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

https://velog.io/@ckstn0777/Context-API%EC%9D%98-%EC%B5%9C%EB%8C%80-%EB%8B%A8%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

 

Context API의 최대 단점은 무엇일까

이번 시간에는 Context API에 대해 좀 더 자세히 알아보고 왜 Context API 대신 Redux나 Recoil 같은 전역 상태 라이브러리를 많이 사용하는지 알아보도록 하겠습니다.

velog.io

 

Zustand

여러 컴포넌트가 함께 쓰는 데이터를 한 곳에 모아두고, 필요한 컴포넌트만 골라서 쓸 수 있게 하는 상태 관리 라이브러리

Store에 상태를 담고, Action으로 변경하고, Selector로 골라서 사용한다. 

  • store 앱 전체에 공유해야 하는 데이터를 한 곳에 모아두는 전역 저장소
  • Action store 안에 함께 정의되는 상태 변경 함수, 상태를 직접 수정하는 대신 Action을 통해서만 바꾸도록 강제 → 추적 용이
  • Selector store 전체를 구독하는 대신, 필요한 값만 골라서 구독하는 함수

장점

  • 코드가 짧고 직관적임 
  • selector로 불필요한 리렌더를 막을 수 있음
  • 컴포넌트 밖( API 호출 유틸 함수, 이벤트 리스너, 타이머 등)에서도 스토어에 접근 가능 

단점

  • 비동기 처리 패턴이 내장되어 있지 않음(API 호출, 로딩 상태, 에러 처리를 직접 구현 必)
  • 매우 복잡한 상태 로직엔 한계가 있음 

패키지 설치 필요 

npm install zustand

 

[예시] 

import { create } from 'zustand';

const useCounterStore = create((set) => ({
  // 상태
  count: 0,

  // 액션 — set()으로 상태를 바꿈
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset:     () => set({ count: 0 }),
}));

export default useCounterStore;
import useCounterStore from '../store/counterStore';

function Counter() {
  // selector로 필요한 값만 골라서 구독
  const count     = useCounterStore((s) => s.count);
  const increment = useCounterStore((s) => s.increment);
  const decrement = useCounterStore((s) => s.decrement);
  const reset     = useCounterStore((s) => s.reset);

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
      <button onClick={reset}>초기화</button>
    </div>
  );
}

 

 

https://velog.io/@yiseungyun/Zustand%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC%EB%A5%BC-%EB%AA%A8%EB%A5%B4%EB%8A%94-Chill-guy%EC%9D%BC-%EB%95%8C

 

Zustand의 동작 원리를 모르는 Chill guy일 때

상태를 가져올 때 구조 분해 할당 방식과 selector가 다르게 동작하는 이유는 무엇일까?

velog.io

 

Redux Toolkit

Redux의 공식 권장 방식으로 예측 가능한 상태 관리를 위한 표준화된 Redux 솔루션

반복 코드를 대폭 줄이고, Immer(불변성 자동 처리)와 Dev Tools를 기본 내장한 공식 라이브러리 

  • Slice 상태 + 액션 묶음으로 하나의 기능 단위에 필요한 상태와 리듀서를 한 곳에 정의하는 단위 
  • Store 여러 slice를 하나로 합쳐 앱 전체의 상태를 관리하는 중앙 저장소
  • Dispatch 상태 변경을 위한 action 발송 함수, 직접 수정 불가 

RTK 데이터 흐름(단방향)
claude로 보는 redux-toolkit

상태 변경은 항상 dispatch → reducer 순서로만 일어나기 때문에, 언제 어디서 상태가 바뀌었는지 DevTools로 완벽히 추적 가능

 

장점

  • Redux DevTools로 상태 변화 추적 가능 
  • Slice 단위로 기능을 나누어 충돌 없이 협업 가능
  • Slice → Store → useSelector/useDispatch 구조로 일관성 있는 코드 구조 
  • Immer 내장
  • RTK Query를 이용한 API 호출 자동 처리(로딩 상태, 에러 처리, 중복 요청 방지 등을 자동으로 처리)

단점

  • 간단한 기능에도 필요한 파일이 많아짐
  • 컴포넌트 외부에서 상태 접근이 불편함
  • 익혀야 할 개념이 많음 

패키지 설치 필요 

npm install @reduxjs/toolkit react-redux

 

[예시] 

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
  },
});

// 액션 생성자 - 컴포넌트 dispatch 용 
export const { increment, decrement } = counterSlice.actions;

// 리듀서 - store 등록 
export default counterSlice.reducer;
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: { counter: counterReducer },
});
import { Provider } from 'react-redux';
import { store } from './store';

<Provider store={store}>
  <App />
</Provider>
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './store/counterSlice';

function Counter() {
  // 상태 읽기
  const value = useSelector((state) => state.counter.value);
  // 액션 보내기
  const dispatch = useDispatch();

  return (
    <div>
      <p>{value}</p>
      <button onClick={() => dispatch(increment())}>+1</button>
      <button onClick={() => dispatch(decrement())}>-1</button>
    </div>
  );
}

전역/지역 상태 경계 설정 기준

지역 상태(Local State)

컴포넌트 안에서만 태어나고, 그 안에서 사라지는 상태

주로 useState 사용 

  • 모달 열림/닫힘 여부
  • input에 입력 중인 텍스트
  • 폼 유효성 검사 오류

전역 상태(Global State)

여러 컴포넌트가 함께 쓰는 상태

한 곳에서 바꾸면 다른 컴포넌트도 즉시 반영 

Zustand, Context API, RTK 등 사용 

  • 로그인한 유저 정보
  • 장바구니 목록
  • 다크 모드 / 언어 설정

보통 경계 설정 기준은 아래 세 가지 경우로 판단한다. 

  1. 이 상태(값)를 2개 이상의 컴포넌트가 필요로 하는가?
  2. 한 곳에서 바꾸면 다른 곳에도 즉시 반영되어야 하는가?
  3. 페이지를 이동해도 유지되어야 하는가? 

그 외의 의견이 더 있다면 추가할 예정이다!

서버 상태 vs 클라이언트 상태 분리

클라이언트 상태(Client State)

브라우저 안에서만 사는 상태(로컬 데이터)

서버와 관계 없이 UI를 위해 존재하는 상태

  • 로딩 처리·캐싱·동기화 필요 없음
  • 우리가 직접 소유하고 제어 

서버 상태(Server State)

API를 통해 받아오는 데이터

최신값인지 신경 써야 하며, 로딩/에러 처리 필요

  • 항상 서버와 동기화 필요
  • 캐싱 중요(중복 요청 방지 필요)
  • isLoading, isError 처리 필요 

Q. 만약 서버 상태를 Zustand로 관리한다면?

isLoading/isError, 캐싱, 재시도, 만료 처리를 직접 관리해야 함 

매번 컴포넌트가 마운트 될 때마다 API 다시 호출 → React Query로 서버 상태 분리 必

→ 우리는 React Query를 통해 아래와 같은 서버 상태 관리를 할 수 있다. 

  •  비동기 데이터 요청 및 응답 처리
  • 캐싱 및 자동 re-fetching
  • 로딩/에러 상태 관리
  • 데이터 동기화 처리

위에서 다뤘던 zustand, redux는 클라이언트 상태 관리에, react query는 서버 상태 관리에 적합하다. 

자세한 내용은 아래 링크 참조, 정리가 잘되어 있다. 

https://velog.io/@okko8522/%EC%84%9C%EB%B2%84-%EC%83%81%ED%83%9C-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%83%81%ED%83%9C

 

클라이언트 - 서버 상태 분리: 둘이 친해지길 바라!

서버 상태와 클라이언트 상태의 관리 전략과 차이를 살펴보고 React Query와 Zustand의 조합으로 로그인부터 장바구니까지 실전 예제로 배워보자!

velog.io