프론트엔드

React 상태 관리 비교: Context, Redux, React Query

지식소 채움이 2025. 8. 22. 23:00

1. 상태 관리란 무엇인가?

React에서 상태(state)는 컴포넌트의 렌더링 결과를 결정하는 데이터입니다.

  • 지역 상태(Local State): useState, useReducer 등을 통해 개별 컴포넌트 안에서 관리
  • 전역 상태(Global State): 여러 컴포넌트가 공유해야 하는 상태 (예: 사용자 로그인 정보, 테마, 언어 설정 등)
  • 서버 상태(Server State): 서버에서 가져온 데이터로, 동기화가 필요 (예: 게시글 목록, API 응답 데이터)

React의 상태 관리 도구들은 이 세 가지 범주를 효율적으로 다루는 데 초점을 맞추고 있습니다.


2. Context API – 전역 상태 관리의 기초

Context API는 React가 제공하는 기본 내장 기능으로, props drilling 문제를 해결하기 위해 등장했습니다.
즉, 여러 단계에 걸쳐 props를 전달하지 않고 전역적으로 데이터를 공급할 수 있습니다.

// ThemeContext.js
import { createContext } from "react";

export const ThemeContext = createContext("light");

// App.js
<ThemeContext.Provider value="dark">
  <Toolbar />
</ThemeContext.Provider>

// Toolbar.js
const theme = useContext(ThemeContext);

✅ 장점

  • React 내장 기능 → 별도 라이브러리 불필요
  • 소규모 전역 상태 관리에 적합 (테마, 언어, 인증 여부 등)

❌ 단점

  • 상태가 커질수록 리렌더링 비용 증가
  • 복잡한 로직 관리에는 불편

👉 따라서 규모가 작은 앱이나 간단한 전역 데이터 공유에는 Context API만으로 충분합니다.


3. Redux – 복잡한 전역 상태 관리의 표준

Redux는 가장 오래되고 널리 사용되는 상태 관리 라이브러리입니다. 상태를 단일 스토어(Single Store) 에 보관하고, 액션(Action)과 리듀서(Reducer)를 통해 상태를 업데이트합니다.

// store.js
import { configureStore, createSlice } from "@reduxjs/toolkit";

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

export const { increment, decrement } = counterSlice.actions;
export const store = configureStore({ reducer: { counter: counterSlice.reducer } });

✅ 장점

  • 예측 가능한 상태 업데이트 (순수 함수 기반)
  • 강력한 Redux DevTools를 통한 디버깅 가능
  • 대규모 애플리케이션에서 검증된 패턴

❌ 단점

  • 보일러플레이트 코드가 많음 (→ Redux Toolkit으로 개선)
  • 학습 곡선이 상대적으로 가파름

👉 Redux는 대규모 프로젝트에서 팀 단위 협업이 필요할 때, 혹은 상태 추적과 디버깅이 중요한 경우 유리합니다.


4. React Query – 서버 상태 관리의 혁신

React Query는 로컬 상태가 아닌 서버 상태 관리에 초점을 맞춘 라이브러리입니다.
API 요청과 캐싱, 동기화, 로딩/에러 상태 관리 등을 자동으로 처리해 주기 때문에 “프론트엔드의 데이터 페칭 표준”으로 불립니다.

import { useQuery } from "@tanstack/react-query";

function PostList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ["posts"],
    queryFn: () => fetch("/api/posts").then(res => res.json())
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error...</p>;

  return (
    <ul>
      {data.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

✅ 장점

  • 캐싱 자동화 (같은 요청은 재사용)
  • 리페치(refetch), 무효화(invalidate), 백그라운드 업데이트 지원
  • API 기반 서비스에 최적화

❌ 단점

  • 로컬 상태 관리에는 적합하지 않음
  • 별도 라이브러리 의존

👉 React Query는 서버 데이터가 많은 서비스(예: 블로그, 쇼핑몰, 대시보드) 에 특히 효과적입니다.


5. 어떤 상황에서 무엇을 선택할까?

  • 소규모 앱 / 간단한 전역 데이터 → Context API
  • 대규모 앱 / 복잡한 상태 로직 → Redux (Redux Toolkit 필수)
  • API 중심 서비스 / 서버 데이터 동기화 → React Query

실제로는 이들을 조합해서 사용하기도 합니다.
예를 들어,

  • 전역 UI 상태(테마, 다크모드) → Context API
  • 복잡한 비즈니스 로직(인증, 권한, 장바구니) → Redux
  • 서버 데이터(API 응답, 게시글, 유저 목록) → React Query

이렇게 역할을 분리하면 프로젝트가 커져도 관리가 훨씬 수월해집니다.


6. 마무리

React는 useState 하나로 시작하지만, 애플리케이션 규모가 커질수록 다양한 상태 관리 전략이 필요해집니다.

  • Context API는 작고 단순한 전역 상태
  • Redux는 예측 가능한 대규모 전역 상태 관리
  • React Query는 서버 데이터 관리의 혁신

세 가지를 상황에 맞게 적절히 조합하는 것이 현명한 접근입니다.

앞으로 React 18+, Next.js와의 결합, 그리고 서버 컴포넌트 시대까지 고려하면 상태 관리의 중요성은 더욱 커질 것입니다.
개발자는 단순히 “도구 선택”을 넘어서, 상태의 성격을 구분하고 최적의 관리 방법을 설계하는 능력이 필요합니다.