Front-end/Nextjs (12버전)

Protected Routes & useRouter exception

biglol 2022. 11. 27. 17:21

1. Protected Routes 설정


보통 토큰값을 쿠키에 저장할텐데 해당 쿠키값이 없을 경우 다른 페이지로 이동하게 하고싶으면 어떻게 할까요?

단순한 방법으론 페이지의 useEffect안에 쿠키값이 있는지 확인하고 redirect하면 될 것 같습니다.

정말 간단하지만 해당 방법으론 매 페이지마다 해당 로직을 적어야 하며 소스가 더러워 질 것입니다.

Nextjs에선 middleware를 통해 이를 해결할 수 있습니다.

 

깃헙 링크 공유드립니다: https://github.com/biglol10/tistory_source/tree/main/route-example

 

우선 프로젝트 루트 폴더에 middleware.js를 생성해줍니다.

 

  • middleware.js 파일 소스
import { NextResponse } from "next/server";

const middleware = (request) => {
  const cookieValue = request.cookies.get("token");
  const pagePath = request.nextUrl.pathname;

  // Protected routes 목록
  const protectedRoutesArray = ["/protected", "/dashboard"];

  // Proctected routes로 진입하는지 확인
  const isWithProtectedRoutes = () => {
    return protectedRoutesArray.some((route) => pagePath.startsWith(route));
  };

  // 쿠키값이 없으며 protected routes로 진입할 경우 로그인 페이지로 이동
  if (!cookieValue && isWithProtectedRoutes()) {
    return NextResponse.redirect(
      `http://localhost:3000/${
        process.env.NODE_ENV === "development" ? "develop" : "production"
      }/login`
    );
  }
};

export default middleware;

매 페이지 진입 시 middleware를 거치게 되며 해당 middleware에선 쿠키값이 있는지 확인 후 로그인 페이지로 이동시킬지 아니면 사용자가 원하는 페이지에 진입시킬지 결정합니다.

 

샘플 프로젝트를 살펴보겠습니다.

쿠키값이 없는데 [Go to Protected page] 버튼을 클릭하면 로그인 페이지로 이동합니다.

 

임의로 쿠키값을 생성 후 버튼을 클릭하면 원하는 페이지로 이동되는 것을 확인할 수 있습니다.

 

 


 

2. useRouter 관련 이상한 점 (Nextjs 12)


http://localhost:3000/dev/page/myvariable 에서 myvariable값을 가져오고 싶은 경우처럼 url에서 path variable을 가져와 로직을 작성해야 하는 경우가 있을겁니다.

일반적으로 useRouter을 이용해 router.query로 값을 가져올 수 있습니다.

다만 Next 12의 경우 특정 상황에선 이 값을 못가져오는 경우가 있습니다.

확인해보니 Next 13에선 잘 가져옵니다!!

그 특정 상황은 바로 next.config.js에서 basePath를 설정했을 때입니다.

 

  • next.config.js 파일 소스
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
};

module.exports = {
  ...nextConfig,
  basePath: process.env.NODE_ENV === "development" ? "/develop" : "/production",
  async redirects() {
    return [
      {
        source: "/",
        destination: `/${
          process.env.NODE_ENV === "development" ? "develop" : "production"
        }`,
        basePath: false,
        permanent: false,
      },
    ];
  },
};

이처럼 개발모드면 develop를 운영모드면 production을 url에 붙힙니다.

 

이후 샘플 페이지 하나 생성합니다.

  • routerCareful/[value].jsx
import { useRouter } from "next/router";
import { Layout1 } from "@components/index";
import { useEffect } from "react";

const RouterCareful = ({ queryObj = {} }) => {
  const router = useRouter();

  const { value } = router.query;

  useEffect(() => {
    console.log(`useRouter value is ${value}`);
    console.log(`getServerSideProps value is ${queryObj.value}`);
  }, []);

  return (
    <>
      <div style={{ borderColor: "red" }}>
        This is the value from dynamic route = {value}
      </div>

      <br />

      <div style={{ borderColor: "blue" }}>
        This is the value from ServerSideRendering = {queryObj.value}
      </div>
    </>
  );
};

RouterCareful.Layout = Layout1;

export const getServerSideProps = async (context) => {
  return { props: { queryObj: context.query } };
};

export default RouterCareful;

 

위의 사진처럼 input에 qwer을 입력 후 [Go to dynamic route page]버튼을 클릭 시 useRouter로 path variable을 못 가져오고 undefined가 뜨는 것을 확인할 수 있습니다.

새로고침하면 나오지만 페이지에서 api request를 날리고 다른 작업을 하는 것이 있을 경우 네트워크 요청도 이뤄지고 전역상태관리에 대한 신경도 써야 한다는 단점이 있습니다.

 

Nextjs docs를 보시면

https://nextjs.org/docs/api-reference/next/router#userouter

서버사이드 랜더링을 사용하지 않을 시 {} 값으로 된다고 되어있는데 basePath를 추가했다고 server-side rendering을 비활성화 시키는 것이 아니기에 그리고 Next 13에선 고쳐진 것을 보아 일종의 버그였던 것으로 보입니다.

 

Chatgpt의 답변

Pre-rendering(Next.js 가 각 페이지에 대한 HTML 을 생성하는 것)할 때 query라는 객체는 아무런 값이 없는 상태로 client에 전달될 것이라 값은 가져올 수 없는 상태입니다. CSR상태일 땐 query 객체에 값이 있어 이를 이용할 수 있습니다.

 

따라서 Next 12에서 해당 query객체를 다룰 때 소스에 적혀있는 것처럼 getServerSideProps를 이용해 router.query값을 가져와야 합니다.