본문 바로가기
React

React - HOC

by 양털의매력 2020. 3. 10.

이 글은 VELOPERT님의 https://velopert.com/3537을 보고 공부하면서 작성한 블로그입니다.


Higher-Order Component (HOC)

HOC는 React 공식문서에 따르면 다음과 같이 나와있다.

 

고차 컴포넌트 (HOC, higher-order component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술입니다. 고차 컴포넌트는 그 자체로는 React API의 일부분이 아닙니다. 고차 컴포넌트는 React의 컴포넌트적 성격에서 나타나는 패턴입니다.

구체적으로 고차 컴포넌트는 컴포넌트를 취하여 새로운 컴포넌트를 반환하는 함수입니다.

 

코드를 작성하다 보면, 반복해서 사용되는 코드나 자주 사용하는 코드를 함수화(모듈화)해서 사용할 때가 있다.

 

리액트에서도 컴포넌트를 만들 때 이러한 반복사용이나 중복사용이 종종 일어나는데 HOC를 이용하여 이것을 줄여줄 수 있다.

 

어떻게 HOC를 통하여 이것이 가능해지는지 알아보기 위해서 간단한 예시를 만들어 보았다.

 


먼저 CRA를 통해 예시 프로젝트를 생성하여 준다.

 

npx create-react-app hoc_sample

 

그리고 필요없는 파일을 삭제한다.

 

directory

그 다음에 동일한 로직이 반복적으로 쓰이는 컴포넌트들을 만들어준다. 

 

LeftComponent와 RightComponent를 아래와 같이 만들어준다.

 

// LeftComponent.js

import React, { Component } from "react";

class LeftComponent extends Component {
  state = {
    value: ""
  };

  componentDidMount() {
    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then(res => res.json())
      .then(result => this.setState({ value: result.title }));
  }

  render() {
    const { value } = this.state;
    return (
      <div className="left_component">
        <div>왼쪽</div>
        {value}
      </div>
    );
  }
}

export default LeftComponent;

 

// RightComponent.js

import React, { Component } from "react";

class RightComponent extends Component {
  state = {
    value: ""
  };

  componentDidMount() {
    fetch("https://jsonplaceholder.typicode.com/posts/2")
      .then(res => res.json())
      .then(result => this.setState({ value: result.title }));
  }

  render() {
    const { value } = this.state;
    return (
      <div className="right_component">
        <div>오른쪽</div>
        {value}
      </div>
    );
  }
}

export default RightComponent;

 

각 컴포넌트들은 state의 value를 렌더링하는 기능을 가지고 있다. 그리고 이 value는 컴포넌트가 마운트 된 후, fetch를 통해 받아온 데이터이다.

// App.js
import React from "react";
import LeftComponent from "./LeftComponent";
import RightComponent from "./RightComponent";

function App() {
  return (
    <div className="App">
      <div className="title">HOC sample</div>
      <div className="components">
        <LeftComponent />
        <RightComponent />
      </div>
    </div>
  );
}

export default App;

 

각 컴포넌트를 만들고 app.js에 import하여 렌더링을 시켜준다.

 

그리고 결과물이 좀 더 잘 보이도록 다음과 같이 css 파일을 추가한다.

 

* {
  box-sizing: border-box;
}

body {
  height: 100%;
  width: 100%;
}

#root {
  height: 100%;
  width: 100%;
}

.components {
  display: flex;
  justify-content: space-around;
}

.left_component {
  border: 1px solid black;
  width: 200px;
  height: 200px;
}

.right_component {
  border: 1px solid black;
  width: 200px;
  height: 200px;
}

 

 

이렇게 하여 렌더링된 화면은 다음과 같다.

렌더링된 화면을 보면 왼쪽 컴포넌트와 오른쪽 컴포넌트가 각각 받아온 데이터를 렌더링하고 있는 것을 볼 수 있다.

 

이 두 컴포넌트에서 반복되는 구조가 있다. 바로 fetch로 받아온 데이터를 받아오는 부분이다. 이것을 HOC로 만들어 반복되는 코드를 없애줄 수 있다.

 

다음과 같은 withRequest HOC를 만들어준다.

 

// withRequest.js

import React, { Component } from "react";

const withRequest = url => WrrapedComponent => {
  return class extends Component {
    state = {
      value: ""
    };

    componentDidMount() {
      fetch(url)
        .then(res => res.json())
        .then(result => this.setState({ value: result.title }));
    }

    render() {
      const { value } = this.state;
      return <WrrapedComponent value={value} />;
    }
  };
};

export default withRequest;

 

그리고 HOC를 이용하여 left와 right 컴포넌트를 바꿔준다.

 

// LeftComponent.js

// componentDidMount 부분이 없어졌다

import React, { Component } from "react";
import withRequest from "./withRequest";

class LeftComponent extends Component {
  render() {
    const { value } = this.props;
    return (
      <div className="left_component">
        <div>왼쪽</div>
        {value}
      </div>
    );
  }
}

// export 시에 컴포넌트를 HOC로 감싸준다
export default withRequest("https://jsonplaceholder.typicode.com/posts/1")(LeftComponent);

 

// RightComponent.js

// Right 컴포넌트도 left 컴포넌트와 동일하게 바꿔주고, fetch url만 다르다

import React, { Component } from "react";
import withRequest from "./withRequest";

class RightComponent extends Component {
  render() {
    const { value } = this.props;
    return (
      <div className="left_component">
        <div>오른쪽</div>
        {value}
      </div>
    );
  }
}

export default withRequest("https://jsonplaceholder.typicode.com/posts/2")(RightComponent);

 

이렇게 하여 실행시킨 화면은 다음과 같다.

 

아까와 동일한 결과는 보여주는 것을 알 수 있다.

 

하지만, HOC를 통하여 반복하여 작성한 코드를 없애고 더 간단한 코드로 작성할 수 있었다.

 

 

여러개의 HOC를 사용해야하는 경우, HOC를 다시 HOC로 감싸주면 된다!! 

 

다음과 같이 withText라는 간단한 HOC를 추가한다.

withText는 단순히 감싸줄 컴포넌트에 props로 text를 전달한다.

 

// withText.js

import React, { Component } from "react";

const withText = WrrapedComponent => {
  return class extends Component {
    render() {
      return <WrrapedComponent text={"another HOC!!"} />;
    }
  };
};

export default withText;

 

그리고 withRequest와 left, right 컴포넌트에도 수정을 해준다.

 

// withRequest.js

...

// props를 다시 전달해주기 위해서
return <WrrapedComponent {...this.props} value={value} />;

...

 

// LeftComponent.js
// RightComponent도 동일하게 작업

import React, { Component } from "react";
import withRequest from "./withRequest";
import withText from "./withText";

class LeftComponent extends Component {
  render() {
    const { value, text } = this.props;
    return (
      <div className="left_component">
        <div>왼쪽</div>
        {value}
        // text 추가
        <div>{text}</div>
      </div>
    );
  }
}

// 한번 더 감싸준다
export default withText(withRequest("https://jsonplaceholder.typicode.com/posts/1")(LeftComponent));

 

결과는 다음과 같다.

 

 

 


HOC를 통해서 React에서 반복적인 컴포넌트 로직을 어떻게 분리하여 재사용할 수 있는지 알아보았다. 

예시는 간단한 것들을 작업했지만, 좀 더 복잡한 로직에도 나중에 사용할 수 있을 것 같다.

 

그동안 React, React Native 프로젝트를 하면서 withRouter나 withNavigation으로 컴포넌트를 감싸주는 것이 무엇인지 모르고 썼었는데 이것들이 HOC였다는 것을 알 수 있었다.

 

그리고 상태관리 라이브러리인 Redux에서 이러한 HOC를 활용한다고 들었는데 Redux를 공부하면서 좀 더 알아보아야겠다.

 

HOC를 공부하면서 관련된 키워드들을 몇가지 알 수 있었는데 이것들도 기회가되면 좀 더 알아봐야겠다.

 

- Wrapper Hell

- React hooks

- Context API

- Redux

'React' 카테고리의 다른 글

React - Life Cycle  (0) 2020.03.24
리액트 개발환경 설정해보기  (0) 2020.03.16

댓글