최근에 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를 사용해야 하는지 고민하면서 적용해봐야겠다.
'프론트엔드 > React' 카테고리의 다른 글
[React]React 의존성 충돌 해결 (0) | 2025.02.13 |
---|---|
[React] State 맘대로 변경하는 법 (1) | 2025.02.11 |
[React] JSX 란?! (1) | 2025.02.11 |
React에서 API 통신: fetch API vs axios 무엇을 선택할까? (0) | 2025.02.06 |
돔(DOM) 과 가상돔(Virtual DOM) (1) | 2025.02.06 |