Context Api 어떻게 사용해야 하는가?

DongKyun Ko
Cross-Platform Korea
5 min readNov 26, 2019

본글은 context api의 적절한 사용방법에 대한 방법이 ‘주’ 이며, 부가적으로 hackatalk 오픈소스 프로젝트에서 왜 redux 대신 사용하게 되었는지에 대한 내용이 들어있습니다.

본 글의 목적상 redux의 장점은 이야기 하지 않았지만, 프로젝트에 따라서 redux는 여전히 훌륭한 툴입니다!

급하신 분들은 ‘Context Api 사용' 부터 읽으셔도 됩니다.

State Management

TL;DR

다수의 컴포넌트에서 공통으로 사용하는 state를 관리하는 방법에는
일반적으로 많이 알고 계시는 Redux 와 Mobx 등이 있습니다

프로젝트마다 상황이 다르지만, 대표적인 경우 공통 state는 Theme관련 그리고 server data 등이 있습니다. 그리고 이러한 state 관리는 항상 개발자들에게 골치거리였으며 Redux와 Mobx 등이 나오면서 그나마 쉽게 관리 할수 있게 되었습니다.

여기서 주목해야 할점은… ‘그나마' 라는 단어 입니다.

먼저 많은 사람들에게 사랑 받았던 redux는 제대로 사용하기 위해서는 개념을 잡기 위한 learning curve가 어느정도 있습니다

그리고 문제는 server 와의 data fetching 을 처리하는 코드들입니다… 서버와의 통신 을 하는 코드를 짜면서 requested, is fetching, success, fail 등의 상황에 대한 코드들이 산더미처럼 쌓이기 시작합니다

그러나 hackatalk 에서는 GraphQL 기반의 Apollo client를 사용 하게 되면서 data fetching 에 대한 boilerplate 걱정을 덜 수 있게 되었습니다.

그렇지만 여전히 redux를 사용하게 될때 생기는 피로감이 남아있습니다. 그것은 쉬운 feature에도 reducer, action, dispatch 등을 선언해야 한다는 점입니다 😵

Context Api 로 단점 해결

다들 이미 아시겠지만, Context Api를 적절히 사용하면 우리는 redux를 쓰지 않아도 됩니다!

psuedo code

더 말이 필요 없습니다. 바로 기본 예제로 넘어가 보시죠

다른 state management library를 사용해보신 분들은 아실 겁니다. 매우 간단합니다!😍

❗️단, context Api를 사용할 때에는 주의할 점이 있습니다.
Redux를 사용하는 것 처럼 한개의 커다란 Store, 즉 한개의 커다란 context를 만들어 두고 사용하게 되면 아래와 같은 문제점이 생길 수 있습니다.

console 창의 각 컴포넌트 별 render 카운트를 확인해 보세요

한개의 context 안에서 제공되는 서로 다른 values 를 A와 B 컴포넌트가 따로 사용합니다

A컴포넌트에서는 value A만 바꾸었는데, value B를 사용하는 B컴포넌트 까지 call되어 예기치 못한 리소스낭비가 이루어 집니다.

실 예로, A가 theme을 바꾸는 역할, B가 user를 관리하는 역할이라고 치면, A에서 dark theme으로 바꾸었더니 전혀 연관이 없는 user 관련 컴포넌트가 re-render 될수 있는 상황입니다.

앱이 작으면 크게 문제가 없지만, 앱이 커질 수록 해당 문제는 커다란 퍼포먼스 저하를 야기할수 있습니다.

해결

위의 문제를 해결하기 위한 방법으로 추천된 3가지를 직접 돌려 보겠습니다.

1. split context

A 와 B 컴포넌트가 사용하는 context를 따로 나누어서 사용합니다. 일반적으로 같이 사용되는 feature들 끼리 묶고, 다른 목적의 feature들은 나누어서 사용합니다

2. use memo()

같은 Context를 사용하지만, B 컴포넌트에서 state를 사용하는 코드(C 컴포넌트)를 따로 때어내어 memo() 로 감싸주었습니다.

A 에서 state 를 바꾸면 B 컴포넌트는 call 되지만 state를 직접 사용할 C 컴포넌트는 call 되지 않습니다

  • 1번 방법에 비하여 코드량이 많고, 직관적이지 못하여 readability 가 떨어져 보입니다
  • B 컴포넌트에 value 나 function들이 선언이 되어 있다면 선언문들이 call되어 예기치 못한 resource를 낭비하게 됩니다

3. use useMemo()

사실상 2번 방법과 다를바가 없기 때문에 같은 문제를 내포합니다

비교 결과

1번 방법이 가장 적절해 보이며 실제로 가장 선호되고 있는 방법입니다. 정리하면 아래와 같은 장점들이 있습니다.

  • 쉬운 재 사용성 -> 같은 목적의 feature들 끼리만 묶여있기에 context api를 쉽게 따로 다른 프로젝트로 옮겨서 사용할 수 있습니다
  • redux와 비교하자면, 꼭 reducer를 사용하지 않아도 되기 때문에 간단한 기능을 직관적이고 적은양의 코드로 만들 수 있습니다.

Typescript와 사용시 팁

  1. Typescript와 함께 사용 하다보면 createContext() 사용시 default 값으로 null 또는 undefined 처리를 해주어야 하는 불편한 점을 해결
  2. useContext 사용시 parent에 provider가 선언되어 있는지 아닌지에 대한 명확한 에러처리

추가!

  • dooboo-cli v3 를 사용하시면 쉽게 context api 패턴을 추가 하실 수 있습니다

ref

--

--