리액트 프로젝트에서 전역적으로 관리할 상태가 있다면 최상위 컴포넌트에 상태를 선언하고 그 상태를 변경할 함수를 Props로 전달하곤 했습니다.

프로젝트 크기에따라 컴포넌트 갯수는 비약적으로 늘어날 수 있는데, 그렇다면 최상위 컴포넌트에서 계속해서 Props를 이용하여 하위 컴포넌트로 전달해주어야 합니다.
Root - A - C - E - G ..
이런 현상을 Props 드릴링이라고하며, 관리하는 상태가 많아진다면 추 후 유지보수 측면에서 굉장한 손해를 보게 될 수 있습니다.
그렇기 때문에 리덕스, MobX 등 전역 상태관리를 도와주는 라이브러리들이 생겨났습니다.
필자는 추후에 리덕스에대해 공부할 예정이며 오늘은 Context API로 전역 상태 관리에 대해 맛을 보려합니다.

Context는 부모 컴포넌트로부터 자식 컴포넌트로 전달되는 데이터의 흐름과는 상관없이 전역적인 데이터를 다룰 때 사용합니다. 전역 데이터를 Context에 저장한 후, 데이터가 필요한 컴포넌트에서 해당 데이터를 불러와 사용할 수 있습니다.
Context API를 이용한 전역상태관리는, Context의 Provider(제공자)와 Consumer(소비자)를 이용합니다.
Context에 저장된 데이터를 사용하기 위해서는 공통 부모 컴포넌트에 Context의 Provider를 사용하여 데이터를 제공해야 하며, 데이터를 사용하려는 컴포넌트에서 Context의 Consumer를 사용하여 실제로 데이터를 사용합니다.
구조의 이해
ContextContext.Provier (공급자)Context.Consumer (소비자)
Context 만들기
createContext 함수는 파라미터에 전역으로 관리할 데이터를 입력받고, 그 데이터를 기본값으로 지정하여 객체를 반환합니다.
import { createContext } from 'react' // createContext 함수 임포트
const GlobalState = createContext({name:"김태형",age:30}) //전역으로 관리할 상태 입력
export default GlobalState;
반환된 객체는 Consumer, Provider 등 제공자와 소비자에 대한 객체, 함수를 가지고 있으며, 기존에 입력한 데이터또한 가지고있습니다.
파라미터로 입력할 데이터는 원시타입, 참조타입 어떤 타입도 상관없습니다.
Provider 사용하기
Provider 컴포넌트는 Context로 만들어둔 상태를 하위 컴포넌트에 공급해줍니다. 값 변경도 가능합니다.
import GlobalState from './context'
const App = () => {
return (
<GlobalState.Provider value={{name:'김김태태형형', age:100}}>
/*전역 데이터를 사용할 소비자 컴포넌트*/
<GlobalState.Provider/>
)
}
export default App;
useState의 형태와 매우 비슷합니다. useState는 기본값, 기본값을 변경할 함수가 제공됩니다. context API도 마찬가지 입니다. 다만 이름과 사용법이 약간 다를뿐이죠.
주의
Provider를 사용하지 않으면 기본값으로 정의된 컴포넌트가 생성됩니다. Provider를 사용하면 받드시 변경된 상태를 명시해야합니다.
//에러발생
render(
<context.Provider>
<div>
<context/>
<div/>
<context.Provider/>
)
위 경우는 에러를 발생합니다. 또한 Provider 에 명시하지 않은 context의 value는 렌더링에서 제외됩니다.
Consumer 사용하기
Consumer는 Context로 만들어둔 상태를 조회할 수 있습니다.
아래 문법은 <component>children<component/> 문법으로 컴포넌트의 칠드런 위치에 함수를 넘겨주는 방법입니다.
import GlobalState from './context'
const App = () => {
return (
<GlobalState.Provider value={{name:'김김태태형형', age:100}}>
<GlobalState.Consumer>
{(value) => (<div>{value.name}{value.age}</div>)}
<GlobalState.Consumer/>
<GlobalState.Provider/>
)
}
export default App;
간단하게 기본적인 사용법을 배워보았습니다. 현재까지의 방법으로는 고정적인 상태만 관리할 수 있습니다. Context의 값을 업데이트하고 동적으로 관리하는 방법을 알아보겠습니다.
createContext의 기본값 설정은 반드시 관리할 상태값만 있어야 하는 것은 아닙니다. 함수, 객체, 원시타입 데이터 등 무엇이든 가능합니다.
Context 생성
import { createContext, useState } from "react";
1. const ColorContext = createContext({
state: {
color: "black",
subcolor: "red",
},
actions: {
setColor: () => {},
setSubColor: () => {},
},
});
2. const ColorProvider = ({ children }) => {
const [color, setColor] = useState("black");
const [subcolor, setSubColor] = useState("red");
const value = {
state: { color, subcolor },
actions: { setColor, setSubColor },
};
return (
<ColorContext.Provider value={value}>{children}</ColorContext.Provider>
);
};
3. const { Consumer: ColorConsumer } = ColorContext;
export { ColorProvider, ColorConsumer };
export default ColorContext;
Provider 설정
전역으로 선언한 데이터를 사용할 컴포넌트들을 Provider하위에 넣어줍니다.
import ColorBox from "./components/ColorBox";
import SelectColors from "./components/SelectColors";
import ColorContext, { ColorProvider } from "./context/color";
function App() {
return (
<ColorProvider>
<div>
<SelectColors />
<ColorBox />
</div>
</ColorProvider>
);
}
export default App;
Consumer 설정
value 로 넘어온 데이터를 사용, 업데이트 합니다.
import React, { useContext } from "react";
import ColorContext, { ColorConsumer } from "../context/color";
//생성된 Context API를 받아옴
const ColorBox = () => {
const { state } = useContext(ColorContext);
return (
<div>
<>
<div
style={{
width: "100px",
height: "100px",
background: state.color,
}}
/>
<div
style={{
width: "200px",
height: "200px",
background: state.subcolor,
}}
/>
</>
)}
</div>
);
};
export default ColorBox;
바로 위 예제에서 useContext 를 사용하여 전역상태에 접근하였는데요, Consumer를 사용하지 않고 쉽게 접근할 수 있고, 사용도 편리하다는 장점이 있습니다.
함수형 컴포넌트에서만 사용할 수 있다는 단점을 빼고는 Consumer 보다 용도파악도 확실해지고 간편하게 사용할 수 있습니다.