상태 관리 전략 비교 - Context API, Zustand, Redux Toolkit 중 무엇을 선택해야 할까?

2026. 3. 23. 11:49·React


상태 관리 전략 비교 - Context API, Zustand, Redux Toolkit을 어떻게 선택해야 할까?

React를 공부하다 보면 어느 순간부터 단순히 컴포넌트를 만드는 것보다 더 중요한 고민을 하게 됩니다. 바로 상태를 어떻게 관리할 것인가에 대한 고민입니다.

 

처음에는 useState만으로도 충분합니다. 입력값을 관리하고, 버튼 클릭 여부를 저장하고, 모달 열림 여부를 제어하는 정도라면 지역 상태만으로도 큰 문제가 없습니다. 하지만 프로젝트 규모가 점점 커지고, 여러 컴포넌트가 같은 데이터를 함께 사용해야 하는 순간이 오면 이야기가 달라집니다.

 

이때부터는 단순히 상태를 저장하는 것을 넘어, 어떤 상태를 어디에 두어야 하는지, 어떤 도구를 선택해야 유지보수가 쉬운지, 그리고 전역 상태와 서버 상태를 어떻게 나누어야 하는지를 고민하게 됩니다.

 

이번 글에서는 이러한 흐름 속에서 자주 비교되는 세 가지 방법인 Context API, Zustand, Redux Toolkit을 중심으로 상태 관리 전략을 정리해보겠습니다. 또한 전역 상태와 지역 상태의 경계를 어떻게 나누어야 하는지, 서버 상태와 클라이언트 상태는 왜 분리해야 하는지도 함께 살펴보겠습니다.


상태 관리가 중요한 이유

상태 관리는 단순히 데이터를 저장하는 문제가 아닙니다. 애플리케이션이 어떤 구조로 흘러가는지, 컴포넌트 간의 책임이 어떻게 나뉘는지, 그리고 코드가 얼마나 예측 가능하게 동작하는지를 결정하는 핵심 요소입니다.

 

예를 들어 로그인한 사용자 정보를 생각해보겠습니다. 이 정보는 헤더에서도 필요하고, 마이페이지에서도 필요하고, 권한이 필요한 페이지에서도 필요할 수 있습니다. 이런 값을 각 컴포넌트가 개별적으로 가지고 있으면 중복이 발생하고, 서로 다른 상태를 참조하는 문제도 생길 수 있습니다.

 

반대로 모든 상태를 전역으로 올려버리면 어떨까요? 이 경우에는 관리해야 할 범위가 지나치게 넓어지고, 특정 컴포넌트에서만 필요한 값까지 전역 구조에 포함되면서 오히려 코드가 복잡해질 수 있습니다.

 

즉, 상태 관리의 핵심은 단순히 전역 라이브러리를 쓰는 것이 아니라, 상태의 성격을 파악하고 적절한 위치에 배치하는 것입니다.


Context API, Zustand, Redux Toolkit은 무엇이 다를까?

React에서 전역 상태를 다루는 방식은 다양하지만, 실무나 프로젝트에서 가장 자주 비교되는 것은 Context API, Zustand, Redux Toolkit입니다. 세 가지 모두 여러 컴포넌트가 공통된 상태를 공유할 수 있게 해준다는 공통점이 있지만, 사용하는 목적과 구조, 확장성에는 분명한 차이가 있습니다.


Context API — React가 기본 제공하는 상태 공유 방식

Context API는 React가 제공하는 공식 기능입니다. 별도의 외부 라이브러리를 설치하지 않아도 사용할 수 있고, 비교적 단순한 전역 데이터 공유에 적합합니다.

보통 다음과 같은 형태로 사용합니다.

import { createContext, useContext, useState } from "react";

type AuthContextType = {
  user: string | null;
  login: (name: string) => void;
  logout: () => void;
};

const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<string | null>(null);

  const login = (name: string) => setUser(name);
  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("AuthProvider 안에서 사용해야 합니다.");
  }
  return context;
}

이 방식의 장점은 명확합니다. React 기본 기능만으로 구현할 수 있고, 구조도 비교적 직관적입니다. 특히 테마, 언어, 인증 정보처럼 앱 전역에서 참조해야 하지만 자주 바뀌지 않는 상태에는 충분히 유용합니다.

 

하지만 Context API는 상태 관리 라이브러리라기보다는 값 전달 도구에 가깝습니다. 즉, “전역 상태를 공유하는 통로”는 제공하지만, 상태 변화 최적화나 구조화된 관리 체계까지는 스스로 해결해야 합니다.

 

가장 대표적인 한계는 리렌더링 문제입니다. Provider의 value가 바뀌면 해당 Context를 구독하는 하위 컴포넌트들이 다시 렌더링될 수 있습니다. 상태가 단순할 때는 괜찮지만, 값이 자주 바뀌거나 Context 하나에 너무 많은 상태를 담기 시작하면 성능과 구조 모두 부담이 커질 수 있습니다.

 

따라서 Context API는 “간단한 전역 공유”에는 좋지만, 전역 상태가 복잡해질수록 관리 부담이 커지는 방식이라고 이해하는 것이 좋습니다.


Zustand — 가볍고 직관적인 전역 상태 관리

Zustand는 최근 React 프로젝트에서 매우 많이 사용되는 상태 관리 라이브러리입니다. 가장 큰 특징은 간결함입니다. Redux처럼 action, reducer, dispatch 구조를 강제하지 않으면서도, 전역 상태를 훨씬 편하게 관리할 수 있습니다.

기본 구조는 다음과 같습니다.

import { create } from "zustand";

type AuthState = {
  user: string | null;
  login: (name: string) => void;
  logout: () => void;
};

export const useAuthStore = create<AuthState>((set) => ({
  user: null,
  login: (name) => set({ user: name }),
  logout: () => set({ user: null }),
}));

사용할 때는 필요한 값만 선택해서 가져올 수 있습니다.

const user = useAuthStore((state) => state.user);
const login = useAuthStore((state) => state.login);

이 구조가 중요한 이유는, Context API와 달리 필요한 값만 구독할 수 있기 때문입니다. 즉, 스토어 전체가 아니라 특정 상태 조각만 선택해서 사용할 수 있고, 그 값이 바뀔 때만 컴포넌트가 다시 렌더링됩니다.

이 때문에 Zustand는 다음과 같은 상황에 매우 잘 어울립니다.

 

하나의 페이지나 앱 전체에서 여러 컴포넌트가 함께 사용하는 UI 상태가 많고, 상태 변경도 자주 일어나며, 그렇다고 Redux처럼 무거운 구조까지는 필요하지 않은 경우입니다.

예를 들어 다음과 같은 값들은 Zustand와 잘 맞습니다.

  • 현재 선택된 사용자
  • 사이드바 열림 여부
  • 다단계 모달의 현재 단계
  • 필터 조건
  • 탭 상태
  • 임시로 유지해야 하는 폼 초안

Zustand의 장점은 단순히 코드가 짧다는 데 있지 않습니다. 상태 관리 로직을 컴포넌트 외부로 분리하기 쉽고, 필요한 상태만 선택적으로 구독할 수 있어서 구조와 성능 사이의 균형이 좋다는 점이 핵심입니다.

 

다만 프로젝트 규모가 아주 크고, 상태 변화 흐름을 팀 단위로 엄격하게 추적해야 하는 환경이라면 다소 느슨하게 느껴질 수도 있습니다. 즉, Zustand는 자유도가 높은 대신, 팀 차원의 규칙과 설계 기준이 함께 있어야 더 안정적으로 사용할 수 있습니다.


Redux Toolkit — 가장 구조적이고 예측 가능한 방식

Redux Toolkit은 Redux를 더 쉽게 사용할 수 있도록 만든 공식 도구입니다. 예전 Redux가 action type, action creator, reducer, store 설정 등 보일러플레이트가 너무 많다는 단점이 있었는데, Redux Toolkit은 이 문제를 크게 완화해주었습니다.

기본 예시는 다음과 같습니다.

import { createSlice, configureStore, PayloadAction } from "@reduxjs/toolkit";

type AuthState = {
  user: string | null;
};

const initialState: AuthState = {
  user: null,
};

const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    login(state, action: PayloadAction<string>) {
      state.user = action.payload;
    },
    logout(state) {
      state.user = null;
    },
  },
});

export const { login, logout } = authSlice.actions;

export const store = configureStore({
  reducer: {
    auth: authSlice.reducer,
  },
});

 

Redux Toolkit의 가장 큰 강점은 예측 가능성입니다. 상태 변경은 반드시 action을 통해 일어나고, reducer를 통해 어떻게 바뀌는지가 명확하게 드러납니다. 즉, “누가”, “어떤 의도로”, “무엇을 바꿨는지”를 추적하기가 쉽습니다.

 

이 점은 협업에서 매우 큰 장점이 됩니다. 규모가 큰 프로젝트에서는 상태 변경이 여러 화면과 기능에 영향을 줄 수 있는데, Redux Toolkit은 그 흐름을 비교적 일관된 구조 안에서 관리할 수 있게 해줍니다.

 

또한 Redux DevTools와의 궁합도 뛰어납니다. 액션 발생 순서와 상태 변화를 시간 순서대로 추적할 수 있어 디버깅에 강합니다.

 

하지만 단점도 있습니다. 아무래도 Zustand보다 진입 장벽이 있고, 구조가 더 엄격합니다. 간단한 프로젝트에서는 오히려 너무 무겁게 느껴질 수 있습니다. 즉, Redux Toolkit은 “필요해서 쓰는 도구”이지, 모든 프로젝트의 기본 선택지가 되지는 않습니다.

 

정리하면 Redux Toolkit은 다음과 같은 상황에서 강점을 발휘합니다.

상태 흐름이 복잡하고, 기능 간 결합이 많고, 여러 명이 함께 개발하며, 상태 변경 이력을 명확히 추적해야 하는 프로젝트입니다.


세 가지를 어떻게 비교해서 이해하면 좋을까?

이 세 가지를 단순히 “무엇이 더 좋다”로 비교하면 오히려 혼란스러울 수 있습니다. 더 중요한 것은 각각이 어떤 상황에 잘 맞는지를 이해하는 것입니다.

 

Context API는 React 기본 기능으로 간단한 전역 공유를 할 때 적합합니다. 러닝커브가 낮고 설정이 쉬우나, 상태가 많아질수록 성능과 구조의 부담이 커집니다.

 

Zustand는 빠르게 전역 상태를 만들고, 필요한 상태만 효율적으로 구독하고 싶을 때 적합합니다. 중소 규모 프로젝트나 프론트엔드 중심의 상태 관리에 매우 잘 맞습니다.

 

Redux Toolkit은 상태 흐름을 엄격하게 통제하고, 디버깅과 협업 효율을 높이고 싶을 때 적합합니다. 복잡한 비즈니스 로직과 큰 규모의 프로젝트에서 강력한 장점을 가집니다.

 

즉, 도구의 우열보다도 프로젝트의 복잡도와 팀의 운영 방식이 더 중요한 선택 기준이 됩니다.


전역 상태와 지역 상태는 어떻게 나눠야 할까?

상태 관리에서 가장 자주 하는 실수 중 하나는, 상태 관리 라이브러리를 도입한 뒤 너무 많은 상태를 전역으로 올리는 것입니다.

전역 상태는 여러 컴포넌트가 함께 사용하는 값에만 사용해야 합니다. 단지 “어디서든 접근 가능하면 편하겠다”는 이유만으로 전역화하면, 오히려 상태의 책임 범위가 흐려지고 디버깅도 어려워집니다.

 

다음과 같은 질문을 기준으로 생각해보면 좋습니다.

  • 이 상태를 정말 여러 컴포넌트가 동시에 참조하는가?
  • 이 상태가 페이지를 넘어서도 유지되어야 하는가?
  • 이 상태가 애플리케이션의 공통된 흐름을 결정하는가?

예를 들어 로그인 사용자 정보, 권한 정보, 전역 테마, 현재 언어 설정, 앱 공통 필터 조건 같은 값은 전역 상태가 될 가능성이 높습니다.

 

반면 다음과 같은 값은 보통 지역 상태로 두는 것이 더 자연스럽습니다.

  • 특정 모달의 열림 여부
  • 특정 입력창의 값
  • 드롭다운 펼침 상태
  • 현재 hover된 카드
  • 한 페이지 안에서만 쓰이는 정렬 기준

이런 값들은 해당 컴포넌트 또는 가까운 상위 컴포넌트에서만 관리하는 편이 훨씬 단순합니다.

상태를 전역으로 뺀다는 것은 “접근이 쉬워진다”는 의미이기도 하지만, 동시에 “영향 범위가 넓어진다”는 뜻이기도 합니다. 그래서 전역 상태는 편의성보다 책임 범위를 먼저 생각하고 결정해야 합니다.


서버 상태와 클라이언트 상태는 왜 분리해야 할까?

이 부분은 실무에서 특히 중요합니다. 많은 개발자들이 초반에는 API로 받아온 데이터도 전역 상태 라이브러리에 넣고, UI 상태도 같은 곳에 넣는 식으로 관리하곤 합니다. 하지만 이 방식은 프로젝트가 커질수록 여러 문제를 만들어냅니다.

 

먼저 서버 상태는 말 그대로 서버에서 가져온 데이터입니다. 게시글 목록, 사용자 프로필, 댓글, 상품 정보처럼 API 요청을 통해 받아오고, 시간이 지나면 다시 갱신될 수 있는 데이터입니다.

 

이 데이터는 단순히 저장만 하면 끝나는 것이 아닙니다. 다음과 같은 문제들을 함께 다뤄야 합니다.

  • 로딩 상태
  • 에러 상태
  • 캐싱
  • 재요청
  • 백그라운드 동기화
  • stale 여부 판단
  • mutation 이후 invalidation

즉, 서버 상태는 일반적인 전역 상태와는 성격이 다릅니다. 그래서 이런 데이터는 Zustand나 Redux에 직접 담기보다, TanStack Query 같은 서버 상태 관리 도구로 분리하는 것이 훨씬 적절합니다.

 

반대로 클라이언트 상태는 사용자 인터랙션과 UI 제어를 위한 상태입니다. 모달, 탭, 드롭다운, 선택값, 임시 필터, 폼 입력값 등이 여기에 해당합니다. 이런 값들은 서버와 직접 동기화되는 것이 아니라 화면 동작을 제어하기 위한 값입니다.

이 둘을 분리하지 않으면 어떤 일이 생길까요?

 

예를 들어 상품 목록을 Zustand에 저장해두고 직접 fetch, loading, error, refetch 로직까지 관리한다고 가정해보겠습니다. 처음에는 가능해 보이지만, 결국 캐시 무효화나 재요청 타이밍, 데이터 최신성 보장까지 직접 구현해야 합니다. 이는 곧 유지보수 비용 증가로 이어집니다.

 

그래서 실무에서는 보통 다음과 같이 나누는 경우가 많습니다.

 

서버 상태는 TanStack Query
클라이언트 전역 상태는 Zustand 또는 Redux Toolkit
컴포넌트 내부 상태는 useState

 

이 구조가 가장 흔하고도 안정적인 이유는, 상태의 성격에 따라 책임을 분리했기 때문입니다.


실무 관점에서 추천하는 상태 관리 기준

실무에서는 “무조건 Redux”, “무조건 Zustand”처럼 하나로 통일하는 것보다, 상태의 성격에 따라 적절한 도구를 조합하는 경우가 많습니다.

 

예를 들어 다음과 같은 기준으로 접근할 수 있습니다.

 

작고 단순한 앱이라면 Context API와 useState만으로도 충분할 수 있습니다.
중간 규모의 앱에서 UI 전역 상태가 많다면 Zustand가 매우 효율적입니다.
대규모 서비스에서 상태 흐름 추적과 협업 규칙이 중요하다면 Redux Toolkit이 유리합니다.

 

그리고 API 데이터는 별도의 서버 상태 관리 도구로 분리하는 것이 좋습니다.

결국 중요한 것은 도구의 이름이 아니라, 상태를 어떤 기준으로 분리하고 배치했는가입니다.

 

좋은 상태 관리는 “어떤 라이브러리를 썼는가”보다 “왜 이 상태를 여기에 두었는가”에 대한 설명이 가능한 구조입니다.


마무리하며

상태 관리 전략은 React 개발에서 단순한 기술 선택이 아닙니다. 그것은 곧 애플리케이션의 구조를 설계하는 일과 같습니다.

 

Context API, Zustand, Redux Toolkit은 각각 장단점이 분명한 도구입니다. 중요한 것은 어떤 도구가 절대적으로 더 좋다고 판단하는 것이 아니라, 지금 프로젝트의 상태 복잡도와 팀의 협업 방식, 그리고 상태의 성격에 맞는 선택을 하는 것입니다.

 

또한 전역 상태와 지역 상태를 구분하는 감각, 서버 상태와 클라이언트 상태를 분리하는 기준은 단순한 라이브러리 사용법보다 훨씬 더 중요한 역량입니다.

 

React에서 상태 관리를 잘한다는 것은 단지 store를 잘 만드는 것이 아닙니다. 상태가 어디에 있어야 가장 자연스럽고, 가장 유지보수하기 쉬운지를 설계할 수 있다는 뜻입니다.

 

이 기준이 잡히면 어떤 상태 관리 도구를 사용하더라도 훨씬 안정적인 애플리케이션을 만들 수 있습니다.

 

감사합니다!

'React' 카테고리의 다른 글

Profiler부터 memo까지 제대로 사용해보자!  (0) 2026.04.27
TanStack Query 에 대해서 알아보기!  (0) 2026.04.04
React useState 완전 이해하기  (0) 2026.03.21
React Hook 완전 이해하기  (0) 2026.03.21
커스텀 훅과 책임 분리  (0) 2026.03.16
'React' 카테고리의 다른 글
  • Profiler부터 memo까지 제대로 사용해보자!
  • TanStack Query 에 대해서 알아보기!
  • React useState 완전 이해하기
  • React Hook 완전 이해하기
수달군
수달군
  • 수달군
    수달 코딩 공장
    수달군
  • 전체
    오늘
    어제
    • 분류 전체보기 (21)
      • React (10)
      • Next.js (7)
      • TypeScript 딥 다이브! (1)
      • 웹 기초 이론 (0)
      • 코딩 테스트 준비 (1)
        • Python 기본 (1)
      • AI 도구들 (0)
      • 프로젝트 회고 (0)
      • 자료구조 (0)
      • 일상 (0)
      • 해외 여행 (0)
      • 국내 여행 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    입력받기
    입력값
    파이썬 초보
    it
    python
    coding
    코딩
    파이썬
    입력
    input
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
수달군
상태 관리 전략 비교 - Context API, Zustand, Redux Toolkit 중 무엇을 선택해야 할까?
상단으로

티스토리툴바