Next.js

[NEXT] HOC를 이용하여 next.js router guard 만들기

uni_i 2022. 9. 4. 15:07

vue를 사용할 때는 vue-router에서 제공하는 navigation guard 기능이 있었기 때문에,

각 페이지에 접근을 할 때 guard 처리해주기가 수월했다. 

하지만, next.js에서는 그러한 기능이 따로 없기 때문에 직접 만들어야 했다. 

그래서, hoc를 통해 next.js router guard(navigation guard)를 만들어보고자 한다.


 

1) 목적

각 페이지에 접근을 할 때, 각 페이지별 접근을 할 수 있는 유저가 있고, 접근이 불가능한 유저가 있을 것이다.

예를 들어, 로그인 페이지 같은 경우 로그인을 안한 유저는 접근이 가능하지만, 이미 로그인 한 유저라면 접근이 불가능하게 만들어야 한다.

이러한 접근에 대한 검사를 매 페이지마다 이루어져야 하는데, 각 페이지 컴포넌트에 이 설정을 셋팅을 하는 것은 번거로운 일이다.

그래서, hoc를 이용하여 페이지 컴포넌트를 보여주기 전, 유저에 대한 접근 권한을 판별하는 전용하는 hoc를 만들어 보고자 한다.

 

 

 

 

2) 간략 요약

  1. hoc 컴포넌트를 통해, 유저의 권한 판별
  2. 각 페이지의 컴포넌트에 hoc 감싸주기

 

 

 

3) 구체적인 과정

[0] 사전 지식

- hoc 

혹시 hoc에 대한 개념이 부족하다면, 아래 글을 참조 부탁드립니다.

https://uni-s-code.tistory.com/24

 

[React] HOC(high order component)의 개념과 사용법

오늘은 react의 hoc 패턴에 대해 알아보겠습니다. 1) hoc란? hoc는 high order component의 약자로써, 고차컴포넌트라고 합니다. 쉽게 말하면, 다른 컴포넌트(=자식)를 감싸는 wrapper 컴포넌트(=부모)라는 뜻

uni-s-code.tistory.com

 

 

- firebase를 통한 authentication 구현(추후 블로그에 작성하겠다.)

 

 

[1] hoc 폴더 안에 guard 파일 생성

hoc 컴포넌트를 만들때에는 with~ 컴포넌트 네이밍을 지키도록 하자.

 

 

[2] hoc 컴포넌트 코드 

import Router from 'next/router';
import { useEffect, useState } from 'react';
import { NO_USER_GUARD, USER_GUARD, PUBLIC_GUARD } from 'data/guardTypes';
import { getAuth, onAuthStateChanged } from 'firebase/auth';

const withAuth = (WrappedComponent, guard, redirect = null) => {
  const RouteGuard = (props) => {
    const [authLoading, setAuthLoading] = useState(true);
    const [authUser, setAuthUser] = useState(null);
    useEffect(() => {
      if (user?.userId) {
        console.log(user);
        setRoleLoading(false);
      }
    }, [user]);
    useEffect(() => {
      const auth = getAuth();
      onAuthStateChanged(auth, async (user) => {
        setAuthUser(user);
        setAuthLoading(false);
      });
    }, []);

    const checkVerification = (guard, authUser) => {
      switch (guard) {
        case NO_USER_GUARD:
          return !authUser;
        case USER_GUARD:
          return authUser;
        case PUBLIC_GUARD:
          return true;
        default:
          return true;
      }
    };

    if (typeof window == 'undefined') {
      return null;
    }

    if(authLoading){
     if (checkVerification(guard, authUser)) {
        console.log(`route - ${guard}: pass`);
        return <WrappedComponent {...props} />;
     } else {
        console.log(`route - ${guard}: not pass`);
        {
          redirect ? Router.push(redirect) : Router.push('/');
        }
     }
    }
  };

  RouteGuard.displayName = 'routeGuard';
  return RouteGuard;
};

export default withAuth;

 

 

[3] guard type 정의

원하는 위치에 guard에 대한 type을 정의하자.

export const NO_USER_GUARD = 'noUserGuard';
export const USER_GUARD = 'userGuard';
export const PUBLIC_GUARD = 'publicGuard';

 

 

[4] 페이지별 hoc 적용

 

 user만 들어올 수 있는 페이지 (ex- 마이페이지, 프로필)

import type { NextPage } from "next";
import withAuth from 'hoc/withRouteGuard'; // HOC import
import { USER_GUARD } from 'data/guardTypes'; // guard type import


const OnlyUser: NextPage = () => {
  return <div>this is only user page</div>;
};

export default withAuth(OnlyUser, USER_GUARD);

 

 

 user가 아닌 사람만 들어올 수 있는 페이지 (ex-회원가입, 로그인)

import type { NextPage } from "next";
import withAuth from 'hoc/withRouteGuard'; // HOC import
import { NO_USER_GUARD } from 'data/guardTypes'; // guard type import


const NoUser: NextPage = () => {
  return <div>this is no user page</div>;
};

export default withAuth(NoUser, NO_USER_GUARD);

 

 

 


참조 자료

https://github.com/vercel/next.js/discussions/11822

https://dev.to/shubhamverma/implement-protected-routes-in-nextjs-37ml