[우이삭] Context API

수연·2023년 12월 5일

Wooisac

목록 보기
1/9

서론

이전 프로젝트인 니르바나의 기술 스택을 정할 때 상태관리 라이브러리로 Recoil 을 도입했다. 나도 Recoil 을 도입하는 데 이견이 없었지만 무언가를 알고 사용하자고 한 것은 아니었다. 팀원들이 사용해보고 싶어했고, 나도 무언가를 하나 더 알아두면 무조건 좋을 것이라고 생각했다.

그렇다면 다른 라이브러리 도입에 있어선 무언가 달랐을까? 답은 No 였다. Recoil 과 비슷하게 다른 라이브러리들도 의견 교환 없이 바로바로 선정되었고, 프로젝트가 끝나고 보니 해당 기술들을 적재적소에 활용했다는 자신이 없었다.

그래서 이번 프로젝트에선 기술 하나를 새로 도입할 때 왜 우리가 해당 기술을 사용해야 하는가? 를 간단히라도 짚고 사용하는 것이 작은 목표가 되었다.

상태관리 라이브러리

우선 팀원들끼리의 회의가 있었다. 지난 프로젝트에서 사용한 상태 관리 라이브러리는 무엇이며 어떤 점이 좋았는지 등에 대해 얘기를 나눴다.

사용한 라이브러리는 달랐지만 전역 상태 관리가 필요한 곳은 드물다. 라는 의견은 공통적이었다. 그래서 이번엔 상태 관리 라이브러리를 사용하는 대신 react-queryContext API 를 사용하기로 했다. 다음과 같은 이유에서였다.

  1. 기본이 되는 Context API

    멘토님과 커피챗을 진행하며 상태 관리에 대한 이야기가 나왔다. 상태 관리 라이브러리는 내부적으로 Context API 를 사용하기 때문에 그 원리를 잘 이해할 필요가 있다고 하셨다.

    Context API 에 대한 이해도가 낮은 상태에서 다른 상태 관리를 이해하려고 해도 잘 안될 것 같아 이번 기회에 Context API 를 사용하며 익혀보기로 했다.

  2. prop drilling 해결

    전역적으로 사용할 상태가 드물긴 해도, 특정 데이터가 필요하지 않은 컴포넌트에 데이터를 내려보내주는 일은 꽤 불편한 일이다.

    다른 패키지를 추가로 설치하지 않으면서도 필요한 컴포넌트만 컨텍스트를 구독하는 방식으로 Prop drilling 문제를 해결할 수 있어 Context API 가 적합하다고 생각했다.

Context API 가 필요한 상황

상태 관리 라이브러리의 필요성은 리액트로 프로젝트를 진행해본 사람이라면 쉽게 알 수 있다. prop 을 저 멀리까지 넘겨주기 위해 사용하지도 않는 컴포넌트에게까지 prop 을 전달해주는 현상이나, 자식의 자식의 자식의…. 컴포넌트에게 해당 prop 을 모두 전달하고 싶은 경우가 더러 있다.

아래의 경우 prop 으로 받는 level 컴포넌트에 따라 각각 다른 크기의 Heading 을 반환한다

<Section>
  <Heading level={3}>About</Heading>
  <Heading level={3}>Photos</Heading>
  <Heading level={3}>Videos</Heading>
</Section>

이 경우 level 을 각각 컴포넌트에 전달해야한다는 번거로움이 있는데, 그러기 보단 Section 에 level 을 전달해주면 Heading 에 전달하는 방식이 더 편할 것이다.

<Section level={3}>
  <Heading>About</Heading>
  <Heading>Photos</Heading>
  <Heading>Videos</Heading>
</Section>

그런데 이 경우 어떻게 Heading 컴포넌트가 가장 가까운 조상 컴포넌트 Section 의 level 을 알 수 있을까? 이 경우 자식 컴포넌트가 데이터를 요청하는 기능이 필요할 것이다.

이 과정은 props 만으로는 이뤄질 수 없기 때문에 Context 가 필요하다.

Context 의 3단계

  • Create Context 를 생성한다.
  • Use Context 를 사용한다.
  • Provide 등록한 데이터를 제공한다.

컨텍스트는 다음 그림과 같이 자식 컴포넌트에 데이터를 전달할 수도 있고, 더 아래에 있는 특정 컴포넌트에만 데이터를 등록할 수도 있다.

사용방법

Create

createContext 메서드를 사용하여 전역적으로 사용할 상태를 추가한다.

파라미터의 값으론 초기 데이터 값이 들어가는데, 위 예제에선 level 을 전달하고 있으므로 1 이라는 숫자를 defaultValue 로 주었다.

// state/level.js

import { createContext } from 'react';

export const LevelContext = createContext(1);

Use

useContext 메서드를 사용하여 생성한 Context 를 사용할 수 있다.

import { createContext } from 'react';
import { LevelContext } from 'state/level.js';

기존의 Heading 컴포넌트는 아래와 같이 prop 으로 전달 받는 형식으로 데이터를 가져왔다.

const Heading = ({level, children}) => {
	// ...
}

이제 Context API 를 사용함으로써 다음과 같이 prop을 줄일 수 있다.

import { createContext } from 'react';
import { LevelContext } from 'state/level.js';

const Heading = ({ children }) => {
	const level = useContext(LevelContext);
}

Provider

생성한 Context 를 제공해주기 위해선 제공해주려는 컴포넌트에서 하위로 내려줘야 한다.

Context.Provider 를 사용하여 컴포넌트를 감싸고, 넘겨주고자 하는 값을 value 로 넘겨준다.

import { LevelContext } from 'state/level.js';

const Section = ({level, children}) => {
	return (
		<LevelContext.Provider value={level}>
			{children}
		</LevelContext.Provider>
	)
}

Context API 단점

React 공식 문서에서 Context API 사용 방법을 읽어본 결과 꽤 간결하고 쉬운 문법을 가지고 있다는 것을 알게 되었다. 그런데 왜 사람들은 다른 상태 관리 라이브러리를 도입하려고 하는 것일까?

리렌더링의 문제

현재 나온 상태 관리 라이브러리는 어떤 상태가 변경되면 해당 상태를 참고하고 있는 컴포넌트만 업데이트 시킨다. 하지만 Context API 는 어떤 데이터가 업데이트 되면, 해당 데이터를 사용하는 컴포넌트를 포함하여 해당 컨텍스트를 구독하는 모든 컴포넌트가 업데이트 된다.

이에 따라 적절하게 Context Provider 의 범위를 설정하고 여러개의 Context 로 나눔으로써 해결할 수 있긴 하나, 이 역시 코드의 복잡성이 증가하는 문제로 번질 우려가 있다.

수많은 Provider 생성의 문제

리렌더링 문제를 해결하기 위해 여러개의 Context 로 나누면 코드의 복잡성이 증가한다. Context 단위가 증가할 수록 수많은 Provider 를 작성하게 될 수 있다.

더불어 이렇게 많은 Context 를 생성하게 될 시 Context 를 작성하고 사용하는 것이 편리함의 범주를 벗어나기 때문에 Context API 보다 보기 쉽고 편리한 상태관리를 찾는 것이라 판단을 내렸다.

0개의 댓글