
리액트에서는 기본적으로 Props로 컴포넌트 간 데이터를 전달할 수 있다. 하지만 전달하려는 데이터가 직접적인 부모-자식 관계가 아니라, 수차례의 props 전달을 거쳐야 할 때가 있다.
이를 Props Drilling이라고 하며, 이는 불필요한 코드를 많이 작성해야 해서 불편함을 준다.
이를 해결하기 위해 Context 개념이 존재한다. Context는 리액트에서 데이터를 전달하는 또 다른 방식이다. 리액트는 기본적으로 데이터가 단방향으로 흐르기 때문에 부모 컴포넌트 -> 자식 컴포넌트로 데이터 전달이 기본이다. 이러한 방식에 Props가 있었는데, Props는 바로 하위 자식으로만 데이터 전달이 안돼서 Props Drilling이 생겼다.
이를 극복하기 위해 Context를 사용하면 데이터는 전달하면서 Props Drilling은 막을 수 있다.
아래 공식문서 내용을 보면, Context는 부모 컴포넌트가 하위에 있는 컴포넌트가 얼마나 깊든 간에 하위에 있는 모든 컴포넌트에게 정보를 전달할 수 있게 하는 방식이다. 이는 데이터를 Props로 전달하는 것이 필요하지 않도록 한다.
Context lets the parent component make some information available to any component in the tree below it—no matter how deep—without passing it explicitly through props.
좀 더 쉽고 직관적으로 이해해보면, Context는 한국말로 맥락이다. Context는 하나의 덩어리로, 여러 개의 컴포넌트를 묶어 하나의 맥락을 갖게 한다고 이해하면 쉽겠다.
Provider와 Consumer 컴포넌트를 사용하여 컨텍스트 데이터를 제공하고 소비하는 방식useContext라는 훅을 사용하여 더욱 간편하게 사용가능.Consumer 컴포넌트를 사용할 필요 없어짐!!Context를 themeProvider로 사용하는 것을 가장 많이 봤다. 리액트 공식문서 예시에도 나와 있는 비슷한 예시를 통해 사용법을 이해해보자.
App 컴포넌트가 루트 컴포넌트다.
이때 App의 하위에 있는 Form 컴포넌트가 있다.
그리고 Form 컴포넌트 하위에는 Panel, Button 컴포넌트가 있다.
이를 코드로 표현하면 아래와 같은 구조가 된다.
// App.jsx
export default function MyApp() {
return (
<Form />
)
}
// Form.jsx
export default function Form() {
return (
<Panel title="Welcome">
<Button>Sign up</Button>
<Button>Log in</Button>
</Panel>
);
}
이떄, App 전체적으로 통일되는 테마를 적용하고 싶다. 다만, 이 테마는 각 컴포넌트 중 필요한 컴포넌트에서만 사용할 것이다. 현재는 Panel, Button에서 사용하고자 한다.
그렇다면, 사용법은 다음과 같은 순서가 된다.
createContext로 context를 생성한다. 이는 루트인 App.js에서 만든다. 그리고 테마를 적용해줄 범위를 Provider로 감싼다.
아래 코드에서는 1)에서 ThemeContext를 만든다. 그리고 2)에서 Provider로 감싸서 적용한다.
// App.jsx
import { createContext } from 'react';
const ThemeContext = createContext(null); //1)
export default function MyApp() {
return (
<ThemeContext.Provider value="dark"> // 2)
<Form />
</ThemeContext.Provider>
)
}
Form.js는 Panel, Button을 감싸기만 하는 컴포넌트이므로 따로 Context를 불러올 필요가 없다.
// Form.jsx
function Form() {
return (
<Panel title="Welcome">
<Button>Sign up</Button>
<Button>Log in</Button>
</Panel>
);
}
실제 사용할 Panel, Button 컴포넌트에서
1)과 같이 useContext를 import 한다. 이후
2)와 같이 useContext의 파라미터로 ThemeContext를 넘겨준다. 이렇게 하면, theme라는 값에 context 값을 반환하게 된다. 이때 리액트는 컴포넌트 트리를 검색해 특정 context에 대해 가장 가까운 context provider를 찾는다. 그리고
3)과 같이 사용할 수 있다.
이는 Button 컴포넌트에서도 마찬가지다.
// Panel.jsx
import { useContext } from 'react'; //1)
function Panel({ title, children }) {
const theme = useContext(ThemeContext); //2)
const className = 'panel-' + theme; //3)
return (
<section className={className}>
<h1>{title}</h1>
{children}
</section>
)
}
// Button.jsx
function Button({ children }) {
const theme = useContext(ThemeContext);
const className = 'button-' + theme;
return (
<button className={className}>
{children}
</button>
);
}
리액트 공식 홈페이지 Props Drilling
리액트 공식 홈페이지 Context
+some GPT의 지원