프론트엔드/React

[UMC] 나의 두번째 React Hook (useContext)

yujenius 2025. 3. 24. 02:05

최근에 umc 활동을 하면서 React의 여러 가지 개념을 배우고 있는데, 이번에는 useContext에 대해 공부했다. 

처음에는 props drilling 이 왜 문제인지 정확히 와닿지 않았는데, 직접 코드를 작성하고 나니 

useContext의 필요성을 확실히 깨달을 수 있었다. 

이 글에서는 props drilling의 문제를 알아보고, 이를 해결하는 방법으로 useContext를 사용하는 방법을 정리해보려고 한다.

 

Props drilling 문제 이해하기

React에서 부모 → 자식 → 손자로 데이터를 전달하려면 props를 계속 내려줘야 한다.
이 과정에서 중간에 위치한 컴포넌트가 실제로는 데이터를 사용하지 않지만,

단순히 props를 전달하기 위해 존재해야 하는 문제가 발생한다.
이런 현상을 props drilling(프롭 드릴링) 이라고 한다.

import { useState } from 'react';
import ButtonGroup from './components/ButtonGroup';

function App() {
  const [count, setCount] = useState(0);

  const handleIncrement = () => setCount(count + 1);
  const handleDecrement = () => setCount(count - 1);

  return (
    <>
      <h1>{count}</h1>
      <ButtonGroup handleIncrement={handleIncrement} handleDecrement={handleDecrement} />
    </>
  );
}

export default App;

ButtonGroup.tsx

interface ButtonGroupProps {
  handleIncrement: () => void;
  handleDecrement: () => void;
}

const ButtonGroup = ({ handleIncrement, handleDecrement }: ButtonGroupProps) => {
  return (
    <div>
      <button onClick={handleIncrement}>+1 증가</button>
      <button onClick={handleDecrement}>-1 감소</button>
    </div>
  );
};

export default ButtonGroup;

이 코드에서는 App 컴포넌트에서 ButtonGroup을 거쳐 Button 까지 props 를 계속 전달해야 한다.

하지만 ButtonGroup 자체는 count 값을 사용하지 않으므로 불필요한 props 전달이 발생하는 것이 문제였다. 

 

이를 해결하기 위해서 useContext를 사용할 수 있다. 

 

props drilling 해결하기 (useContext 사용하기)

React의 useContext를 활용하면 중간 컴포넌트를 거치지 않고 필요한 곳에서 바로 데이터를 사용할 수 있다.

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

//Context 생성
const CountContext = createContext<{ count: number; setCount: (value: number) => void } | undefined>(undefined);

//Provider 생성
const CountProvider = ({ children }: { children: ReactNode }) => {
  const [count, setCount] = useState(0);

  return <CountContext.Provider value={{ count, setCount }}>{children}</CountContext.Provider>;
};

//Custom Hook 생성(useContext를 쉽게 사용하도록)
const useCount = () => {
  const context = useContext(CountContext);
  if (!context) throw new Error('useCount must be used within a CountProvider');
  return context;
};

export { CountProvider, useCount };

App.tsx

import { CountProvider } from './context/CountContext';
import Counter from './components/Counter';

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

export default App;

Counter.tsx

import { useCount } from '../context/CountContext';
import ButtonGroup from './ButtonGroup';

const Counter = () => {
  const { count } = useCount();
  return (
    <>
      <h1>{count}</h1>
      <ButtonGroup />
    </>
  );
};

export default Counter;

ButtonGroup.tsx

import { useCount } from '../context/CountContext';
import Button from './Button';

const ButtonGroup = () => {
  const { setCount } = useCount();

  return (
    <div>
      <Button onClick={() => setCount(prev => prev + 1)} text='+1 증가' />
      <Button onClick={() => setCount(prev => prev - 1)} text='-1 감소' />
    </div>
  );
};

export default ButtonGroup;

 

마무리

이번에 공부하면서 props drilling 이 왜 문제가 되는지 직접 경험해 보고,

이를 해결하는 방법으로 useContext를 활용할 수 있다는 걸 배웠다.

물론 처음 써보기에 내가 익숙하게 잘 쓴다는 생각이 들지는 않았지만 여러번 활용해보면 그만큼 언제 Context API를 사용해야 하는지 알 수 있겠다는 생각이 들었다. 앞으로 React 프로젝트를 할 때, 언제 Context API를 사용해야 하는지 고민하면서 적용해봐야겠다.