컨텍스트(Context)

JaeungE·2022년 1월 20일
0

React 찍어먹기

목록 보기
5/8
post-thumbnail

React 에서는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 props 객체를 이용해서 데이터를 전달한다.

하지만 데이터를 필요로 하는 자식 컴포넌트의 개수가 많다면 같은 코드를 반복해서 입력해야 하고, 중첩 구조를 가질 때 중간에 있는 컴포넌트들은 해당 데이터가 필요하지 않아도 의무적으로 자식 컴포넌트에게 데이터를 전달해야 한다.

이럴 때 컨텍스트(Context)를 활용하면 좀 더 효율적으로 처리할 수 있다.




컨텍스트가 필요한 경우

간단한 예제를 통해 컨텍스트가 필요한 경우부터 알아보자.



App 컴포넌트에서 props 객체를 이용해 유저 정보를 자식 컴포넌트에게 전달하고, UserViewer 컴포넌트에서 해당 데이터를 사용해 렌더링 하는 간단한 예제다.

UserWrap 컴포넌트를 보면 유저 정보가 필요 없기 때문에 다시 자식 컴포넌트로 props를 전달하는 것을 볼 수 있다.

이렇게 데이터를 사용하려는 컴포넌트가 아래에 있으면 중간에 있는 컴포넌트는 의미 없이 데이터 전달만 하게 되고, 컴포넌트 트리가 복잡할수록 더욱 문제가 생긴다.

그럼 컨텍스트를 이용한 코드로 고치기 전에, 어떻게 사용해야 하는지 API 부터 확인하고 넘어가자.





컨텍스트 API

React.createContext(defaultValue)
ex) const ColorContext = React.createContext('red')

컨텍스트 객체를 생성하는 메서드다.

생성된 컨텍스트 객체는 ProviderConsumer 컴포넌트를 가지며, 이를 이용해 데이터의 변경 및 감지가 가능하다.

인자로 전달되는 defaultValue의 경우, 컨텍스트를 구독하는 컴포넌트가 마땅한 Provider를 찾지 못 하면 기본으로 사용되는 값이다.



Context.Provider
ex)

<ColorContext.Provider value="blue" >
  ...ManyComponent
</ColorContext.Provider>

<Context.Priveder> 컴포넌트는 컨텍스트를 구독하고 있는 자식 컴포넌트에게 컨텍스트의 변화를 알리는 컴포넌트다.

value prop을 이용해 전달한 데이터는 Provider 컴포넌트의 자식 컴포넌트 중 해당 컨텍스트를 구독하고 있는 모든 컴포넌트에게 전역적(global)인 데이터가 된다.

따라서 전달 가능한 컴포넌트의 개수에 제한 없이 전달이 가능하다.



Class.contextType
ex)

class AnyClass extends React.Component {
  render() {
  	return <h1>Color is {this.context}</h1>
  }
}
AnyClass.contextType = ColorContext;

클래스 컴포넌트의 contextType 프로퍼티를 이용해 컴포넌트가 컨텍스트를 구독하게 할 수 있다.

일치하는 컨텍스트 중 가장 가까이에 있는 Providervalue prop을 우선으로 전달받으며, 컴포넌트 내부에서 this.context를 이용해 데이터를 가져온다.

단점으로는 클래스 컴포넌트만 가능하며, contextType 프로퍼티를 이용하면 하나의 컨텍스트만 구독이 가능하다.



Context.Consumer
ex)

class AnyClass extends React.Component {
  render() {
    return (
      <ColorContext.Consumer>
        {color => <h1>Color is {color}</h1>}
      </ColorContext.Consumer>
    );
  }
}

위의 Class.contextType과 마찬가지로 컨텍스트를 구독하는 방법 중 하나다.

차이점은 Context.Consumer 컴포넌트를 이용하면 함수 컴포넌트에서도 컨텍스트 구독이 가능하며, 하나의 컴포넌트에서 여러개의 컨텍스트 구독이 가능해진다.

그리고 Context.Consumer 컴포넌트의 자식은 무조건 함수 형태여야 한다는 것을 주의하자.


정리

위에서 설명한 Class.contextTypeContext.Consumer를 이용해 컨텍스트를 구독하는 컴포넌트를 consumer 라고 부르는데, Provider의 하위에 있는 모든 consumer들은 부모 컴포넌트의 재렌더링 여부와 관계없이 Providervalue prop에 변경이 생기면 consumer도 다시 렌더링 된다.

이 부분은 컨텍스트를 필요로 하지 않는 컴포넌트는 업데이트가 일어나지 않게 해서 성능 최적화가 가능하다.

그리고 컨텍스트를 사용하면 컴포넌트의 재사용성이 떨어지기 때문에 항상 컨텍스트를 사용하기 전에 컴포넌트 합성으로 해결이 가능한지 먼저 생각해보자.

여기까지가 기본적인 컨텍스트 API 사용법이다. 이제 컨텍스트를 직접 사용해보자.





기존 코드 변경

위에서 사용했던 코드를 컨텍스트를 활용한 코드로 다시 작성해보자.



컨텍스트를 사용하기 전과 다르게 UserWrap 컴포넌트는 UserViewer 컴포넌트로 어떠한 데이터도 전달하지 않고, ProviderConsumer 컴포넌트를 이용해 App 컴포넌트에서 UserViewer 컴포넌트로 직접 데이터를 전달하는 것을 볼 수 있다.

이번엔 중첩 컨텍스트state 끌어올리기를 사용하는 좀 더 복잡한 예제를 만들어보자.


UserContext.js


컨텍스트 객체를 외부로 내보내는 모듈이다.


index.js


entry point가 될 파일이다.

state를 변경하는 changeUserName() 함수를 setUserContext.Provider 컴포넌트의 value prop으로, 그 아래에 state.namevalue prop으로 전달하는 UserContext.Provider가 중첩되어서 consumer에게 데이터를 전달하고 있다.

이제 UserWrap 컴포넌트의 모든 자식 컴포넌트는 consumer를 통해 Appstate에 접근이 가능해졌다.


User.js


UserForm 컴포넌트에서 setUserContextsetterAppchangeUserName() 메서드, UserContextnameAppstate.name이 된다.


정리하면 UserForminput 엘리먼트에 변경이 생기면 이벤트 핸들러인 setter가 호출되어 부모 컴포넌트인 Appstate를 변경시킨다.

그렇게 되면 UserContext.Providervalue prop에 변경이 생겨 consumer 컴포넌트들이 다시 렌더링 되고, UserContext.Consumer로 전달받은 name값을 이용해 다시 렌더링 된다.

그럼 정상적으로 동작하는지 확인해보자.



의도했던 대로 동작하는 것을 볼 수 있다.





컨텍스트 사용 시 주의점

Context.Provider 컴포넌트의 value prop은 이전 value와의 차이를 비교할 때 참조(reference)를 기준으로 비교한다.

따라서 아래처럼 value prop에 새 객체를 생성해서 전달하게 되면 이전 객체와 차이가 없다고 생각해도, 참조값이 다르기 때문에 value가 변경된 것으로 인식되어 자식 컴포넌트에 불필요한 렌더링이 생길 수 있다.


class MyComponent extends React.Component {
    render() {
        return (
            <Context.Provider value={{ name : 'default' }}>
                ...ManyComponent
            </Context.Provider>
        );
    }
}

이런 상황을 피하기 위해 value prop에 객체를 전달하고 싶다면 아래처럼 state를 이용해서 객체의 참조값을 전달해주자.


class MyComponent extends React.Component {
    consturctor(props) {
        super(props);
        this.state = {
            user : { name : 'default' }
        };
    }

    render() {
        return (
            <Context.Provider value={this.state.user}>
                ...ManyComponent
            </Context.Provider>
        );
    }
}





참고 자료

Context - React
https://ko.reactjs.org/docs/context.html

0개의 댓글