본문 바로가기
source-code/React

리액트를 다루는 기술 _ 15장

by mattew4483 2021. 3. 18.
728x90
반응형

목요일은 역시 목요일이군.


15장. Context API

Context API는 React 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능!

(ex 로그인 정보, 애플리케이션 환경 설정, 테마 등)

 

여태 사용했던 stlyed-components, 리액트 라우터 등의 라이브러리도 Context API를 기반으로 구현되어 있다.

 

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

그런데 전역 상태를 관리한다는 게 무슨 뜻?

 

지금껏 우리는 프로젝트 내에서 환경 설정, 사용자 정보와 같이 전역적으로 필요한 상태를 관리할 때

→ 주로 최상위 컴포넌트인 App의 state에 넣어 관리했다!

왼쪽 그림에 해당하는 건데...

App이 지닌 value를 F나 J에 전달하려면 여러 컴포넌트를 거치는 수밖에 없었다!

(App→A→B→F / App→A→B→E→G 과 같이..! 실재 Todo 만들 때도 이렇게 했다)

 

컴포넌트나 데이터가 많아질수록 코드가 복잡해지는 건 당연지사.

따라서 오른쪽 그림과 같이 Context를 만들어 단 한 번에 원하는 값을 받아와 사용할 수 있도록!

 

15-2) Context API 사용법 익히기

일단 context-tutorial이라는 프로젝트를 생성하자.

 

1. 새 Context 만들기

src 디렉터리에 contexts라는 폴더를 만들고, color.js 파일을 생성!

color.js

새 Context를 만들 때는 React의 createContext 함수를 사용한다.

파라미터에는 해당 Context의 상태를 지정!

 

다른 컴포넌트가 이 context를 가지려면 해당 컴포넌트 상위에 context를 정의한 변수(ColorContext)를 감싸면 된다.

 

Context에는 Provider와 Consumer 두 개의 컴포넌트가 존재한다.

Provider은 Context에서 사용할 값을 설정할 때 사용되고,

(이때 전달받는 컴포넌트의 제한 수는 없음)

Consumer은 설정한 값을 불러와야 할 때 사용된다.

 

2. Consumer 사용하기

다음으로 ColorBox란 컴포넌트를 만들어 ColorContext 속 색상을 보여주자.

 

이때 색상을 props로 받아오는 것이 아닌...

ColorContext 안에 들어 있는 Consumer라는 컴포넌트를 통해 색상을 조회한다!

 

components/ColorBox.js

Consumer 사이 중괄호를 열어 그 안에 함수를 넣어 줬다! (Consumer의 자식은 항상 함수여야 함)

이러한 패턴을 Function as a child 혹은 Render Props라고 하는데...

컴포넌트의 children이 있어야 할 자리에 일반 JSX나 문자열이 아닌, 함수를 전달한 것이다.

 

http://localhost:3000/

Context가 잘 뜨는 모습.

 

3. Provider 사용하기

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

App.js

App 컴포넌트를 이렇게 수정해주자!

 

쨘. 정의한 Context가 변경된 모습.

 

즉 createContext 함수를 사용할 때 지정한 기본값은 Provider를 사용하지 않았을 때 적용된다!

이때 Provider를 전달하는 변수는 반드시 value를 사용해야 하며,

Provider는 사용했는데 value를 명시하지 않으면 기본값을 사용하지 않아 오류가 발생한다.

 

15-3) 동적 Context 사용하기

고정적인 값 대신, Context의 값을 업데이트하려면 어떻게 해야 할까?

 

1. Context 수정하기

Context의 value에는 무조건 상태 값만 있어야 하는 건 아니다 → 함수도 전달 가능!

 

기존에 작성했던 ColorContext를 수정해보자.

contexts/color.js

ColorProvider라는 컴포넌트를 새로 만들었고, 여기에서 ColorContext.Provider를 렌더링하고 있다.

 

그런데 여기서 props로 받아온 {children}의 정체는 뭘까?

기본적으로 children은 부모 컴포넌트의 태그 사이에 있는 녀석들 몽땅을 지칭한다.

즉 부모인 App 컴포넌트에서 ColorProvider 사이에 있는 div를 뜻하는 것.

얘를 받아온 후 return에 {children}으로 렌더링함으로써,

Django base.html의 block처럼 해당 부분에 구멍을 뚫어 가져온 것이라 생각하면 되겠다.

 

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

이를 통해 Context에서 값을 동적으로 사용할 수 있게 된 것!

(반드시 state와 actions를 묶어줄 필요는 없지만, 따로 분리해주면 이후 다른 컴포넌트에서 Context를 사용할 때 편하다)

 

추가적으로 createContext를 사용할 때 기본값으로 사용할 객체도 수정!

이때 createContext의 기본값은 실제 Provider의 value에 넣는 객체 형태와 일치시키는 게 좋다.

(Context 코드를 파악하기도 쉬워지고, Provider를 실수로 사용하지 않았을 때 에러 발생도 막는단다)

 

App.js

App 컴포넌트도 이렇게 수정!

 

ColorBox.js

이렇게 바꿔줄 수 있다.

Consumer를 사용했으니 value를 통해 해당 Context 속성들을 사용할 수 있을 테고,

여기서는 그중에서도 .state.color / .subcolor로 현재 지정된 값들을 적용시킨 것!

 

Consumer 자식은 무조건 함수!

실수로 끝에 ; 를 붙였더니 render is not a function react context 에러가 뜨고 말았다!(한참이나 끙끙...)

똑같이 쳤다면 이렇게 뜬다!

 

2. 색상 선택 컴포넌트 만들기

이번에는 Context의 actions에 넣어 준 함수를 호출해보자. SelectColors라는 컴포넌트를 생성!

 

component/SelectColors.js

App에 렌더링 하면 색 별 상자들이 뜨겠지?

 

쨔라란.

 

component/SelectColors.js

이제 기능을 추가!

마우스 왼쪽 클릭을 하면 Color를, 오른쪽 클릭은 하면 subColor를 바꿔주고자 한다.

 

component/SelectColors.js

비구조화 할당을 통해 { value }를 전달받고, value.actions.setColor&subColor를 사용하지 않고,

{ actions }를 전달받고 actions.setColor&subColor 형태로 사용!

 

아차차. 마우스 오른쪽 버튼 클릭 이벤트는 onContextMenu를 사용하면 된다!

(이때 e.preventDefault()를 통해 원래 이벤트(브라우저 메뉴 뜨는 것)가 발생하지 않도록)

 

그럼 클릭할 때마다 색이 잘 바뀌는 걸 볼 수 있다.

 

15-4) Consumer 대신 Hook 또는 static contextType 사용하기

Context에 있는 값을 사용할 때, Consumer 대신 다른 방식을 사용해 값을 받아와 보자!

 

1. useContext Hook 사용하기

우리의 친구 Hooks 중 useContext라는 Hook을 사용하면, 함수형 컴포넌트에서 Context를 편하게 쓸 수 있다.

ColorBox.js

...? 코드가 한결 간단해졌다.

 

children에 함수를 전달하는 Render Props 패턴이 불필요하다면,

useContext Hook을 사용해 훨씬 편하게 Context 값을 조회할 수 있다.

(즉 현재 우리는 ColorContext 속 state만 필요하니까!)

 

2. static contextType 사용하기

얘는 클래스형 컴포넌트에 해당!

(useContext는 당연히 함수형 컴포넌트에만 사용된다)


Context API를 통해 무조건 부모 → 자식 흐름으로 props를 전달하지 않고, 더욱 쉽게 상태 교류가 가능해졌다.

 

당연히 작은 규모의 프로젝트에서는 오히려 불필요하겠지만...

전역적으로 여기저기서 사용되는 상태가 있고, 컴포넌트 개수가 많은 상황이라면 Context API를 활용하는 게 좋겠다.

 

다음 장은 Redux라는 녀석인데...

이 라이브러리도 Context API 기반으로 만들어져 있으며, 전역 상태 관리를 도와준다.

단순한 상태 관리는 Context API만으로도 가능하지만...

Redux는 더욱 향상된 성능과 미들웨어 기능, 개발자 도구, 높은 유지 보수성을 제공한다는 강점이 있다!

728x90
반응형