Context API vs Redux

2025. 4. 1. 00:42Frameworks/React

Context API vs Redux

Context API를 선택하는 경우

  • 간단한 애플리케이션: 규모가 작고 상태 관리가 복잡하지 않은 경우
  • 추가 종속성 최소화: 외부 라이브러리 없이 React만으로 구현하고 싶은 경우
  • 빠른 프로토타이핑: 신속하게 상태 관리 구현이 필요한 경우

Redux를 선택하는 경우

  • 복잡한 상태 로직: 여러 컴포넌트에서 공유되는 복잡한 상태가 많은 경우
  • 예측 가능한 상태 변화: 명확한 데이터 흐름과 디버깅이 중요한 경우
  • 미들웨어 활용: 비동기 작업, 로깅 등 부가 기능이 필요한 경우
  • 개발자 도구: Redux DevTools를 통한 상태 변화 추적이 필요한 경우
  • 대규모 팀: 명확한 상태 관리 패턴이 필요한 큰 규모의 협업 프로젝트

기타 상태 관리 라이브러리

Redux와 Context API 외에도 다양한 상태 관리 라이브러리가 있습니다:

  1. Zustand: 간단하고 사용하기 쉬운 상태 관리 라이브러리
  2. Recoil: Facebook에서 개발한 React를 위한 상태 관리 라이브러리
  3. MobX: 관찰 가능한 상태를 기반으로 하는 상태 관리 라이브러리
  4. Jotai: 최소한의 API로 상태를 원자 단위로 관리하는 라이브러리
  5. XState: 상태 머신 기반의 상태 관리 라이브러리

실제 애플리케이션 예제: 쇼핑 카트

다음은 Redux Toolkit을 사용한 간단한 쇼핑 카트 기능의 구현 예제입니다:

1. 상품 슬라이스

// features/products/productsSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const fetchProducts = createAsyncThunk(
  'products/fetchProducts',
  async () => {
    const response = await fetch('/api/products');
    return response.json();
  }
);

const productsSlice = createSlice({
  name: 'products',
  initialState: {
    items: [],
    loading: false,
    error: null
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchProducts.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchProducts.fulfilled, (state, action) => {
        state.loading = false;
        state.items = action.payload;
      })
      .addCase(fetchProducts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  }
});

export default productsSlice.reducer;

2. 장바구니 슬라이스

// features/cart/cartSlice.js
import { createSlice } from '@reduxjs/toolkit';

const cartSlice = createSlice({
  name: 'cart',
  initialState: {
    items: [], // { id, name, price, quantity }
    totalAmount: 0,
    totalQuantity: 0
  },
  reducers: {
    addToCart: (state, action) => {
      const newItem = action.payload;
      const existingItem = state.items.find(item => item.id === newItem.id);
      
      if (existingItem) {
        existingItem.quantity++;
      } else {
        state.items.push({ ...newItem, quantity: 1 });
      }
      
      state.totalQuantity++;
      state.totalAmount += newItem.price;
    },
    removeFromCart: (state, action) => {
      const id = action.payload;
      const existingItem = state.items.find(item => item.id === id);
      
      if (existingItem.quantity === 1) {
        state.items = state.items.filter(item => item.id !== id);
      } else {
        existingItem.quantity--;
      }
      
      state.totalQuantity--;
      state.totalAmount -= existingItem.price;
    },
    clearCart: (state) => {
      state.items = [];
      state.totalQuantity = 0;
      state.totalAmount = 0;
    }
  }
});

export const { addToCart, removeFromCart, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

3. 스토어 설정

// app/store.js
import { configureStore } from '@reduxjs/toolkit';
import productsReducer from '../features/products/productsSlice';
import cartReducer from '../features/cart/cartSlice';

export const store = configureStore({
  reducer: {
    products: productsReducer,
    cart: cartReducer
  }
});

4. 제품 목록 컴포넌트

// components/ProductList.js
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchProducts } from '../features/products/productsSlice';
import { addToCart } from '../features/cart/cartSlice';

function ProductList() {
  const dispatch = useDispatch();
  const { items: products, loading, error } = useSelector(state => state.products);
  
  useEffect(() => {
    dispatch(fetchProducts());
  }, [dispatch]);
  
  if (loading) return <p>로딩 중...</p>;
  if (error) return <p>오류: {error}</p>;
  
  return (
    <div className="product-list">
      <h2>제품 목록</h2>
      <div className="products">
        {products.map(product => (
          <div key={product.id} className="product-card">
            <h3>{product.name}</h3>
            <p>{product.price}원</p>
            <button onClick={() => dispatch(addToCart(product))}>
              장바구니에 추가
            </button>
          </div>
        ))}
      </div>
    </div>
  );
}

export default ProductList;

5. 장바구니 컴포넌트

// components/Cart.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addToCart, removeFromCart, clearCart } from '../features/cart/cartSlice';

function Cart() {
  const dispatch = useDispatch();
  const { items, totalAmount, totalQuantity } = useSelector(state => state.cart);
  
  if (items.length === 0) {
    return (
      <div className="cart">
        <h2>장바구니</h2>
        <p>장바구니가 비어 있습니다.</p>
      </div>
    );
  }
  
  return (
    <div className="cart">
      <h2>장바구니 ({totalQuantity}개 상품)</h2>
      
      <ul>
        {items.map(item => (
          <li key={item.id} className="cart-item">
            <div>
              <h3>{item.name}</h3>
              <p>{item.price}원 x {item.quantity} = {item.price * item.quantity}원</p>
            </div>
            <div className="item-actions">
              <button onClick={() => dispatch(removeFromCart(item.id))}>-</button>
              <span>{item.quantity}</span>
              <button onClick={() => dispatch(addToCart(item))}>+</button>
            </div>
          </li>
        ))}
      </ul>
      
      <div className="cart-summary">
        <p>총액: {totalAmount}원</p>
        <button onClick={() => dispatch(clearCart())}>장바구니 비우기</button>
        <button className="checkout-btn">결제하기</button>
      </div>
    </div>
  );
}

export default Cart;

결론

상태 관리는 React 애플리케이션 개발에서 중요한 측면이며, 애플리케이션의 크기와 복잡성에 따라 적절한 상태 관리 솔루션을 선택하는 것이 중요합니다.

  • 간단한 애플리케이션: 로컬 컴포넌트 상태 (useState)
  • 중간 규모 애플리케이션: Context API 또는 Zustand
  • 대규모 복잡한 애플리케이션: Redux (가급적 Redux Toolkit)

최근에는 Redux Toolkit이 Redux의 복잡성을 크게 줄여주어 많은 개발자들이 선호하게 되었습니다. 어떤 상태 관리 솔루션을 선택하든, 중요한 것은 일관된 패턴을 유지하고 상태의 예측 가능성을 확보하는 것입니다.

다음 학습 단계로는 API 연동, 인증 구현, 폼 관리, 테스팅 등의 주제를 탐구해볼 수 있습니다.

'Frameworks > React' 카테고리의 다른 글

React 컴포넌트에서 API 연동하기  (0) 2025.04.01
React와 API 연동하기  (0) 2025.04.01
Redux Toolkit  (0) 2025.04.01
Redux  (0) 2025.04.01
React 상태 관리  (0) 2025.04.01