본문 바로가기
웹 프론트엔드/React 리액트 (기초)

[React] 컨텍스트(context) 기능으로 데이터 관리하기

by 지구인한군 2020. 10. 14.

 리액트 기초 관련 포스팅에서 프로퍼티(props)와 스테이트(state)로 컴포넌트의 데이터를 관리하는 법을 배웠습니다. 다시 한번 정리하자면 주로 외부 데이터는 프로퍼티로, 내부 데이터는 스테이트로 관리했었습니다. 그래서 이번에는 프로퍼티의 단점을 보완한 컨텍스트 기능으로 데이터를 관리해보는 시간을 갖겠습니다. 아래 링크도 참고해 보세요.


2020/09/15 - [Web프론트엔드/리액트] - [React] 컴포넌트의 데이터 관리 ① - props 프로퍼티

2020/09/19 - [Web프론트엔드/리액트] - [React] 컴포넌트의 데이터 관리 ② - state 스테이트

2020/09/21 - [Web프론트엔드/리액트] - [React] 컴포넌트의 데이터 관리 ③ - 콜백 함수

2020/09/29 - [Web프론트엔드/리액트] - [React] 리액트 기초 관련 글 정리 (개발환경, 데이터, 컴포넌트 등)


 프로퍼티의 특징은 읽기 전용이며 부모에서 자식 컴포넌트로 전달이 됩니다. 특히 위에서 아래로 내려가는 데이터 흐름은 장점도 존재하지만, 계층 구조가 깊은 경우에는 여러 문제점이 생깁니다. 그래서 컨텍스트 기능을 사용하면 저장소에서 공급하는(provider) 데이터를, 소비하고(consumer) 싶은 컴포넌트가 받아서 사용할 수 있습니다.

 

// ..\test_project\src\components\CountUpContextProvider.jsx

import React from "react";

// 컨텍스트로 공급자와 소비자를 생성
const { Provider, Consumer } = React.createContext({});

// 소비자 익스포트
export { Consumer };

export default class CountUpContextProvide extends React.Component {
  constructor(props) {
    super(props);

    // 스테이트값은 꼭 초기화!!!
    this.state = {
      count: 1,
      isTen: false,
    };

    // countUp함수에서 this.state 객체인식을 위해 바인딩
    this.countUp = this.countUp.bind(this);
  }

  countUp() {
    // 숫자 증가
    const countTemp = this.state.count + 1;
    let isTenTemp = this.state.isTen;

    // 10이상이면 true
    if (countTemp >= 10) isTenTemp = true;

    // 스테이트값 변경은 꼭 리액트의 setState 메서드 사용
    this.setState({
      count: countTemp,
      isTen: isTenTemp,
    });
  }

  render() {
    // 콘텍스트 데이터
    const context = {
      ...this.state,
      countUp: this.countUp,
    };

    // 자식 컴포넌트에 데이터 제공 (value 프로퍼티 사용)
    return <Provider value={context}>{this.props.children}</Provider>;
  }
}

 

 먼저 저장소 개념의 컴포넌트를 만들었습니다. React.createContext()로 데이터 공급자와 소비자를 생성합니다. 공급자에는 스테이트 관련 포스팅에서 실습했던 버튼 CountUp 기능을 넣었습니다. 그리고 마지막으로 render() 부분에 Provider로 감싼 자식 컴포넌트에게 value 값을 연결하였습니다. 이게 끝입니다! 이제 소비자를 만들어 보시죠~ ㄱㄱ

 

// ..\test_project\src\components\CountUpContextConsumer.jsx

import React from "react";
import PropTypes from "prop-types";
import Text from "./Text";
import Button from "./Button";
import { Consumer } from "./CountUpContextProvider";

function CountUpContextButton({ text }) {
  return (
    // 소비자로 데이터 사용
    <Consumer>
      {(value) => (
        <React.Fragment>
          <Text>숫자값 : {value.count}</Text>
          <br />
          <Text>10이상 : {String(value.isTen)}</Text>
          <Button name="countUp" onClick={value.countUp}>
            {text}
          </Button>
        </React.Fragment>
      )}
    </Consumer>
  );
}

CountUpContextButton.propTypes = {
  text: PropTypes.string,
};

export default CountUpContextButton;

 

 소비자 부분은 더 간단합니다. 위에서 export한 Consumer 부분을 import하고 Provider에서 넘겨받은 value값을 소비하고 싶은 부분에 화살표 함수 식으로 사용하시면 됩니다. Provider에서 this.props.children으로 정의했기 때문에 이런 식으로 사용이 가능합니다. 즉 프로바이더의 하위에 있다면 언제든지 저장소 개념으로 사용이 가능합니다.

 

// ..\test_project\src\stories\Story_Context.jsx

import React from "react";
import { storiesOf } from "@storybook/react";
import Text from "../components/Text";
import CountUpContextProvider from "../components/CountUpContextProvider";
import CountUpContextConsumer from "../components/CountUpContextConsumer";

storiesOf("Context", module)
  .addWithJSX("CountUp", () => (
    <CountUpContextProvider>
      <Text xlarge primary>
        One Provide and One Button
        <br />
        <CountUpContextConsumer text="버튼" />
      </Text>
    </CountUpContextProvider>
  ))
  .addWithJSX("CountUp Two Button", () => (
    <CountUpContextProvider>
      <Text xlarge primary>
        One Provide and Two Button
        <br />
        <CountUpContextConsumer text="버튼1" />
        <CountUpContextConsumer text="버튼2" />
      </Text>
    </CountUpContextProvider>
  ))
  .addWithJSX("CountUp Two Provide", () => (
    <Text xlarge primary>
      Two Provide and Two Button
      <br />
      <React.Fragment>
        <CountUpContextProvider>
          <CountUpContextConsumer text="버튼1" />
        </CountUpContextProvider>
        <CountUpContextProvider>
          <CountUpContextConsumer text="버튼2" />
        </CountUpContextProvider>
      </React.Fragment>
    </Text>
  ));

 

 스토리북에 세 가지 타입으로 만들어 넣어봤습니다. <CountUpContextProvider>로 감싸고 Consumer를 사용하는 컴포넌트라면 전부 데이터를 공유할 수 있습니다. 첫 번째는 기본적인 프로바이더, 두 번째는 하나의 프로바이더이기 때문에 같은 데이터를 공유하고, 세 번째는 프로바이더가 두 개이기 때문에 각자 다른 데이터를 사용합니다.

 

한개의 데이터를 한개의 버튼이 사용

 

한개의 데이터를 두개의 버튼이 사용

 

두개의 데이터를 두개의 버튼이 각자 사용

 

 보시는 바와 같이 두 번째는 같이 데이터를 공유하기 때문에 버튼을 누르면 같이 카운트가 되지만, 세 번째는 각자 다른 프로바이더에서 데이터가 공급되기 때문에 분리가 되어 있습니다. 이렇듯 컨텍스트를 사용하면 프로퍼티 보다 조금 더 원활하고 유연한 데이터 관리가 가능합니다. 다만 데이터의 변경에는 주의를 기울일 필요가 있습니다.

 

그럼 오늘은 여기까지!

 

참 쉽죠?


2020/10/16 - [Web프론트엔드/리액트] - [React] 리덕스(redux) 기능으로 데이터 관리하기 ①

댓글