Context API 를 이용한 전역 상태 관리

taehyung·2023년 10월 30일

React.js

목록 보기
16/24
post-thumbnail

Context API의 이해

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

프로젝트 크기에따라 컴포넌트 갯수는 비약적으로 늘어날 수 있는데, 그렇다면 최상위 컴포넌트에서 계속해서 Props를 이용하여 하위 컴포넌트로 전달해주어야 합니다.

Root - A - C - E - G ..

이런 현상을 Props 드릴링이라고하며, 관리하는 상태가 많아진다면 추 후 유지보수 측면에서 굉장한 손해를 보게 될 수 있습니다.

그렇기 때문에 리덕스, MobX 등 전역 상태관리를 도와주는 라이브러리들이 생겨났습니다.

필자는 추후에 리덕스에대해 공부할 예정이며 오늘은 Context API로 전역 상태 관리에 대해 맛을 보려합니다.

Context는 부모 컴포넌트로부터 자식 컴포넌트로 전달되는 데이터의 흐름과는 상관없이 전역적인 데이터를 다룰 때 사용합니다. 전역 데이터를 Context에 저장한 후, 데이터가 필요한 컴포넌트에서 해당 데이터를 불러와 사용할 수 있습니다.

Context API를 이용한 전역상태관리는, Context의 Provider(제공자)Consumer(소비자)를 이용합니다.

Context에 저장된 데이터를 사용하기 위해서는 공통 부모 컴포넌트에 Context의 Provider를 사용하여 데이터를 제공해야 하며, 데이터를 사용하려는 컴포넌트에서 Context의 Consumer를 사용하여 실제로 데이터를 사용합니다.


구조의 이해

  1. Context
    전역으로 관리할 데이터, 공급자, 소비자 등을 관리해줄 관리자 객체 입니다. 만들어진 context를 다른 컴포넌트에서 사용하기위헤 export 해줘야합니다.
  2. Context.Provier (공급자)
    하위 컴포넌트에 전역으로 관리할 데이터를 전달해주는 역할을 하는 컴포넌트 입니다.
    공급자 컴포넌트 내부에 선언된 하위 컴포넌트는 공급자 컴포넌트가 제공하는 전역 데이터를 프롭스 드릴링없이 사용할 수 있습니다.
  3. 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의 값을 업데이트하고 동적으로 관리하는 방법을 알아보겠습니다.

동적 Context 사용하기

createContext의 기본값 설정은 반드시 관리할 상태값만 있어야 하는 것은 아닙니다. 함수, 객체, 원시타입 데이터 등 무엇이든 가능합니다.

Context 생성

  1. Context 생성시 상태, 상태를 변경할 함수를 미리 모델링
  2. Context.Provider 를 리턴하는 함수를 만듭니다.
    Childeren으로 들어온 컴포넌트를 포함하여 제공자로 만들어진 컴포넌트를 반환합니다.
    UseState를 이용하여 전역으로 관리할 상태를 선언하고 수정할 함수를 Provider 에 value props로 넘겨줍니다.
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

바로 위 예제에서 useContext 를 사용하여 전역상태에 접근하였는데요, Consumer를 사용하지 않고 쉽게 접근할 수 있고, 사용도 편리하다는 장점이 있습니다.

함수형 컴포넌트에서만 사용할 수 있다는 단점을 빼고는 Consumer 보다 용도파악도 확실해지고 간편하게 사용할 수 있습니다.

profile
Front End

0개의 댓글