[React] Context API

hang_kem_0531·2022년 6월 11일
1

React

목록 보기
13/16
post-thumbnail

Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능이다. 이를테면 사용자 로그인 정보, 애플리케이션 환경 설정, 테마 등 여러 종류가 있다.

Context API를 사용한 전역 상태 관리 흐름 이해하기

프로젝트 내에서 환경 설정, 사용자 정보와 같은 전역적으로 필요한 상태를 관리해야 할 때는 어떻게 해야 할까? 리액트 애플리케이션은 컴포넌트 간에 데이터를 props로 전달하기 때문에 컴포넌트 여기저기서 필요한 데이터가 있을 때는 주로 최상위 컴포넌트인 App의 state에 넣어서 관리한다.

G 컴포넌트는 전역 상태를 업데이트시키고, F와 J 컴포넌트는 업데이트된 상태를 렌더링한다. 그렇다면 App 컴포넌트에서는 다음과 같이 상태와 업데이트 함수를 정의해야 한다.

const [value, setValue] = useState('hello');
const onSetValue = useCallback(value => setValue(value), []);

그리고 App이 지니고 있는 value 값을 F 컴포넌트와 J 컴포넌트에 전달하려면 여러 컴포넌트를 거쳐야 한다. F의 경우 App -> A -> B -> F의 흐름이고, J의 경우 App -> H -> J의 흐름이다. 추가로 G 컴포넌트에 상태 업데이트 함수를 전달할 때도 App -> A -> B -> E -> G와 같이 복잡하게 여러 번 거쳐서 전달해야 한다.

실제 리액트 프로젝트에서는 더 많은 컴포넌트를 거쳐야 할 때도 있고 다루어야 하는 데이터가 훨씬 많아질 수도 있으므로, 이런 방식을 사용하면 유지 보수성이 낮아질 가능성이 있다. 그렇기 때문에 리덕스나 MobX 같은 상태 관리 라이브러리를 사용하여 전역 상태 관리 작업을 더 편하게 처리하기도 하는데, 리액트 v16.3 업데이트 이후에는 Context API가 많이 개선되었기 때문에 별도의 라이브러리를 사용하지 않아도 전역 상태를 손쉽게 관리할 수 있다.

Context API를 사용하면 Context를 만들어 단 한번에 원하는 값을 받아 와서 사용할 수 있다.


Context API 사용법 익히기

새 Context를 만들 때는 createContext 함수를 사용한다. 파라미터에는 해당 Context의 기본 상태를 지정한다.

Consumer 사용하기

이번에는 ColorBox라는 컴포넌트를 만들어서 ColorContext 안에 들어 있는 색상을 보여 줄 것이다. 이때 색상을 props로 받아 오는 것이 아니라 ColorContext 안에 들어 있는 Consumer라는 컴포넌트를 통해 색상을 조회할 것이다.

Consumer 사이에 중괄호를 열어서 그 안에 함수를 넣어 주었는데, 이러한 패턴을 Function as a child, 혹은 Render Props라고 한다. 컴포넌트의 children이 있어야 할 자리에 일반 JSX 혹은 문자열이 아닌 함수를 전달하는 것이다.

Provider

Provider를 사용하면 Context의 value를 변경할 수 있다.

기존에 createContext 함수를 사용할 때는 파라미터로 Context의 기본값을 넣어 주었는데, 이 기본값은 Provider를 사용하지 않았을 때만 사용된다. 만약 Provider는 사용했는데 value를 명시하지 않았다면, 이 기본값을 사용하지 않기 때문에 오류가 발생한다. 그렇기 때문에 Provider를 사용할 때는 value 값을 명시해 주어야 한다.


동적 Context 사용하기

이번에는 고정적인 값이 아닌, Context의 값을 업데이트해야 하는 경우 어떻게 해야 하는지 알아보자.

Context 파일 수정하기

Context의 value에는 무조건 상태 값만 있어야 하는 것은 아니다. 함수를 전달해 줄수도 있다.

위 파일에서는 ColorProvider라는 컴포넌트를 새로 작성해 주었고, 그 컴포넌트에서 ColorContext.Provider를 렌더링하고 있다. 이 Provider의 value에는 상태는 state로, 업데이트 함수는 actions로 묶어서 전달하고 있다. Context에서 값을 동적으로 사용할 때 반드시 묶어줄 필요는 없지만, 이렇게 state와 actions 객체를 따로따로 분리해 주면 나중에 다른 컴포넌트에서 Context의 값을 사용할 때 편하다.

추가로 createContext를 사용할 때 기본값으로 사용할 객체도 수정했다. createContext의 기본값은 실제 Provider의 value에 넣는 객체의 형태와 일치시켜 주는 것이 좋다.

Consumer 대신 Hook 또는 static contextType 사용하기

useContext Hook 사용하기

useContext라는 Hook을 사용하면, 함수형 컴포넌트에서 Context를 아주 편하게 사용할 수 있다.

import React, { 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;

그러나 Hook은 함수형 컴포넌트에서만 사용할 수 있다는 점에 주의해야 한다. 클래스형 컴포넌트에서는 Hook을 사용할 수 없다.

static contextType 사용하기

클래스형 컴포넌트에서 Context를 좀 더 쉽게 사용하고 싶다면 static contextType을 정의하는 방법이 있다.

import React, { Component } from 'react';
import ColorContext from '../contexts/color';

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

class SelectColors extends Component {
  static contextType = ColorContext;

	handleSetColor = color => {
      this.context.actions.setColor(color);
    };

	handleSetSubcolor = subcolor => {
      this.context.actions.setSubcolor(subColor);
    };

	render() {
      return (
        <div>
        	<h2> 색상을 선택하세요 </h2>
        	<div style={{ display: 'flex' }}>
              {colors.map(color => (
                <div
                  key={color}
                  style={{
                    background: color,
                    width: '24px',
                    height: '24px',
                   	cursor: 'pointer'
                  }}
                  onClick{() => this.handleSetColor(color)}
                  onContextMenu={e => {
                    e.preventDefault();
                    this.handleSetSubcolor(color);
                  }}
                  />
                  ))}
                  </div>
                <hr />
                </div>
                );
             }
          }
              
export default SelectColors;

static contextType을 정의하면 클래스 메서드에서도 Context에 넣어 둔 함수를 호출할 수 있다는 장점이 잇다. 단점이라면, 한 클래스에서 하나의 Context밖에 사용하지 못한다는 것이다. 그러나 앞으로 새로운 컴포넌트를 작성할 때 클래스형으로 작성하는 일은 많지 않기 때문에 useContext를 사용하는 쪽을 권한다.

profile
Front-End Developer

0개의 댓글