ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Bug Fix] Next.js 14 App Router 버전, 동적 경로에서 데이터가 없는 경우 404 페이지로 redirect 시키기
    Front-end 개발 2024. 11. 23. 21:58

     

    Problem


    기본적으로 존재하지 않는 경로는 자동으로 app 폴더 밑에 있는 not-found.tsx 파일인 404 페이지로 redirect 된다.
    그런데 Next.js 14 App Router 버전에서 동적 경로를 사용하는 Catch All Segment를 사용하는 경우 하위의 모든 경로를 포함하기 때문에 라우팅에서 편리 하지만 데이터가 존재하지 않는 페이지도 보여지게 된다.

     

    Solution


    notFound 활용하기

    기존적인 해법으로 직접 코드 상에서 특정 조건에서 notFound 메서드를 호출하면 된다.

    내 경우에는 데이터 페칭 결과값이 없을 때, 서버 액션에서 에러를 반환하는 것이 아니라 undefined를 반환하도록 하고, 데이터 페칭의 결과가 존재하지 않을 경우 notFound 를 호출하는 코드로 작성했었다.

    import { notFound } from 'next/navigation';
    ...
    export default async function OrderPage({ params }: { params: { model: string } }) {
      const modelData = await fetchProduct(params.model);
      if (!modelData) {
        notFound();
      }
      ...

     

    dynamicParams 활용하기

    Next.js App Router 버전에서 Route Segment Option이라는 기능을 제공한다. Route Segment Option이란 약속된 이름의 변수를 내보냄(export)으로써 특정 페이지의 동작을 강제로 설정해주는 기능이다.

    Next.js App Router 버전에서는 컴포넌트를 Client component와 Server component로 구분하고, 다시 Server component는 빌드 타임에 미리 만들어지는지 여부에 따라서 Dynamic page와 Static Page로 구분된다. 그리고 Static Page로 만들 경우 Next 서버측에서 빌드 타임에 특정 페이지의 렌더링 결과를 캐싱하는 Full Route Cache를 사용하여 굉장히 빠른 속도로 접속 요청을 처리하게 된다.

    동적 경로를 사용하는 페이지에도 Full Route Cache를 적용하기 위해, 페이지를 Static Page로 만들어줄 수 있는 generateStaticParams를 사용하여 빌드타임에 사용할 특정 params을 지정해주는 방법을 사용할 수 있다. 이때 generateStaticParams에 정적으로 설정해둔 URL 파라미터 외에는 전부 404 페이지로 보내고 싶은 경우에는 약속된 변수명인 dynmaicParams를 설정해서 내보내주면 된다. 그러면 자동으로 Next 서버가 빌드 타임에 해당 페이지를 생성할 때, dynamicParams 변수의 값이 false인 경우 generateStaticParams 함수에 등록된 것 외에는 URL 파라미터가 존재하지 않아야 한다고 해석하게 된다.

    결국 사용자가 generateStaticParams 함수에 명시하지 않은 URL 파라미터 값을 요청하게 되면 dynamicParams = false 설정에 의해서 바로 404 페이지로 redirect 된다.

    export const dynamicParams = false; // 지정한 URL 파라미터 외에는 notFound()
    
    // DB에 등록된 모든 차량의 URL 파라미터를 설정
    export async function generateStaticParams() {
      const vehicles = await fetchVehicles();
      return vehicles.map(({ _id, name }) => {
        if (name !== 'neolun-concept') {
          return { model: _id.toString() };
        }
      });
    }
Designed by Tistory.