Frontend Study

React #14 _ 다시 한번 useContext

jimmmy_jin 2025. 1. 3. 12:26

React의 useContext로 상태를 공유하는 효율적인 방법: Cart 예제

React 개발을 하다 보면 컴포넌트 간에 데이터를 공유해야 하는 상황이 자주 발생한다. 특히, 쇼핑 카트와 같은 기능에서는 여러 컴포넌트에서 동일한 상태(예: 장바구니 아이템 목록, 총 금액)를 참조하거나 수정해야 할 때가 많다. 이런 상황에서 **useContext**는 간단하고 효율적인 해결책을 제공한다.


1. useContext란 무엇인가?

useContext는 React의 Context API를 활용하여 전역 상태를 쉽게 사용할 수 있도록 도와주는 Hook이다.

주요 특징

  • 부모 컴포넌트에서 데이터를 자식 컴포넌트로 전달할 때 Props Drilling을 방지한다.
  • 전역 상태를 관리하는 Context를 생성하고, 하위 컴포넌트에서 해당 상태를 쉽게 소비할 수 있다.
  • 상태나 로직을 필요한 컴포넌트에서만 접근 가능하다.

2. 기본적인 사용법

1) Context 생성

createContext를 사용하여 Context를 생성한다.

import React, { createContext } from "react";

const CartContext = createContext(); // Cart 상태를 위한 Context 생성

2) Context Provider

Provider 컴포넌트를 통해 상태와 값을 하위 컴포넌트에 전달한다.

export function CartProvider({ children }) {
  const cart = []; // 장바구니 초기 상태
  const addItemToCart = (item) => {
    cart.push(item); // 장바구니에 아이템 추가 (예제용)
  };

  return (
    <CartContext.Provider value={{ cart, addItemToCart }}>
      {children}
    </CartContext.Provider>
  );
}

3) Context Consumer

useContext를 사용하여 Context에 저장된 상태와 함수를 소비한다.

import { useContext } from "react";
import { CartContext } from "./CartProvider";

export function CartDisplay() {
  const { cart, addItemToCart } = useContext(CartContext);

  return (
    <div>
      <h2>Your Cart</h2>
      <ul>
        {cart.map((item, index) => (
          <li key={index}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

3. 쇼핑 카트 예제

Cart 상태 관리

아래 코드는 useContext를 사용하여 쇼핑 카트 상태를 관리하는 예제이다.

CartProvider.js

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

const CartContext = createContext();

export function CartProvider({ children }) {
  const [cart, setCart] = useState([]);

  const addItemToCart = (item) => {
    setCart((prevCart) => [...prevCart, item]);
  };

  const removeItemFromCart = (id) => {
    setCart((prevCart) => prevCart.filter((item) => item.id !== id));
  };

  return (
    <CartContext.Provider value={{ cart, addItemToCart, removeItemFromCart }}>
      {children}
    </CartContext.Provider>
  );
}

export function useCart() {
  return useContext(CartContext);
}

CartDisplay.js

import React from "react";
import { useCart } from "./CartProvider";

export function CartDisplay() {
  const { cart, removeItemFromCart } = useCart();

  return (
    <div>
      <h2>Your Cart</h2>
      <ul>
        {cart.map((item) => (
          <li key={item.id}>
            {item.name} - ${item.price}
            <button onClick={() => removeItemFromCart(item.id)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

ProductList.js

import React from "react";
import { useCart } from "./CartProvider";

export function ProductList() {
  const { addItemToCart } = useCart();

  const products = [
    { id: 1, name: "Apple", price: 2 },
    { id: 2, name: "Banana", price: 1 },
    { id: 3, name: "Cherry", price: 3 },
  ];

  return (
    <div>
      <h2>Product List</h2>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            {product.name} - ${product.price}
            <button onClick={() => addItemToCart(product)}>Add to Cart</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

App.js

import React from "react";
import { CartProvider } from "./CartProvider";
import { ProductList } from "./ProductList";
import { CartDisplay } from "./CartDisplay";

export default function App() {
  return (
    <CartProvider>
      <ProductList />
      <CartDisplay />
    </CartProvider>
  );
}

4. useContext와 useReducer 비교

useContext는 전역적으로 상태를 공유할 때 매우 편리하지만, 상태 관리가 복잡해지는 경우 **useReducer**와 조합하면 더 강력한 상태 관리가 가능하다.

  • 단순한 상태 공유: useContext로 충분하다.
  • 복잡한 로직 및 여러 액션: useContext + useReducer 조합 추천.

5. useContext의 장점

  1. Props Drilling 방지
    여러 단계의 자식 컴포넌트에 Props를 전달할 필요가 없다.
  2. 코드 간결화
    상태와 로직을 전역으로 관리하여 중복 코드를 줄일 수 있다.
  3. 유연성
    필요에 따라 언제든지 새로운 상태와 로직을 추가하거나 수정할 수 있다.

6. 마무리

React의 useContext는 상태를 여러 컴포넌트에서 공유할 때 매우 유용한 도구다. 특히 쇼핑 카트처럼 상태가 여러 컴포넌트에서 필요할 때 Props Drilling을 방지하고 코드를 깔끔하게 유지할 수 있다.