고차 컴포넌트 (Higher-Order Components)

YUKI KIM·2022년 2월 25일
1

고차 컴포넌트 (HOC, higher-order component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술이다. 즉, 아래의 그림처럼 원래의 컴포넌트를 렌더링하면서 추가적인 기능을 포함시켜 새로운 컴포넌트를 반환하는데, 리액트 공식문서 고차 컴포넌트를 읽으면서 정리해보았다.


크로스 커팅 문제에 사용하기

객체 지향 소프트웨어 개발에서 횡단 관심사 또는 크로스 커팅 문제는 다른 관심사에 영향을 미치는 프로그램의 aspect다. 이는 디자인과 구현 면에서 시스템의 나머지 부분으로부터 깨끗이 분해되지 못하는 경우가 있을 수 있으며 분산(코드 중복)되거나 얽히는(시스템 간의 상당한 의존성 존재) 일이 일어날 수 있다.

이전에는 크로스 커팅 문제를 제어하기 위해 mixin 사용을 권장했다고 한다. mixin과 HOC의 비교는 아래 그림을 보면 쉽게 이해 가능할 것이다.

컴포넌트는 React에서 코드 재사용의 기본 단위이다. 그러나 일부 패턴은 기존의 컴포넌트에 적합하지 않을 수 있다.

언제 사용하면 좋을까

예를 들어, 댓글 목록을 렌더링하는 CommentList 컴포넌트와, 블로그 포스트를 렌더링하는 BlogPost 컴포넌트가 있다고 하자. 두 컴포넌트는 당연히 동일하게 생기진 않았겠지만 data를 fetch하는 구현체는 동일하다.

  1. 마운트되면, change 리스너를 data에 추가한다.
  2. data가 변경되면 렌더링할 state를 data로 만들어준다. (setState)
  3. 마운트 해제되면 change 리스너를 제거합니다.

어플리케이션에서 data를 구독하고 setState를 호출하는 동일한 패턴이 반복적으로 발생한다면, 이 로직을 추상화 하는 것이 좋다. 이 때 고차 컴포넌트가 탁월하다.


고차 컴포넌트 예제

앞서 말했듯이, HOC는 컴포넌트 로직을 재사용하기 위해 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수이다. 그런데 HOC를 사용할 때 주의 사항이 있다.

고차 컴포넌트 주의 사항

  • 항상 함수로 감싸줘야 한다.
  • render 메소드 안에서 HOC를 사용하면 안된다.
  • 정적 메소드는 따로 복사해야 한다.
  • ref는 전달되지 않는다. (React.forwardRef API 사용)

사용 예제

App.js는 가장 하위에 위치할 컴포넌트다.

// App.js
import TestComponent from './TestComponent';

const App = () => {
    return (
        <h1>
            김혜수 하이하이
            <TestComponent name="테스트 컴포넌트" />
        </h1>
    )
}

export default App;

export 할 때 withChildrenTestComponent 컴포넌트를 호출하면서 자기 자신을 파라미터로 전달하고 있다. 이 때, TestComponent 컴포넌트는 export한 것이 아니므로 props.name : {this.props.name}은 TestComponent 컴포넌트에서 render 되지 않는다.

// TestComponent.js
import React from 'react';
import withChildrenTestComponent from './withChildrenTestComponent';

class TestComponent extends React.Component {
    render() {
        console.log('2. TestComponent Render');
        return (
            <div>props.name : {this.props.name}</div>
        )
    }
}

export default withChildrenTestComponent(TestComponent, 'TestComponent');

위에 TestComponent 컴포넌트에서 withChildrenTestComponent(TestComponent, 'TestComponent') 자체를 export 했었다. 따라서 여기서의 render 부분에 <InComponent {...this.props} />에서 TestComponent 컴포넌트의 render 부분이 실행되는 것이다.

// withChildrenTestComponent.js
import React from "react";

export default function withChildrenTestComponent(InComponent, InComponentName) {
    return class OutComponent extends React.Component {
        componentDidMount() {
            console.log(`3. InComponentName : ${InComponentName}`);
        }
        render() {
            console.log('1. InComponent Render');
            return (
                <InComponent {...this.props} />
            )
        }
    }
}


고차 컴포넌트의 단점

HOC라고 항상 옳은 것은 아니다. 고차 컴포넌트의 단점(이라 쓰고 HOC 도입 시 주의할 점이라 읽자.)은 다음과 같다.

  1. 사용자가 직접 넘긴 props 외에도 암묵적으로 넘어오는 속성 값이 존재한다. (this.props.dispatch)
  2. 고차 컴포넌트 간에 같은 이름의 속성 값을 사용하면 충돌이 발생한다.
  3. 의례적인 절차가 많이 필요하다.
  4. 사실 너무 잘개 쪼개진 고차 컴포넌트는 오히려 렌더링 성능에 좋지 않다.

따라서, 요구사항을 분석해서 적절한 리액트 코딩 패턴을 선택하는 것이 적절하다.


레퍼런스

profile
유키링と 욘데 쿠다사이

0개의 댓글