본문 바로가기

Develop Log/React

React - 고차컴포넌트(HOC) 알아보기

반응형

고차 컴포넌트(HOC, Higher Order Component)

고차 컴포넌트(HOC, Higher Order Component)란?

고차 컴포넌트는 React에서 사용되는 기술로써 React API 일부가 아닌 React의 구성적 특성에서 나오는 패턴이다.

고차 컴포넌트는 컴포넌트의 재사용, 로직 추상화, 상태 관리 등에 유용하게 사용되는 패턴인데, 이는 자바스크립트의 고차 함수(Higher-Order Function) 개념에서 비롯된 것으로 다른 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수이다.

고차 컴포넌트의 목적

  1. 코드 재사용: 리액트 컴포넌트는 독립적인 단위로 기능을 캡슐화한다. 이로 인해 컴포넌트 간에 상태나 메소드를 공유하는 것이 어려울 수 있는데, 고차 컴포넌트는 이 문제를 해결하기 위한 하나의 방법으로 사용된다. 특정 기능을 가진 고차 컴포넌트를 만들어 두면, 그 기능을 사용해야 하는 다른 컴포넌트들이 그 고차 컴포넌트를 사용하도록 만들어 코드를 재사용할 수 있다.
  2. Cross-cutting concerns: 애플리케이션의 여러 부분에서 걸쳐 나타나는 기능이나 로직을 "cross-cutting concerns"라고 한다. 예를 들어, 로깅, 데이터 페칭, 액세스 제어 등이 이에 하는데, 이러한 종류의 로직은 종종 여러 컴포넌트에 걸쳐 나타나기 때문에, 이를 재사용 가능한 고차 컴포넌트로 만들면 코드의 중복을 피하고 일관성을 유지할 수 있게 된다.
  3. 컴포넌트 수정 없이 로직 추가: 고차 컴포넌트를 사용하면 기존 컴포넌트의 구조를 바꾸지 않고도 새로운 기능을 추가하거나 변경할 수 있게 되며, 이는 코드의 유지보수를 간단하게 만들어주고, 코드의 가독성을 높여준다.

 

코드를 통해 확인해보기

고차컴포넌트를 사용한 예시로 loading 기능에 관련된 컴포넌트를 작성해 보았다.

import React from 'react';

// 고차 컴포넌트(HOC)
const withLoading = (WrappedComponent) => {
  return (props) => {
    if (props.isLoading) {
      return <div>Loading...</div>;
    } else {
      return <WrappedComponent {...props} />;
    }
  };
};

// 일반적인 함수형 컴포넌트
const MyComponent = (props) => {
  return <div>Hello, {props.name}!</div>;
};

// 고차 컴포넌트를 사용하여 함수형 컴포넌트 감싸기
const MyComponentWithLoading = withLoading(MyComponent);

코드를 보았을 때, withLoading이라는 이름의 컴포넌트는 고차 컴포넌트(HOC)로 구성되어 있다. 이 HOC는 다른 컴포넌트WrappedComponent를 인자로 받고, 새로운 컴포넌트를 반환하는 함수이다. 반환되는 컴포넌트는 isLoading 프로퍼티를 체크하여, 만약 isLoading가 참이면 "Loading..."을 표시하고, 아니라면 원래 컴포넌트를 그대로 렌더링 하게 된다.

MyComponent는 이름을 출력하는 매우 단순한 함수형 컴포넌트이다.

withLoading HOC를 사용하여 MyComponent를 감싸서 새로운 컴포넌트 MyComponentWithLoading를 만들었는데 이렇게 만든 MyComponentWithLoading는 로딩 상태에 따라 내부 컴포넌트의 내용이 다르게 렌더링 된다.

 

React.memo

React 내부에서도 이런 HOC 패턴의 함수를 자주 볼 수 있는데 그중 하나는 React.memo()이다.

React.memo는 주어진 컴포넌트의 props가 변경되지 않았다면 React가 컴포넌트를 다시 렌더링하지 않도록 하는데 이런 React.memo내부에 개발자가 구성한 컴포넌트를 적용함으로써 고차 컴포넌트를 사용하게 된다.

const MyComponent = (props) => {
  // 개발자가 만든 컴포넌트
};

export default React.memo(MyComponent); // 컴포넌트를 인자로 넣고 props의 변경이 없다면 렌더링을 새로 하지 않는다

 

사용 시 주의점

  1. Props 이름 충돌: 고차 컴포넌트가 props를 조작하다 보면 동일한 prop 이름을 가진 여러 HOC들 사이에서 충돌이 발생할 수 있으니 props가 중복되지 않도록 주의해야 한다.
  2. 컴포넌트 구조의 복잡성: HOC는 종종 새로운 컴포넌트를 생성하며, 이로 인해 React의 컴포넌트 계층이 복잡해져 디버깅에 어려움을 겪을 수 있다.
  3. Ref 전달 문제: 고차 컴포넌트를 통해 생성된 컴포넌트는 자신에게 전달된 ref를 자신의 자식 컴포넌트에게 전달하지 않는다. 이는 React에서 ref는 실제 props가 아닌 key처럼 특별하게 취급되기 때문인데, 이 문제는 React.forwardRef 등을 사용해 해결할 수 있지만, 이에 대한 추가적인 처리가 필요하게 된다.
  4. 코드의 추적이 어려워질 수 있다: HOC는 로직을 추상화하여 공통 로직을 재사용하게 해 주지만, 고차 컴포넌트가 감싸는 방식 때문에 코드의 추적이 어렵게 만들 수 있다. 즉, HOC가 렌더링 하는 컴포넌트가 어떤 것인지 즉시 파악하기 어려울 수 있다.
반응형