구원자인가, 또 다른 괴물인가 (HOC)

52025년 08월 20일3

“함수를 인자로 받고, 함수를 반환하는 함수를 ‘고차 함수(Higher-Order Function)’라고 부르죠.”

리액트 팀의 회의실. 화이트보드 앞에 선 한 시니어 개발자가 함수형 프로그래밍의 기본 개념을 설명하고 있었다.

“이 아이디어를 리액트 컴포넌트에 적용해 보는 겁니다. 컴포넌트를 인자로 받고, 새로운 기능을 덧씌운 컴포넌트를 반환하는 함수. 우리는 이것을 ‘고차 컴포넌트(Higher-Order Component, HOC)’라고 부릅니다.”

개념은 지극히 우아했다.
댄은 동료의 설명에 집중하며, 머릿속으로 코드를 그렸다.

먼저, ‘친구의 온라인 상태 구독’이라는 공통 로직을 처리하는 함수를 하나 만든다. 이 함수의 이름은 withFriendStatus라고 짓는다. 이름 앞의 with는 무언가를 ‘덧입힌다’는 관례적인 표현이었다.

// HOC의 기본 구조
function withFriendStatus(WrappedComponent) {
  // 이 안에서 상태 로직을 처리하는 새로운 클래스 컴포넌트를 만든다.
  class WithStatus extends React.Component {
    // ... componentDidMount, componentWillUnmount 등에서 상태 구독 로직 처리 ...
    // ... isOnline 상태를 내부적으로 관리 ...

    render() {
      // 그리고 원본 컴포넌트에게 isOnline 상태를 prop으로 내려준다.
      return <WrappedComponent isOnline={this.state.isOnline} {...this.props} />;
    }
  }
  // 그렇게 만들어진 새로운 컴포넌트를 반환한다.
  return WithStatus;
}

withFriendStatus라는 마법 상자는 어떤 컴포넌트든 집어넣으면, isOnline이라는 새로운 능력을 가진 컴포넌트로 바꿔서 뱉어냈다.

이제 이 HOC를 사용하는 것은 놀랍도록 간단했다.
온라인 상태 표시가 필요한 컴포넌트들을 각각 withFriendStatus로 감싸주기만 하면 되었다.

// FriendProfile 컴포넌트에 온라인 상태 확인 기능을 추가
const FriendProfileWithStatus = withFriendStatus(FriendProfile);

// ChatListItem 컴포넌트에도 똑같은 기능을 추가
const ChatListItemWithStatus = withFriendStatus(ChatListItem);

더 이상 코드를 복사해서 붙여넣을 필요가 없었다.
상태 로직은 이제 withFriendStatus라는 단 하나의 함수 안에서 중앙 관리되었다. 만약 API가 바뀌면, 개발자는 오직 이 함수 하나만 수정하면 되었다. FriendProfileChatListItem은 자신의 주된 임무인 UI 렌더링에만 집중할 수 있게 되었다. 그들은 isOnline이라는 값이 어디서 오는지 신경 쓸 필요조차 없었다. 그냥 props로 내려오는 값을 사용하기만 하면 되었다.

“이거다!”

팀 내부에서 탄성이 터져 나왔다.
마치 어지러운 전선들을 하나의 멀티탭으로 깔끔하게 정리한 기분이었다. HOC는 상태 로직 재사용 문제에 대한 명쾌하고 강력한 해결책처럼 보였다. 리액트 커뮤니티는 이 패턴을 빠르게 받아들였고, 수많은 라이브러리들이 HOC를 기반으로 만들어지기 시작했다.

댄 역시 처음에는 HOC의 우아함에 매료되었다. 그는 이 패턴이 리액트의 고질적인 문제를 해결할 구원자가 될 것이라고 믿었다.

하지만 그 믿음은 오래가지 않았다.
페이스북의 애플리케이션이 점점 더 복잡해지고, 하나의 컴포넌트가 여러 종류의 상태 로직을 필요로 하기 시작하면서, HOC의 어두운 그림자가 서서히 모습을 드러내기 시작했다.

구원자로 여겨졌던 이 패턴은, 예상치 못한 모습의 또 다른 괴물을 낳고 있었다. 댄은 며칠 뒤, 자신의 모니터에 떠오른 리액트 개발자 도구의 컴포넌트 트리를 보며 깊은 한숨을 내쉬게 될 터였다. 그곳에는 아무도 예상치 못했던 ‘지옥’이 펼쳐지고 있었다.