
React에서는 함수형 컴포넌트에서 상태값을 관리하기 위해 useState라는 React Hook을 제공한다. 그리고 관리해야 하는 상태값이 복잡해지는 경우를 대비하여 useReducer라는 React Hook도 제공한다.
이렇게 만든 상태값이나 그 이외의 데이터들이 컴포넌트 사이에 공유되어야 하는 경우가 있을 것이다. 그럴 때 사용하는 것이 props이다.
// App.jsx
export default function App() {
const [num, setNum] = useState(0);
return (
<div>
<Component num={num} />
</div>
);
}
// Component.jsx
export default function Component({ num }) {
// num을 사용한 컴포넌트 설계
}
하지만 props만으로 컴포넌트 사이에 데이터를 공유한다면 Prop drilling이 발생할 수 있다.
Prop drilling은 React에서 props를 오로지 하위 컴포넌트로 전달하는 용도로만 사용되는 컴포넌트들을 거치며 props가 전달되는 현상을 의미한다. 아래는 이해를 돕기 위한 그림이다.

위 이미지와 같은 경우(Prop drilling)가 발생하면 코드를 작성하기에도 번거롭고 props를 추적하기도 어렵다.
이러한 상황이 발생하는 것을 막으면서도 여러 컴포넌트 사이에 데이터를 공유하기 위해 React에서는 Context를 제공하고 있습니다.
Context는 부모 컴포넌트가 자신의 자식 컴포넌트 전체에게 데이터를 전달할 수 있게 한다. props와 다를게 뭐냐라고 생각할 수 있지만, props를 전달할 필요 없이 context는 원하는 자식 컴포넌트에서만 데이터를 뽑아서 쓸 수 있다.

위 그림과 같이 Context를 선언하고 Context.Provider라는 Wrapper로 해당 Context를 사용할 컴포넌트들을 감싼다. 그러면 상태가 바뀔 때마다 여러 컴포넌트를 통해 props를 전달하지 않고도, Context Provider를 통해 Context에 바로 접근할 수 있다는 장점이 존재한다.
사용법을 간단하게 알아보자.
// 1.
createContext(initialState);
// 2.
<Context.Provider value={value}>
{children}
</Context.Provider>
// 3.
useContext(Context);
// NumContext.js
import { createContext } from "react";
// 사용할 Context 생성
export const NumContext = createContext(0);
export const SetNumContext = createContext(null);
// App.jsx
export default function App() {
const [num, setNum] = useState(0);
return (
<div>
<NumContext.Provider value={num}>
<SetNumContext.Provider value={setNum}>
<Component />
</SetNumContext.Provider>
</NumContext.Provider>
</div>
);
}
// Component.jsx
export default function Component() {
// num을 사용할 컴포넌트에서 context를 꺼내어 쓴다.
const num = useContext(NumContext);
const setNum = useContext(SetNumContext);
...
}
위 코드는 직접 작성해본 예제이다.
useState로 num과 setState를 선언하긴 했지만 다시 Context에 대입하여 Provider로 묶여있는 children에서는 어디에서나 값을 바꾸고 사용할 수 있다. useReducer와 많이 사용하기도 한다.
React는 Context가 변경될 때마다 해당 Context를 사용하는 모든 컴포넌트를 리렌더링 시킨다. Context의 변경 여부는 Object.is 함수를 활용하여 비교한다.
Context가 만능은 아니다. props를 사용하는 것이 더 좋은 경우가 존재할 수 있다.
Prop drilling이 일어나지 않는 상황이라면 굳이 Context를 사용하지 않아도 된다. props가 여러 컴포넌트를 거쳐 가는 것은 자연스러운 현상이다.
props를 사용함으로써 컴포넌트 사이의 데이터 흐름을 명확하게 파악할 수 있기 때문에 코드 유지보수에 유리할 수 있다.
컴포넌트 분리가 충분히 이루어지지 않는다면 props를 전달하는 층이 증가할 수도 있다.
<Layout posts={posts} />를 <Layout><Posts posts={posts} /></Layout>으로 컴포넌트를 분리하고 children 형태를 활용하면 컴포넌트 층이 줄어들게 된다.