React 컴포넌트 재사용을 위한 Higher-Order Component 패턴

문제 상황

어드민 대시보드를 만들면서 인증이 필요한 컴포넌트가 늘어났다. 각 컴포넌트마다 componentWillMount에서 로그인 체크를 하다 보니 동일한 코드가 반복됐다.

class Dashboard extends Component {
  componentWillMount() {
    if (!this.props.isAuthenticated) {
      this.props.history.push('/login');
    }
  }
  render() { /* ... */ }
}

HOC 패턴 적용

Higher-Order Component는 컴포넌트를 받아서 새로운 컴포넌트를 반환하는 함수다. 공통 로직을 HOC로 분리했다.

function withAuth(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      if (!this.props.isAuthenticated) {
        this.props.history.push('/login');
      }
    }

    render() {
      return this.props.isAuthenticated 
        ? <WrappedComponent {...this.props} />
        : null;
    }
  };
}

// 사용
const AuthenticatedDashboard = withAuth(Dashboard);

추가 개선

Redux의 connect처럼 여러 HOC를 조합할 수 있다. recompose 라이브러리도 검토 중이다.

const enhance = compose(
  withAuth,
  withLogging,
  connect(mapStateToProps)
);

export default enhance(Dashboard);

주의사항

  • HOC 내부에서 원본 컴포넌트를 수정하지 말 것
  • displayName을 설정해 디버깅을 쉽게 할 것
  • ref는 전달되지 않으므로 필요시 별도 처리

React 공식 문서에서도 권장하는 패턴이라 팀에 공유했다. Mixin보다 명확하고 조합하기 좋다.