[React] Context API가 있는데도 전역 상태 관리 라이브러리가 나오게 된 이유는?

gyo_zaa·2025년 1월 15일
0

React

목록 보기
3/4
post-thumbnail

📍 문제 제기

전역 상태 관리는 리액트 개발에서 필수적인 요소입니다. React는 Context API를 통해 기본적인 전역 상태 관리 기능을 제공하지만, 이것만으로 충분하지 않은 경우가 많습니다.
예를 들어, 복잡한 애플리케이션에서 Context API를 사용하면 코드가 복잡해지고 성능 저하가 발생할 수 있습니다.
따라서, 바로 이러한 문제를 해결하기 위해 등장한 것이 Zustand와 같은 전역 상태 관리 라이브러리입니다.


📍 Context API 코드

CounterContext.js

import React, { createContext, useContext, useState } from 'react';

const CounterContext = createContext();

export const CounterProvider = ({ children }) => {
  const [count, setCount] = useState(0);

  const increase = () => {
    console.log("Context API: 증가 함수 호출됨");
    setCount((prev) => prev + 1);
  };

  return (
    <CounterContext.Provider value={{ count, increase }}>
      {children}
    </CounterContext.Provider>
  );
};

export const useCounter = () => {
  console.log("Context API: useCounter 호출됨");
  return useContext(CounterContext);
};

App.js

import React from 'react';
import { CounterProvider, useCounter } from './CounterContext';

function Counter() {
  const { count, increase } = useCounter();
  console.log("Context API: Counter 컴포넌트 렌더링됨");

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increase}>증가</button>
    </div>
  );
}

export default function App() {
  return (
    <CounterProvider>
      <Counter />
    </CounterProvider>
  );
}

1. Context API의 동작 원리

  • Context API는 전역 상태를 관리할 수 있는 React의 기본 도구입니다.
  • Provider를 통해 상태를 하위 컴포넌트에 전달하며, 상태가 변경되면 Provider 하위에 있는 모든 컴포넌트가 리렌더링됩니다.
  • useContext를 사용하는 모든 컴포넌트는 Provider에서 제공하는 값이 변경될 때마다 다시 호출됩니다.

2. 출력 결과 분석

context api

첫 번째 증가 함수 호출(count: 0 ->1)

Context API: useCounter 호출됨
Context API: Counter 컴포넌트 렌더링됨
Context API: 증가 함수 호출됨
Context API: useCounter 호출됨
Context API: Counter 컴포넌트 렌더링됨
  • increase 함수 호출 -> setCount로 상태 업데이트 -> Provider의 값이 변경되면서 하위 컴포넌트(Counter)가 리렌더링됨.
  • useCounter가 다시 호출되며, useContext를 통해 새로운 상태를 받아옴.

두 번째, 세 번째 증가 함수 호출(count: 1->2->3)

  • 같은 패턴이 반복되어 useCounter와 Counter 컴포넌트가 다시 호출 및 렌더링됨.

3. Context API의 문제점

Context API는 전역 상태를 관리하기 위한 기본적인 도구지만, Provider의 값이 변경되면 하위의 모든 컴포넌트가 리렌더링된다는 단점이 있습니다. 이로 인해 위와 같이 상태가 1씩 증가할 때마다 useCounter와 Counter 컴포넌트가 반복적으로 호출되고 렌더링됩니다.
리렌더링 최적화가 어렵고 불필요한 연산이 많아지기 때문에, 이런 문제를 해결하기 위해 Zustand와 같은 전역 상태 관리 라이브러리가 등장하게 되었습니다.


📍 Zustand 코드

store.js

import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increase: () => {
    console.log("Zustand: 증가 함수 호출됨");
    set((state) => ({ count: state.count + 1 }));
  },
}));

export default useStore;

App.js

import React from 'react';
import useStore from './store';

function Counter() {
  const { count, increase } = useStore();
  console.log("Zustand: Counter 컴포넌트 렌더링됨");

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increase}>증가</button>
    </div>
  );
}

export default function App() {
  return <Counter />;
}

1. Zustand의 동작 원리

  • Zustand는 전역 상태 관리를 위한 라이브러리로, React의 Context API와 비교해 더 간단하고 효율적으로 상태를 관리할 수 있습니다.
  • 구조
- create 함수로 상태와 업데이트 로직을 정의
- 컴포넌트는 useStore 훅을 사용하여 필요한 상태를 구독
- 상태가 변경되면 구독된 컴포넌트만 리렌더링
  • 리렌더링 최적화 : Context API는 Provider 하위 모든 컴포넌트를 리렌더링하지만, Zustand는 상태를 사용하는 컴포넌트만 리렌더링됩니다.

2. 출력 결과 분석

zustand

첫 번째 증가 함수 호출(count: 0 ->1)

Zustand: Counter 컴포넌트 렌더링됨
Zustand: 증가 함수 호출됨
Zustand: Counter 컴포넌트 렌더링됨
  • 컴포넌트가 마운트될 때 Counter 컴포넌트가 처음 렌더링됨.
  • useStore를 통해 상태를 구독하며, 콘솔에 "Zustand: Counter 컴포넌트 렌더링됨"이 출력
  • increase 함수 호출 시 "Zustand: 증가 함수 호출됨"이 출력
  • 상태가 업데이트되고, 상태를 사용하는 Counter 컴포넌트가 다시 렌더링

두 번째, 세 번째 증가 함수 호출(count: 1->2->3)

Zustand: 증가 함수 호출됨
Zustand: Counter 컴포넌트 렌더링됨
Zustand: 증가 함수 호출됨
Zustand: Counter 컴포넌트 렌더링됨
  • increase 함수가 호출될 때마다 상태가 업데이트되고, 구독 중인 Counter 컴포넌트만 리렌더링됨.

📍 결론

비교 항목Context APIZustand
코드 복잡도Provider와 useContext 필요단순한 useStore 사용
리렌더링 성능모든 하위 컴포넌트 리렌더링구독된 컴포넌트만 리렌더링
사용성React 내부에서만 사용 가능React 외부에서도 사용 가능

Context API는 기본적인 전역 상태 관리에 적합하지만, 리렌더링 최적화와 코드 간결성 측면에서 한계가 있습니다.
반면, Zustand는 리렌더링을 최소화하고, 간결한 코드 구조와 유연성을 제공하여 대규모 애플리케이션에서도 효과적으로 상태를 관리할 수 있는 강력한 도구입니다.

profile
닌자가 되자!

0개의 댓글