← 전체 글로 돌아가기

웹 개발

동적 라우트 배포할 때 확인할 사항들

Next.js나 다른 프레임워크의 동적 라우트가 프로덕션에서 404를 반환할 때 문제 해결 방법.

동적 라우트가 작동 안 할 때

"로컬에서는 /product/123이 잘 뜨는데 서버에선 404다"라는 신고가 오면, 보통 동적 라우트 설정이나 빌드 프로세스에서 뭔가 잘못되었다.

동적 라우트는 빌드할 때 미리 생성할 수도 있고(ISR), 요청 시 동적으로 생성할 수도 있는데, 각 방식에 문제가 생길 수 있다.

Next.js 동적 라우트 기본

// app/product/[id]/page.tsx
export default function ProductPage({ params }: { params: { id: string } }) {
  return <h1>상품 {params.id}</h1>;
}

// 이 파일은 다음 경로들을 처리
// /product/1
// /product/abc
// /product/123

빌드할 때 라우트가 생성되지 않음

ISR(Incremental Static Regeneration) 설정이 있으면 빌드 시 라우트 목록을 생성한다.

// app/product/[id]/page.tsx
export async function generateStaticParams() {
  const products = await getAllProducts();
  return products.map(product => ({
    id: product.id.toString(),
  }));
}

export const revalidate = 3600; // 1시간마다 갱신

export default function ProductPage({ params }: { params: { id: string } }) {
  return <h1>상품 {params.id}</h1>;
}

generateStaticParams가 없으면 모든 라우트를 미리 생성하지 못해서, 빌드 후 처음 요청하는 경로는 로드되지 않을 수 있다.

동적 라우트 테스트하기

npm run build

# 빌드 결과 확인
cat .next/routes-manifest.json | jq '.dynamicRoutes'

# 프로덕션 빌드 실행
npm run start

# 실제로 경로에 접근해보기
curl http://localhost:3000/product/123
curl http://localhost:3000/product/456

서버에서 라우트 처리 실패

만약 요청 시에 라우트를 동적으로 생성한다면, 서버에서 404를 반환할 수 있다.

// app/product/[id]/page.tsx
import { notFound } from 'next/navigation';

export default async function ProductPage({
  params
}: {
  params: { id: string }
}) {
  const product = await getProduct(params.id);

  if (!product) {
    notFound(); // 404 페이지 표시
  }

  return <h1>{product.name}</h1>;
}

만약 getProduct가 실패하면(DB 연결 오류, 타임아웃 등) 의도하지 않은 404가 나타날 수 있다.

서버 로그를 확인해서 실제 오류를 본다.

# Docker 로그 확인
docker logs my_app_container

# 또는 SSH로 접속해서 확인
ssh user@server
tail -100 /var/log/nextjs/error.log

리다이렉트 설정 문제

만약 라우트를 리다이렉트 설정했다면 그게 제대로 작동하는지 확인한다.

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/old-product/:id',
        destination: '/product/:id',
        permanent: true, // 301 리다이렉트
      },
    ];
  },
};

리다이렉트도 빌드할 때 설정되므로, next.config.js를 수정한 후 다시 빌드해야 한다.

동적 라우트 캐시 문제

ISR로 설정했으면 캐시 시간이 지나야 새 콘텐츠가 로드된다.

export const revalidate = 60; // 60초마다 갱신

// On-demand 갱신도 설정 가능
export async function POST(request: Request) {
  revalidateTag('product');
  return new Response(JSON.stringify({ revalidated: true }));
}

배포 전 체크리스트

  • 동적 라우트가 [id] 형식으로 정의되어 있는가?
  • ISR을 사용한다면 generateStaticParams가 구현되어 있는가?
  • 로컬에서 빌드 후 npm run start로 테스트했는가?
  • 여러 동적 경로(예: /product/1, /product/abc)를 테스트했는가?
  • 존재하지 않는 경로(예: /product/notfound)는 404를 반환하는가?
  • 리다이렉트 설정이 있다면 제대로 작동하는가?
  • 서버 로그에서 에러가 없는가?

이 항목들을 확인한 후 배포하면 동적 라우트 관련 404 에러를 대부분 방지할 수 있다.