컴포넌트 재사용을 위한 고급 기술 HOC와 HOF

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

HOC MDN 공식문서 보러가기

이전에 작성한 클로저의 연장선상에 있는 포스트이다.

고차 컴포넌트(HOC, Higher Order Component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술입니다. 고차 컴포넌트(HOC)는 React API의 일부가 아니며, React의 구성적 특성에서 나오는 패턴입니다.

구체적으로, 고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.

고차 컴포넌트에 대해 리액트 공식 문서에서는 위와 같이 설명하고 있다.

로그인 유저만 접근이 가능한 메뉴에 비로그인 회원이 접근할 경우 로그인 페이지로 이동시키는 기능을 구현한다고 가정해보겠다.

1.로그인 회원만 이용이 가능한 컴포넌트 페이지

import { gql, useQuery } from '@apollo/client';
import { withAuth } from '../../src/components/commons/hocs/withAuth';

const FETCH_USER_LOGGED_IN = gql`
  query fetchUserLoggedIn {
    fetchUserLoggedIn {
      email
      name
    }
  }
`;

function LoginSuccessPage() {
  const { data } = useQuery(FETCH_USER_LOGGED_IN);

  return (
    <div>
      <div>{data?.fetchUserLoggedIn.name} 환영합니다</div>
      <div>이메일주소는 {data?.fetchUserLoggedIn.email}</div>
    </div>
  );
}

export default withAuth(LoginSuccessPage);

2.로그인 여부 판별 컴포넌트

import { useRouter } from 'next/router';
import { useEffect } from 'react';

export const withAuth = (Component) => (props) => {
  // 권한 분기 로직 추가

  const router = useRouter();
  useEffect(() => {
    if (!localStorage.getItem('accessToken')) {
      alert('로그인 후 이용 가능합니다');
      router.push('/23-06-login-hoc');
    }
  }, []);

  return <Component {...props} />;
};

위의 코드는 1번의 페이지가 렌더링 되기 전에 2번 코드가 먼저 실행되어 로그인 여부를 먼저 확인하게 되도록 되어 있다. 눈여겨 볼 부분은 1번 코드의 마지막 부분이다.

고차함수(HOF, Higher Order Function)

사실 이미 고차함수는 나도 모르게 사용하고 있었다. 바로 .map, .sort와 같은 메서드를 사용할때 고차함수를 사용하고 있었던 것이다.

이 고차함수를 react에서 사용할 때 유용한 방법이 있다.

그동안 특정 버튼을 클릭했을 때, id 값을 넘겨주기 위해 event.target.id를 사용하곤 했다.

하지만, 이는 고유한 id를 태그에 입력하게 되므로, 예기치 못하게 id가 중복되어 작성되는 경우에 오작동을 할 가능성이 있습니다.

물론, 서비스의 규모가 커지면 커질수록 가능성은 더 높아질 것이다. 이러한 이유로 HOF를 사용하게 됩니다. (또한, HOF를 사용하면 기존에 UI프레임워크를 사용하면서 발생했던 문제(id가 사라지는 문제)도 해결됩니다.

// 기존의 방법
export default function Aaa(){

	const onClickButton = (event) => {
		console.log(event.target.id)
	}

	return <button id={123} onClick={onClickButton}>클릭</button>
	
}

그동안 이렇게 사용을 했다면 고차함수를 사용하면 아래와 같이 바꿀 수 있다.

// HOF 방법
export default function Bbb(){

	const onClickButton = (id) => (event) => {
		console.log(id)
	}

	return <button onClick={onClickButton(123)}>클릭</button>
	
}