[카테고리:] Next.js

  • Next.js 에서 loading 페이지나 스트리밍 기능 사용 시 404 처리가 불가능합니다

    현재 Next.js 15.1.7 버전으로 작업 중인데요. dynamic route로 API 호출해 처리하는 페이지가 있고 페이지 호출 시 id 값으로 API 호출하는데 자료가 없는 경우 HTTP STATUS로 404 리턴을 해줘야 합니다.

    일반적으로는 아래와 같은 코드로 작동할 수 있습니다.
    예를 들어 app/pages/[id]/page.tsx 파일을 아래와 같이 작성합니다.

    import { notFound } from 'next/navigation';
    
    export default async function Page(props: {
      params: Promise<{ id: number }>;
    }) {
      const pparams = await props.params;
      const id = Number(pparams.id);
      const data = await fetch(API_URI, { localdata_id });
      if (!data) {
        notFound();
      }
      return (<div>{JSON.stringify(data)}</div>)
    }
    

    /pages/112 이런 형태로 호출하면 정상적으로 200 리턴하며 페이지를 출력할 것입니다.

    만약 999999 란 아이디가 없다면 /pages/999999 호출 시 notFound() 가 호출됩니다.

    notFound() 의 return 형식은 never 입니다. 절대로 리턴이 없다는 의미이며 내부적으로 예외를 발생시키는 것으로 알고 있습니다.

    결국 Next.js 에서 처리해서 정상적으로 404 리턴을 해줍니다.

    문제점

    하지만 아래와 같이 loading.tsx 를 넣어서 로딩 UI 나 스트리밍 처리를 넣게 되면 무조건 200 리턴만 가능합니다.

    app/loading.tsx 를 아래와 같이 넣는다면…

    'use client';
    import CircularProgress from '@mui/material/CircularProgress';
    import { useEffect, useState } from 'react';
    export default function Loading() {
      const [showLoading, setShowLoading] = useState(false);
      useEffect(() => {
        const timer = setTimeout(() => {
          setShowLoading(true);
        }, 500); // 0.5초 후에 로딩 페이지를 보여줌
        return () => clearTimeout(timer); // 컴포넌트가 unmount 될 때 타이머 정리
      }, []);
      // 0.5초 이전에는 아무것도 렌더링 하지 않음
      if (!showLoading) {
        return null;
      }
      // You can add any UI inside Loading, including a Skeleton.
      return (
        <div className="page-content w-desktop flex flex-col items-center justify-center">
          <CircularProgress />
          <div>Loading...</div>
        </div>
      );
    }
    

    Next.js는 loading.tsx 파일 있는 경우 페이지 스트리밍 처리를 자동으로 시작하므로 무조건 200 리턴을 하고 페이지 로딩을 이어가기 때문에 중간에 notFound() 호출은 이미 버스가 떠난 뒤에 손을 흔드는 것과 같습니다.

    따라서 loading UI 를 사용하면서도 404 처리를 하려면 middleware를 활용하는 수밖에 없습니다. 문제는 이때 미들웨어에서 먼저 API 를 fetch() 한 결과를 React.cache()로 캐싱 처리해도 React.cache() 는 서버 컴포넌트 내에서만 캐시 처리가 가능하기 때문에 Next.js 에서 제공하는 미들웨어서는 캐싱이 작동하지 않아 페이지 한 번 호출에 여러번 API 를 호출하게 됩니다. fetch() 옵션으로 캐싱을 넣을 수도 있지만 미들웨어에서 호출하는 것과 서버 컴포넌트에서 호출하는 것이 실제로는 헤더부분이 달라 다른 것으로 인식해 간단히 캐싱 처리가 되지 않습니다.

    결국 Next.js 가 제공하는 기능인 loading.tsx를 제거하고 로딩 처리는 자체적으로 처리하는 형태로 변경해야 정상적으로 notFound() 가 작동해 404 처리가 가능해집니다.

    참고자료