본질적으로 왜 상태관리 라이브러리를 써야 하는가?

핵심 문제 정의

React의 기본 상태 관리만으로는 복잡한 애플리케이션의 상태 공유와 동기화가 한계에 부딪힌다. useStateprop drilling, Context API의 본질적 한계를 이해하고, 언제 상태관리 라이브러리가 필요한지 명확히 구분해야 한다.

[차트:108]

1. React 기본 상태 관리의 한계

1.1 useState의 근본적 제약

  • 범위 제한: 컴포넌트 내부에서만 접근 가능1
  • 공유 불가: 형제 컴포넌트나 먼 거리 컴포넌트와 상태 공유 불가능
  • 생명주기 의존: 컴포넌트가 언마운트되면 상태 소실

1.2 Prop Drilling의 심각한 문제점

문제점설명영향도
코드 복잡성2중간 컴포넌트가 불필요한 props를 받아 전달높음
유지보수성 악화3props 변경 시 여러 컴포넌트 수정 필요매우 높음
가독성 저하4데이터 흐름 추적이 어려워짐높음
컴포넌트 결합도 증가5재사용성이 떨어지고 리팩토링 어려움높음
확장성 문제6앱이 커질수록 문제가 기하급수적으로 증가매우 높음
// 🚫 나쁜 예: Excessive Prop Drilling
function App() {
  const [user, setUser] = useState({ name: "김철수", role: "admin" });
  const [theme, setTheme] = useState("dark");
  const [cart, setCart] = useState([]);
  
  return (
    <Layout 
      user={user} 
      theme={theme} 
      cart={cart}
      setUser={setUser}
      setTheme={setTheme}
      setCart={setCart}
    />
  );
}
 
function Layout({ user, theme, cart, setUser, setTheme, setCart }) {
  return (
    <div>
      <Header user={user} theme={theme} setTheme={setTheme} />
      <MainContent cart={cart} setCart={setCart} />
      <Footer user={user} />
    </div>
  );
}
 
// 3-4단계 더 중첩... 😱

2. Context API의 치명적 성능 문제

2.1 전체 Consumer 리렌더링 문제

Context API의 가장 큰 문제는 값이 변경되면 모든 Consumer가 무조건 리렌더링된다는 점이다789.

// 🚫 성능 문제가 있는 Context 사용
const AppContext = createContext();
 
function AppProvider({ children }) {
  const [user, setUser] = useState({ name: "김철수", email: "kim@example.com" });
  const [theme, setTheme] = useState("light");
  const [cart, setCart] = useState([]);
  
  // user.email만 변경되어도 theme나 cart를 사용하는 모든 컴포넌트가 리렌더링! 😱
  const value = { user, theme, cart, setUser, setTheme, setCart };
  
  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}

2.2 Context API 성능 문제의 실제 영향

문제 상황원인결과
대량 리렌더링10하나의 값 변경이 모든 Consumer 영향앱 전체 성능 저하
메모이제이션 무효화11Context 변경으로 React.memo 효과 상실최적화 노력 무산
디버깅 어려움12어떤 변경이 리렌더링을 유발했는지 추적 곤란개발 생산성 저하
배터리 소모 증가13불필요한 연산으로 모바일 기기 배터리 소모사용자 경험 악화

2.3 Context API 적합 사용 사례 (제한적)

Context API는 변화 빈도가 낮고 전역적으로 필요한 상태에만 적합하다1415:

  • 사용자 인증 정보 (로그인 후 거의 변하지 않음)
  • 테마 설정 (사용자가 가끔 변경)
  • 언어/로케일 설정 (앱 실행 중 거의 변하지 않음)

3. 상태관리 라이브러리가 해결하는 핵심 문제들

3.1 선택적 구독 (Selective Subscription)

상태관리 라이브러리의 가장 중요한 특징은 필요한 부분만 구독할 수 있다는 점이다16.

// ✅ Zustand: 선택적 구독으로 성능 최적화
const useAppStore = create((set) => ({
  user: { name: "김철수", email: "kim@example.com" },
  theme: "light",
  cart: [],
  updateUser: (user) => set({ user }),
  updateTheme: (theme) => set({ theme }),
  addToCart: (item) => set((state) => ({ cart: [...state.cart, item] }))
}));
 
// 이름만 구독 - user.email 변경되어도 리렌더링 안 됨!
const UserName = () => {
  const userName = useAppStore(state => state.user.name);
  return <span>{userName}</span>;
};
 
// 테마만 구독 - user나 cart 변경되어도 리렌더링 안 됨!
const ThemeButton = () => {
  const theme = useAppStore(state => state.theme);
  const updateTheme = useAppStore(state => state.updateTheme);
  return <button onClick={() => updateTheme(theme === 'light' ? 'dark' : 'light')}>
    {theme} 모드
  </button>;
};

3.2 성능 최적화 메커니즘

라이브러리최적화 방식성능 특징
Redux15selector 기반 구독, 불변성 체크connect된 컴포넌트만 영향받음
Zustand1shallow comparison + selector구독한 부분만 리렌더링
Jotai14atomic state, 의존성 추적관련 atom만 업데이트

3.3 복잡한 상태 로직 관리

상태관리 라이브러리는 비즈니스 로직을 UI에서 분리하여 테스트와 유지보수를 용이하게 한다17.

// ✅ 복잡한 비즈니스 로직을 스토어에 캡슐화
const useEcommerceStore = create((set, get) => ({
  products: [],
  cart: [],
  user: null,
  
  // 복잡한 할인 계산 로직
  calculateDiscount: () => {
    const { cart, user } = get();
    const subtotal = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
    
    let discount = 0;
    if (user?.membership === 'VIP') discount += subtotal * 0.1;
    if (subtotal > 100000) discount += subtotal * 0.05;
    if (cart.length > 5) discount += 5000;
    
    return Math.min(discount, subtotal * 0.3); // 최대 30% 할인
  },
  
  // 재고 확인 및 카트 추가
  addToCart: (productId, quantity) => {
    const { products, cart } = get();
    const product = products.find(p => p.id === productId);
    
    if (!product || product.stock < quantity) {
      throw new Error('재고가 부족합니다');
    }
    
    set(state => ({
      cart: [...state.cart, { ...product, quantity }],
      products: state.products.map(p => 
        p.id === productId 
          ? { ...p, stock: p.stock - quantity }
          : p
      )
    }));
  }
}));

4. 실제 문제 상황별 해결책 비교

4.1 쇼핑몰 장바구니 시나리오

접근법구현 복잡도성능유지보수성확장성
Prop Drilling😱 매우 복잡😐 보통😱 매우 나쁨😱 불가능
Context API😐 보통😱 매우 나쁨😐 보통😱 제한적
Zustand😊 간단😊 우수😊 우수😊 우수

4.2 대시보드 실시간 데이터 관리

// Context API로는 불가능한 복잡한 상태 동기화
const useDashboardStore = create((set, get) => ({
  widgets: [],
  filters: { dateRange: '7d', category: 'all' },
  loading: false,
  error: null,
  
  // 여러 API 동시 호출 및 상태 동기화
  refreshAllData: async () => {
    set({ loading: true, error: null });
    
    try {
      const [metrics, charts, notifications] = await Promise.all([
        fetchMetrics(get().filters),
        fetchCharts(get().filters),
        fetchNotifications()
      ]);
      
      set({
        widgets: mergeWidgetData(metrics, charts),
        notifications,
        loading: false,
        lastUpdated: Date.now()
      });
    } catch (error) {
      set({ error: error.message, loading: false });
    }
  },
  
  // 필터 변경 시 관련 데이터만 업데이트
  updateFilters: (newFilters) => {
    set({ filters: { ...get().filters, ...newFilters } });
    get().refreshAllData(); // 자동으로 데이터 재조회
  }
}));

5. 언제 상태관리 라이브러리를 사용해야 하는가?

5.1 필수 사용 시나리오

상황판단 기준권장 라이브러리
3단계 이상 prop drilling중간 컴포넌트 2개 이상이 props만 전달Zustand
빈번한 상태 변경초당 여러 번 상태 업데이트 발생Zustand/Jotai
복잡한 비즈니스 로직상태 변경 시 여러 부작용 처리 필요Redux RTK
실시간 데이터WebSocket이나 Server-Sent Events 사용Zustand + middleware
큰 팀 개발5명 이상 개발자가 상태 로직 공유Redux RTK (패턴 강제)

5.2 사용하지 않아도 되는 경우

  • 단순한 폼 상태: 로컬 useState로 충분18
  • 모달 표시/숨김: 2-3 레벨 lifting state up으로 해결 가능
  • 정적 설정: 변경 빈도가 매우 낮은 경우 Context API 적합

6. 2025년 현재 권장사항

6.1 프로젝트 규모별 선택 기준

// 🎯 최적 선택 가이드
type ProjectSize = 'small' | 'medium' | 'large' | 'enterprise';
type StateComplexity = 'simple' | 'moderate' | 'complex';
type TeamSize = 'solo' | 'small' | 'medium' | 'large';
 
const getRecommendation = (
  size: ProjectSize, 
  complexity: StateComplexity, 
  team: TeamSize
): string => {
  if (size === 'small' && complexity === 'simple') return 'useState + Context';
  if (size === 'medium' || complexity === 'moderate') return 'Zustand';
  if (size === 'large' || team === 'large') return 'Redux RTK';
  if (complexity === 'complex') return 'Redux RTK + middleware';
  
  return 'Zustand'; // 기본 권장사항
};

6.2 마이그레이션 전략

  1. Context → Zustand: 성능 문제 해결 우선
  2. Redux → Zustand: 보일러플레이트 축소 목적
  3. useState → 라이브러리: prop drilling 해결 필요 시

결론: 상태관리 라이브러리의 본질적 가치

상태관리 라이브러리는 단순히 “편의성”을 위한 도구가 아니다. React의 구조적 한계를 보완하고 확장 가능한 아키텍처를 구현하기 위한 필수 도구다19.

핵심 가치

  1. 성능 최적화: 선택적 구독으로 불필요한 리렌더링 방지
  2. 코드 품질: 비즈니스 로직과 UI 분리로 테스트 용이성 확보
  3. 개발 생산성: prop drilling 제거로 유지보수성 대폭 향상
  4. 확장성: 복잡한 상태 로직을 체계적으로 관리

React의 기본 상태 관리만으로는 한계가 명확한 지점에서, 상태관리 라이브러리는 선택이 아닌 필수가 된다. 특히 Zustand는 2025년 현재 복잡성과 기능성의 최적 균형점을 제공하며, 대부분의 React 프로젝트에서 가장 실용적인 선택이다201416.

Footnotes

  1. https://www.freecodecamp.org/news/zustand-vs-usestate-how-to-manage-state-in-react/ 2

  2. https://dev.to/brandoniscoding-dev/state-management-in-react-why-prop-drilling-becomes-a-problem-and-how-zustand-fixes-it-3p21

  3. https://www.freecodecamp.org/news/avoid-prop-drilling-in-react/

  4. https://programmingly.dev/practical-tips-for-clean-code-and-reduced-prop-drilling-in-react/

  5. https://www.linkedin.com/pulse/pitfalls-excessive-prop-drilling-when-use-state-tools-ololchike-sxk6f

  6. https://www.geeksforgeeks.org/reactjs/what-is-prop-drilling-and-how-to-avoid-it/

  7. https://leewarrick.com/blog/the-problem-with-context/

  8. https://www.reddit.com/r/reactjs/comments/g9yuto/understanding_cons_of_react_context_for_state/

  9. https://thoughtspile.github.io/2021/10/04/react-context-dangers/

  10. https://blog.logrocket.com/pitfalls-of-overusing-react-context/

  11. https://www.linkedin.com/posts/edirodriguez_many-apps-using-react-context-have-huge-performance-activity-7297872598149808128-3Sbr

  12. https://www.greatfrontend.com/questions/quiz/what-are-some-pitfalls-about-using-context-in-react

  13. https://www.tenxdeveloper.com/blog/optimizing-react-context-performance

  14. https://dev.to/hijazi313/state-management-in-2025-when-to-use-context-redux-zustand-or-jotai-2d2k 2 3

  15. https://www.linkedin.com/pulse/choosing-right-state-management-react-comparing-redux-bogosyan-xu3pe 2

  16. https://betterstack.com/community/guides/scaling-nodejs/zustand-vs-redux-toolkit-vs-jotai/ 2

  17. https://blog.pixelfreestudio.com/how-to-handle-complex-state-in-large-scale-frontend-projects/

  18. https://dev.to/get_pieces/you-dont-need-a-state-management-library-for-react-use-usestate-context-dka

  19. https://www.frontendundefined.com/posts/state-management/need-state-management-library/

  20. https://www.linkedin.com/pulse/state-management-2025-redux-zustand-react-query-sbtqc