본문 바로가기
React Native

0022. Front-end 공부하기13 - Context

by 보초코더^_^;; 2023. 12. 26.
반응형

섹션 14. Context

1강. Context란?

  • 기존의 일반적인 React 애플리케이션에서는 데이터가 컴포넌트의 props를 통해 부모에서 자식으로 단방향 전달
  • 컨텍스트 : 리액트 컴포넌트들 사이에서 데이터를 기존에 props를 통해 전달하는 방식 대신 컴포넌트 트리를 통해 곧바로 컴포넌트로 전달하는 새로운 방식

 

기존 방식, 부모에서 자식으로 내려오는 데이터 전달 방식이다(출처 : soaple)

  • 이 방식의 단점은 단방향으로 전달되기 때문에 반복되는 코드가 많아지고 복잡해진다는 것임
  • 예를 들어, Root에서 10단계 밑에 있는 자식으로 전달하려면 props를 통해 10번 내려가야 되는 것임
  • 이 불편함을 개선하기 위해 등장한 것이 바로 context

 

일일이 내려갈 필요 없이 데이터가 필요한 컴포넌트에 바로 데이터 전달 가능(출처 : soaple)

 

  • 코드도 매우 깔끔해지고, 데이터를 한 곳에서 관리하기 때문에 디버깅을 하기에도 굉장히 편리함

 

1) 언제 Context를 사용해야 할까?

  • 여러 개의 Component들이 접근해야 하는 데이터
    - 예 : 로그인 여부, 로그인 정보, UI테마, 현재 언어 등..

 

기존 방식. theme props가 계속 하위 컴포넌트로 전달해 주어야 한다. (출처 : soaple)

매우 반복적이고 비효율적이기 때문에 번거롭다.

 

기존 데이터와 동일한 기능을 컨텐스트로 구현한 코드. (출처 : soaple)

 

 

2) Context를 사용하기 전에 고려할 점

  • 컨텍스트는 다른 레벨의 많은 컴포넌트가 특정 데이터를 필요로 하는 경우에 주로 사용
  • 하지만, 무조건 Context를 사용하는 것이 좋지는 않다.
    - 컴포넌트와 컨텍스트가 연동되면 재사용성이 떨어지기 때문
  • 다른 레벨의 많은 컴포넌트가 데이터를 필요로 하는 경우가 아니라면 기존에 사용하던 방식대로 props를 통해 데이터를 전달하는 컴포넌트 컴포지션 방법이 더 적합함

Link 컴포넌트가 user와 avatarSize가 필요해서 여러 단계에 걸쳐서 전달하는 것이 비효율적이다. (출처 : soaple)

  • props를 통해 전달하는 방식에서 Avatar에 추가로 정보가 필요하면 같은 방식으로 또 전달해야 하기 때문에 매우 번거롭다.
  • 여기서 컨텍스트를 사용하지 않고 이런 문제를 해결하는 방법은 아바타 컴포넌트를 변수에 저장하여 직접 넘겨주는 것

 

  • 유저와 아바타 사이즈가 props로 들어간 아바타 컴포넌트를 userLink라는 변수에 저장한 뒤에 해당 변수를 하위 컴포넌트로 넘기고 있음
  • 이렇게 해야 가장 상위 레벨에 있는 페이지 컴포넌트만 아바타 컴포넌트에서 필요로 하는 유저와 아바타 사이즈에 대해 알고 있으면 된다.

 

  • 이 방식은 중간 레벨의 컴포넌트를 통해 전달해야 하는 props를 없애고 코드를 더욱 간결하게 쓸 수 있도록 만들어 줌
  • 최상위에 있는 컴포넌트에 좀 더 많은 권한을 부여해 줌

 

<단점>

데이터가 많아질수록 상위 컴포넌트에 몰리기 때문에 상위 컴포넌트는 점점 더 복잡해지고 하위 컴포넌트는 너무 유연해지게 됨

 

하위 컴포넌트의 의존성을 상위 컴포넌트와 분리할 필요가 있는 경우에 사용 (출처 : soaple)

  • Context 사용 적합한 예 : 현재 지역 정보, UI테마, 캐싱된 데이터 등


2강. Context API

1) Context, Provider

  • 컨텍스트를 사용하기 위해서 가장 먼저 할 일은 컨텍스트를 생성하는 것
    - React.creatContext() 함수 사용

Context 생성. 함수의 파라미터로 기본 값을 넣어주면 된다.

 

  • 하위 컴포넌트들이 해당 컨텍스트의 데이터를 받을 수 있도록 설정해 주어야 함
    - Context.Provider 함수 사용
    - Provider : 데이터를 제공해 주는 컴포넌트
  • 모든 컨텍스트 개체는 Provider라는 React 컴포넌트를 갖고 있음
  • Context.Provider 컴포넌트로 하위 컴포넌트들을 감싸주면 모든 하위 컴포넌트들이 해당 컨텍스트의 데이터에 접근할 수 있음

Provider 사용

 

  • Provider 컴포넌트에서는 value라는 prop이 있으며, 이것은 Provider 컴포넌트 하위에 있는 컴포넌트들에게 전달됨
  • 하위 컴포넌트들이 이 값을 사용하게 되는데, 이 하위 컴포넌트들이 데이터를 소비한다는 뜻에서 컨슈밍 컴포넌트라고 부름
  • 컨슈밍 컴포넌트는 컨텍스트 값의 변화를 지켜보다가 값이 변경되면 재렌더링 됨
  • 하나의 프로바이더 컴포넌트는 여러 개의 컨슈밍 컴포넌트와 연결될 수 있으며, 여러 개의 프로바이더 컴포넌트는 중첩되어 사용될 수 있음
  • 프로바이더 컴포넌트로 감싸진 모든 컨슈밍 컴포넌트는 프로바이더의 value prop이 바뀔 때마다 재렌더링 됨
  • 값이 변경되었을 때, 상위 컴포넌트가 업데이트 대상이 아니더라도 하위에 있는 컴포넌트가 context를 사용한다면 하위 컴포넌트에서는 업데이트가 일어남
    - 이 때, 값의 변화를 판단하는 기준은 JavaScript 객체의 object.is라는 함수와 같은 방식으로 판단.

<object.is 함수에 대한 자세한 내용>

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#decription 

 

Object.is() - JavaScript | MDN

The Object.is() static method determines whether two values are the same value.

developer.mozilla.org

 

2) Provider value에서 주의해야 할 사항

  • 컨텍스트는 재렌더링 여부를 결정할 때 레퍼런스 정보를 사용하기 때문에 Provider의 부모 컴포넌트가 재렌더링 되었을 경우 의도치 않게 컨슈머 컴포넌트가 재렌더링이 일어날 수 있는 문제가 있음
  • 위의 코드는 프로바이더 컴포넌트가 재렌더링 될 때마다 모든 하위 컨슈머 컴포넌트를 재렌더링함. 
    - value prop을 위한 새로운 객체가 매번 생성되기 때문
    - value를 직접 넣는 것이 아닌, 컴포넌트의 state로 옮기고 해당 state의 값을 넣어주어야 함

 

수정한 이후의 모습

  • value를 직접 넣는 것이 아니라 state를 선언하고, state의 값을 프로바이더에 넣음

 

3) Class.contextType

  • Class.contexdtType은 프로바이더 하위에 있는 class 컴포넌트에서 context의 데이터에 접근하기 위해 사용하는 것
  • 거의 사용하지 않기 때문에, 이런 방법이 있다는 정도만 알면 될 듯

 

4) Context.Consumer

  • Consumer 컴포넌트 : 컨텍스트의 데이터를 구독하는 컴포넌트
  • 클래스 컴포넌트에서는 앞에 나온 Class.contextType을 사용하면 되고, 함수 컴포넌트에서는 Context.Consumer를 사용하여 컨텍스트를 구독

 

  • 컴포넌트의 자식으로 함수가 올 수 있는데, 이것을 function as a child라고 함
  • Context.Consumer로 감싸주면 자식으로 들어간 함수가 현재 컨텍스트의 value를 받아서 react node로 리턴하게 됨
    - 이때, 함수로 전달되는 value는 프로바이더의 value prop과 동일함
  • 상위 컴포넌트에 프로바이더가 없다면 value 파라미터는 creat context를 호출할 때 넣는 기본 값과 동일한 역할을 함

 

5) function as a child

컴포넌트의 자식으로 함수를 사용하는 방법

 

React에서는 기본적으로 하위 컴포넌트들을 children이라는 prop으로 전달해 주는데, children으로 컴포넌트 대신 함수를 사용하여 위의 코드와 같이 사용할 수 있음

 

6) Context.displayName

Context 객체는 displayName이라는 문자열 속성을 가짐

크롬의 리액트 개발자 도구에서는 Context의 Provider나 Consumer를 표시할 때, 이 displayName을 함께 표시함

myDisplayName이 React 개발자 도구에 표시됨

 

7) 여러 개의 Context 사용하기

Class.context는 한 번에 하나만 사용 가능

Context.provider를 중첩해서 사용하면 여러 개의 Context 사용 가능

  • Theme 컨텍스트와 User 컨텍스트에 대해서
  • App 컴포넌트에서는 각 컨텍스트에 대해 2개의 프로바이더를 사용하여 자식 컴포넌트인 레이아웃을 감싸줌
  • 실제 컨텍스트의 데이터를 사용하는 컨텐츠 컴포넌트에서는 2개의 컨슈머 컨포넌트를 사용하여 데이터 전달
  • -> 이렇게 하면 여러 개의 컨텍스트를 동시에 사용할 수 있음

2개 또는 그 이상의 컨텍스트의 값이 자주 함께 사용될 경우, 모든 값을 한 번에 제공해 주는 별도의 렌더 플랍 컴포넌트를 직접 만드는 것을 고려하는 게 좋음

 

 

8) useContext()

  • 함수 컴포넌트에서 컨텍스트를 사용하기 위해 컴포넌트를 매번 컨슈머 컴포넌트로 감싸주는 것보다 더 좋은 방법이 있음
    - 7강에서 배운 훅을 사용
  • useContext 훅은 함수 컴포넌트에서 컨텍스트를 쉽게 사용할 수 있게 해 줌

 

 

  • react.creatContext 함수 호출로 생성된 컨텍스트 객체를 인자로 받아서 현재 컨텍스트의 값을 리턴함
  • useContext 훅을 사용하면 컨텍스트의 값을 다른 방식과 동일하게 컴포넌트 트리상에서 가장 가까운 상위 프로바이더로부터 받아오게 됨
  • 컨텍스트의 값이 변경되면 변경된 값과 함께 useContext 훅을 사용하는 컴포넌트가 재렌더링 됨
  • useContext 훅을 사용하는 컴포넌트의 렌더링이 꽤 무거운 작업일 경우에는 별도로 최적화 작업을 해줄 필요가 있음
    - 파라미터로 context 객체를 넣어줘야 함

Consumer나 Provider를 넣으면 안 됨

 

 

3강. (실습) Context를 사용하여 테마 변경 기능 만들기

 

npm start를 하면

 

밝은 버전

 

어두운 버전

 

 

반응형