본문 바로가기
리액트(React)

리액트(React) Context API 알아보기

by 즐거운코딩 2023. 6. 15.
반응형

Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 사용합니다.

예를들면 사용자 로그인 정보, 애플리케이션 환경 설정, 테마 등

리액트 애플리케이션은 컴포넌트 간에 데이터를 props로 전달하기 때문에 다양한 컴포넌트에서 필요한 데이터가 있을 경우 주로 최상위 컴포넌트인 App 의 state에 넣어서 관리합니다.

하지만 실제 리액트 프로젝트에서는 더 많은 컴포넌트를 거쳐야 할 때도 있고 다루어야 할 많아 질 수 있어서 이런 방식으로 사용하면 유지보수가 힘들어질 수 있습니다.

리덕스나 MobX 같은 상태관리 라이브러리를 사용하여 전역 상태 관리 작업을 편하게 처리하기도 하였는데 리액트 v16.3 업데이트 이후에는 Context API가 많이 개선되었기 때문에 별도의 라이브러리를 사용하지 않아도 전역 상태를 손쉽게 관리할 수 있습니다.

Context API 사용 효과 : context를 만들어 단 한 번에 원하는 값을 받아 와서 사용할 수 있음

Context API 사용법

Color box를 예시로 만들어 봅니다.

$yarn create react-app context-tutorial

src 디렉토리에 contexts 디렉토리를 만들고 color.js 파일 생성

import { createContext } from "react";

const ColorContext = createContext({ color: "black" });

export default ColorContext;

새 context를 만들 때 createContext 함수를 사용하고, 파라미터에는 해당 context의 기본 상태를 지정

 

ColorBox 컴포넌트 만들기 : ColorContext의 색상을 보여줌. 색상을 props로 받아오는 것이 아니라 ColorContext 안에 있는 Consumer 컴포넌트를 통해 색상 조회

import ColorContext from '../contexts/color';

const ColorBox = () => {
  return (
    <ColorContext.Consumer>
      {(value) => (
        <div
          style={{
            width: '64px',
            height: '64px',
            background: value.color,
          }}
        />
      )}
    </ColorContext.Consumer>
  );
};

export default ColorBox;

Consumer 사이에 중괄호 안에 함수를 넣어주는 패턴을 Function as a child 혹은 Render Props 라고 함

컴포넌트의 children이 있어야 할 자리에 일반 JSX 혹은 문자열이 아닌 함수를 전달 함

import ColorBox from "./components/ColorBox";

function App() {
  return (
    <div>
      <ColorBox />
    </div>
  );
}

export default App;

64px black box 를 브라우저에서 확인

  • Provider

Provider를 사용하면 Context의 value를 변경 가능 (Provider 사용시 value 입력은 필수 !!!)

import ColorBox from './components/ColorBox';
import ColorContext from './contexts/color';

function App() {
  return (
    <ColorContext.Provider value={{ color: 'red' }}>
      <div>
        <ColorBox />
      </div>
    </ColorContext.Provider>
  );
}

export default App;

동적 Context 사용하기

  • Context 파일 수정하기
    Context의 value에는 상태값 외에도 함수를 전달 가능 합니다.

ColorContext 코드를 수정

import { useState } from 'react';
import { createContext } from 'react';

const ColorContext = createContext({
  state: { color: 'black', subcolor: 'red' },
  actions: {
    setColor: () => {},
    setSubcolor: () => {},
  },
});

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>
  );
};

// const ColorConsumer = ColorContext.Consumer와 같은 의미
const { Consumer: ColorConsumer } = ColorContext;

// colorProvider와 ColorConsumer 내보내기
export { ColorProvider, ColorConsumer };

export default ColorContext;

ColorProvider 컴포넌트를 새로 작성하고 그 컴포넌트에서 ColorContext.Provider 를 렌더링 합니다.

Provider의 value에는 상태는 state로, 업데이트 함수는 actions 로 묶어서 전달합니다.

Context에서 값을 동적으로 사용할 때 이렇게 state와 actions 객체를 따로따로 분리하면 다른 컴포넌트에서 Context값을 사용할 때 편합니다.

createContext를 사용할 때 객체도 Provider에 넣는 value에 넣는 객체의 형태와 일치 시켜주는 것이 Context 코드를 볼 때 내부 값이 어떻게 구성되어 있는지 파악하기 쉽고, 실수로 Provider를 사용하지 않았을 때 리액트 애플리케이션에서 에러가 발생하지 않습니다.

 

이렇게 새로워진 Context를 프로젝트에 다음과 같이 반영합니다.

App 컴포넌트에서 ColorContext.Provider 를 ColorProvider로 대체 합니다.

import ColorBox from './components/ColorBox';
import { ColorProvider } from './contexts/color';

function App() {
  return (
    <ColorProvider>
      <div>
        <ColorBox />
      </div>
    </ColorProvider>
  );
}

export default App;

ColorBox도 ColorContext.Consumer 를 ColorConsumer로 변경합니다.

import { ColorConsumer } from '../contexts/color';

const ColorBox = () => {
  return (
    <ColorConsumer>
      {({ state }) => (
        <>
          <div
            style={{
              width: '64px',
              height: '64px',
              background: state.color,
            }}
          />
          <div
            style={{
              width: '32px',
              height: '32px',
              background: state.subcolor,
            }}
          />
        </>
      )}
    </ColorConsumer>
  );
};

export default ColorBox;

이제 색상 선택 컴포넌트를 만들어 보겠습니다.

Context에 actions에 넣어준 함수를 호출하는 컴포넌트로 components 디렉토리에 SelectColors.js 라는 파일을 만듭니다.

const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];

const SelectColors = () => {
  return (
    <div>
      <h2>색상을 선택하세요.</h2>
      <div style={{ display: 'flex' }}>
        {colors.map((color) => (
          <div
            key={color}
            style={{
              background: color,
              width: '24px',
              height: '24px',
              cursor: 'pointer',
            }}
          />
        ))}
      </div>
      <hr />
    </div>
  );
};

export default SelectColors;

이 컴포넌트를 App 컴포넌트에서 ColorBox 위에 렌더링 합니다.

import ColorBox from './components/ColorBox';
import SelectColors from './components/SelectColors';
import { ColorProvider } from './contexts/color';

function App() {
  return (
    <ColorProvider>
      <div>
        <SelectColors />
        <ColorBox />
      </div>
    </ColorProvider>
  );
}

export default App;

이제 SelectColors에서 마우스 왼쪽 버튼을 클릭하면 큰 정사각형 색상을 변경하고, 마우스 오른쪽 버튼을 클릭하면 작은 정사각형의 색상을 변경하도록 수정합니다.

import { ColorConsumer } from '../contexts/color';

const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];

const SelectColors = () => {
  return (
    <div>
      <h2>색상을 선택하세요.</h2>
      <ColorConsumer>
        {({ actions }) => (
          <div style={{ display: 'flex' }}>
            {colors.map((color) => (
              <div
                key={color}
                style={{
                  background: color,
                  width: '24px',
                  height: '24px',
                  cursor: 'pointer',
                }}
                onClick={() => actions.setColor(color)}
                onContextMenu={(e) => {
                  e.preventDefault(); // 마우스 오른쪽 버튼 클릭시 메뉴가 뜨는 것을 무시함
                  actions.setSubcolor(color);
                }}
              />
            ))}
          </div>
        )}
      </ColorConsumer>
      <hr />
    </div>
  );
};

export default SelectColors;

마우스 오른쪽 버튼 클릭 이벤트는 onContextMenu 를 사용하면 됩니다. 

브라우저에서 SelectColors에 있는 정사각형들을 마우스 왼쪽 , 오른쪽 버튼으로 클릭하면 하단의 정사각형 색상이 바뀝니다.

useContext Hook을 사용하면 다음과 같이 함수 컴포넌트에서 Context를 아주 편하게 사용할 수 있습니다.

ColorBox 컴포넌트를 다음과 같이 수정합니다.

import { useContext } from 'react';
import ColorContext from '../contexts/color';

const ColorBox = () => {
  const { state } = useContext(ColorContext);
  return (
    <>
      <div
        style={{
          width: '64px',
          height: '64px',
          background: state.color,
        }}
      />
      <div
        style={{
          width: '32px',
          height: '32px',
          background: state.subcolor,
        }}
      />
    </>
  );
};

export default ColorBox;
반응형