← 전체 글로 돌아가기

Next.js

Next.js 빌드에서 페이지가 누락된 경험

Next.js 빌드 후 특정 페이지가 없다고 하면, 빌드 로그를 먼저 자세히 확인해야 한다. 빌드 경고나 에러가 숨어 있을 수 있다.

Next.js로 블로그를 만들었는데, 빌드는 성공했다고 하는데 특정 페이지가 없다고 한다. 이런 상황은 보통 빌드 로그에 힌트가 있다.

빌드 로그를 전체로 보기

먼저 빌드 로그를 끝까지 본다.

# 빌드 실행
npm run build 2>&1 | tee build.log

# 로그 파일 검토
cat build.log | grep -E 'warning|error|skipped'

# 특정 페이지의 빌드 결과만 보기
grep "pages/blog" build.log

Next.js는 대부분의 경고를 화면에 출력하지 않는다. 로그 파일을 저장해서 봐야 한다.

페이지 파일이 실제로 있는지 확인하기

# 소스 파일 확인
find src/pages -name "*.tsx" | sort
find src/app -name "*.tsx" | sort

# 빌드된 파일 확인
ls -la .next/server/pages/
ls -la .next/static/

# 특정 페이지가 없다면?
ls -la .next/server/pages/blog/

빌드 후 .next 디렉토리에 파일이 없다면, 빌드 중에 건너뛰어졌다는 뜻이다.

getStaticProps 에러 확인하기

Next.js 스태틱 제너레이션에서 에러가 나면 그 페이지는 빌드되지 않을 수 있다.

# pages/blog/[slug].tsx
export async function getStaticPaths() {
  const posts = await fetchPosts();
  return {
    paths: posts.map(post => ({
      params: { slug: post.slug }
    })),
    fallback: false // 빌드되지 않은 경로는 404
  };
}

// fetchPosts에서 에러가 나면 빌드가 실패한다

데이터 소스(API, 데이터베이스)가 빌드 시간에 접근 불가능하면 페이지 생성이 실패한다.

ISR 또는 동적 렌더링으로 바꾸기

getStaticProps로 빌드 시간에 모든 데이터를 fetch하는 대신, ISR이나 동적 렌더링을 쓸 수 있다.

// getStaticProps 대신 getServerSideProps (매 요청마다 렌더링)
export async function getServerSideProps(context) {
  const post = await fetchPost(context.params.slug);
  return {
    props: { post },
    revalidate: 3600 // 1시간마다 재검증
  };
}

// 또는 App Router의 동적 렌더링
export default async function Page({ params }) {
  const post = await fetchPost(params.slug);
  return <Article post={post} />;
}

이렇게 하면 빌드 중에 모든 페이지를 미리 생성할 필요가 없다.

빌드 로그에서 에러 찾기

# 특정 문자열 검색
grep -i "failed\|error\|cannot" build.log

# 또는 less로 대화형 검색
less build.log
# 다음 'failed' 검색: /failed

# 에러 주변 문맥까지 보기
grep -B 2 -A 2 "Error:" build.log

환경 변수 확인하기

빌드 중에 필요한 환경 변수가 없으면 에러가 날 수도 있다.

# 빌드 시 환경 변수 확인
echo $API_URL
echo $DATABASE_URL

# .env.local에 있는가?
cat .env.local | grep API

# 빌드 시간에 필요한 변수를 .env.local에 추가
echo "API_URL=https://api.example.com" >> .env.local
npm run build

동적 라우트 경로 확인하기

동적 라우트[slug]로 페이지를 만들었을 때, getStaticPaths에서 반환한 경로만 빌드된다.

// 만약 getStaticPaths에서 이것을 반환했다면
return {
  paths: [
    { params: { slug: 'first-post' } },
    { params: { slug: 'second-post' } },
  ],
  fallback: false // 다른 경로는 404
};

// 그러면 'third-post' 페이지는 빌드되지 않는다

블로그 글이 늘어날 때마다 getStaticPaths를 수동으로 업데이트해야 하는데, 이걸 빠뜨릴 수 있다.

빌드 캐시 삭제하고 다시

.next 디렉토리의 캐시 때문에 실제로 다시 빌드되지 않을 수도 있다.

# 캐시 삭제
rm -rf .next

# 다시 빌드
npm run build

# 빌드 로그 확인

앞으로의 예방법

  1. 빌드 로그를 항상 전체로 저장하고 검토하기
  2. getStaticPaths에서 에러 처리 추가
  3. 데이터 소스가 빌드 시간에 접근 가능한지 확인
  4. 필요시 ISR이나 동적 렌더링으로 전환
  5. 새로운 페이지를 추가했을 때 getStaticPaths도 업데이트했는지 확인