[React] 인증 로직 재사용하기: HoC와 커스텀 훅(Custom Hook)

Sara Jo·2024년 10월 14일
post-thumbnail

English with Suits 프로젝트에서 아래 두 가지의 인증 로직 재사용이 필요했다.

  • 로그인 한 유저만 특정 페이지에 접근할 수 있도록 허용 (ex. 마이페이지)
  • 로그인 하지 않은 유저만 특정 페이지에 접근할 수 있도록 허용 (ex. 로그인 페이지)

이와 같은 인증 로직 재사용을 위해 처음에는 HoC를 사용했다가, 커스텀 훅을 사용하는 방법으로 수정했는데, 컴포넌트 로직 재사용을 위해 사용할 수 있는 이 두가지 방법을 정리해보고 내가 프로젝트에서 어떻게 적용하였으며, 각각의 장단점은 무엇인지 비교해보았다.


HoC란?

공식문서에서는 HoC(Higher Order Component)에 대해 아래와 같이 설명하고 있다.

고차 컴포넌트(HOC, Higher Order Component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술입니다.
구체적으로, 고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
컴포넌트는 props를 UI로 변환하는 반면에, 고차 컴포넌트는 컴포넌트를 새로운 컴포넌트로 변환합니다.

HoC는 리액트의 Hooks가 나오기 전에 컴포넌트 로직 재사용을 위해 주로 사용되었던 패턴으로, 함수형 컴포넌트에서는 Hooks로 대체가 가능해 현재는 전보다 많이 쓰이지 않는 패턴이다.


HoC로 인증 로직 처리하기

사용방법은 아주 간단하다. 재사용되는 로직을 HoC 안에 적용해두고, 해당 로직이 필요한 컴포넌트를 HoC의 인자로 받아 반환하면 된다.

인증된 사용자만 특정 페이지에 접근할 수 있도록 하는 로직을 HoC을 통해 아래처럼 작성하였다.

import { useEffect } from "react";
import { useAuthContext } from "./supabaseProvider";

const withAuth = (WrappedComponent: React.ComponentType) => {
  const ComponentWithAuth = (props: any) => {
    const { user, loading } = useAuthContext();

    useEffect(() => {
      if (!loading && !user) {
        window.location.href = "/login";  // 인증되지 않은 사용자는 로그인 페이지로 리디렉션
      }
    }, [loading, user]);

    if (loading || !user) {
      return <div>Loading...</div>;  // 로딩 중이거나 인증되지 않았을 때 로딩 화면 출력
    }

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

  return ComponentWithAuth;
};

export default withAuth;

위에서 만든 withAuth HoC로 아래와 같이 마이페이지 컴포넌트를 감싸면 마이페이지는 인증된 사용자에게만 노출되고, 인증되지 않은 사용자가 접근 시 로그인 페이지로 리디렉션된다.

function MyPage() {
  ...
}

export default withAuth(MyPage);

커스텀 훅(Custom Hook)으로 인증 로직 처리하기

리액트의 Hooks가 등장한 이후 컴포넌트 로직 재사용을 위해 커스텀 훅을 사용할 수 있게 되었다. 커스텀 훅을 사용하면 함수형 컴포넌트 내에서 상태 관리와 부수 효과를 쉽게 처리할 수 있다.

인증된 사용자만 특정 페이지에 접근할 수 있도록 하는 로직을 이번에는 아래와 같이 커스텀 훅으로 처리해보았다.

import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useAuthContext } from "./supabaseProvider";

const useAuth = () => {
  const { user, loading } = useAuthContext();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !user) {
     window.location.href = "/login";  // 인증되지 않은 사용자는 로그인 페이지로 리디렉션
    }
  }, [loading, user, router]);

  return { user, loading };
};

export default useAuth;

위에서 만든 useAuth 커스텀 훅을 마이페이지 컴포넌트에 아래와 같이 적용할 수 있다.

import useAuth from "./useAuth";
import Loading from "../_components/Loading/Loading";

const MyPage = () => {
  const { user, loading } = useAuth();

  if (loading) {
    return <Loading />;  // 로딩 중일 때 로딩 화면 출력
  }

  if (!user) {
    return null;  // 인증되지 않은 사용자는 렌더링하지 않음 (리디렉션 대기)
  }

  return <div>My Page Content</div>;  // 인증된 사용자는 페이지 내용 렌더링
};

export default MyPage;

HoC vs 커스텀 훅?

HoC을 이용하면,
원본 컴포넌트를 수정하지 않고 로직을 분리할 수 있어 모듈화에 유리하다. 그러나 여러 개의 HoC가 중첩될 경우 컴포넌트 구조가 복잡해져 디버깅이 어려워질 수 있다.


반면 커스텀 훅을 이용하면,
로직이 컴포넌트 내부로 들어와 직관적이고 가독성이 좋으며, 상태 관리나 부수 효과 처리가 쉽게 가능해진다. 그러나 커스텀 훅을 사용하는 컴포넌트마다 코드 중복 가능성이 생기게 된다.


나의 경우 인증 로직이 재사용되는 컴포넌트의 수가 많지 않았고 가독성을 중요하게 생각해서 커스텀 훅으로 변경했다. 그러나 로직을 여러 컴포넌트에서 재사용해야 한다면, HoC로 처리하는 것이 더 깔끔할 수 있을 것이다.

0개의 댓글