← 전체 글로 돌아가기

Next.js

Next.js에서 에러 페이지를 볼 때 먼저 확인할 로그

Next.js 프로덕션 서비스에서 에러 페이지가 보일 때 실제 에러가 뭔지 찾기 위해 로그를 읽고 원인을 파악하는 방법을 정리했다.

실제 서비스 중인 Next.js 사이트에서 갑자기 에러 페이지가 보인다면, 사용자는 "뭐가 잘못된 거야?"라고 알려주지만, 개발자로서는 어디를 봐야 할지 모를 수 있다. 로그를 체계적으로 찾아보면 대부분의 문제는 빠르게 해결된다.

서버 로그 확인

Next.js 서버가 뭘 실행하고 있는지, 에러가 뭔지 봐야 한다:

# 로컬에서 프로덕션 빌드 후 실행
rm -rf .next
npm run build
NODE_ENV=production npm start

# 콘솔 출력을 주의 깊게 본다
# 에러가 있으면 스택 트레이스가 나타난다

Docker 컨테이너 환경

# 컨테이너의 로그 확인
docker logs <container_id>

# 로그를 계속 추적
docker logs -f <container_id>

# 로그를 파일로 저장
docker logs <container_id> > logs.txt

PM2로 실행 중인 경우

# 현재 실행 중인 프로세스 확인
pm2 list

# 로그 확인
pm2 logs
pm2 logs --lines 100  # 최근 100줄
pm2 logs --err  # 에러 로그만

# 특정 앱의 로그
pm2 logs my-app

시스템 저널(journalctl)

# systemd로 관리되는 경우
sudo journalctl -u nextjs -n 100  # 최근 100줄
sudo journalctl -u nextjs -f      # 실시간 추적

# 에러 레벨만
sudo journalctl -u nextjs -p err

에러 메시지 읽기

실제 에러가 나면 이렇게 보인다:

Error: Cannot find module 'sharp'
  at Function.Module._resolveFilename (internal/modules/loader.js:1093:17)
  at Function.Module._load (internal/modules/loader.js:949:21)

읽어야 할 부분:

  1. 에러의 첫 줄이 핵심 (여기서는 Cannot find module 'sharp')
  2. 어느 파일에서 발생했는가?
  3. 스택 트레이스는 어디에서 시작했는가?

빌드 타임 에러 vs 런타임 에러

빌드 타임 에러 (배포 시)

npm run build

# 출력:
# TypeError: Cannot read property 'slice' of undefined
#   at pages/api/users.js:25:15

# 이는 빌드 중 getStaticProps나 getServerSideProps에서 데이터를 못 가져왔다는 뜻

런타임 에러 (사용자가 페이지 접속 시)

# 서버 로그:
# Error: ReferenceError: window is not defined
#   at pages/blog/[id].js:10:5

# 이는 서버에서 클라이언트 API(window, document)를 사용했다는 뜻

흔한 에러와 해결책

1. "Cannot find module"

# 원인: 필수 패키지가 설치되지 않음
# 해결:
npm install sharp  # 또는 필요한 패키지
npm ci  # 정확한 버전으로 다시 설치
rm -rf node_modules && npm install  # 완전 재설치

2. "Port 3000 is already in use"

# 원인: 다른 프로세스가 포트를 사용 중
# 해결:
lsof -i :3000  # 포트 사용 프로세스 확인
kill -9 <PID>  # 강제 종료

# 또는 다른 포트로
PORT=3001 npm start

3. "window is not defined"

// 문제 코드
export default function Page() {
  const height = window.innerHeight;  // 서버에서 실행되므로 에러
  return <div>{height}</div>;
}

// 해결: useEffect 사용
import { useEffect, useState } from 'react';

export default function Page() {
  const [height, setHeight] = useState(0);

  useEffect(() => {
    setHeight(window.innerHeight);
  }, []);

  return <div>{height}</div>;
}

4. "getServerSideProps 실패"

// 문제: API 호출 실패
export async function getServerSideProps() {
  const data = await fetch('https://api.example.com/data');
  return { props: { data } };
}

// 해결: 에러 처리
export async function getServerSideProps() {
  try {
    const res = await fetch('https://api.example.com/data');
    if (!res.ok) throw new Error(`API returned ${res.status}`);
    const data = await res.json();
    return { props: { data } };
  } catch (error) {
    console.error('getServerSideProps error:', error);
    return { notFound: true };  // 404 페이지 표시
  }
}

에러 페이지 커스터마이징

기본 에러 페이지

// pages/500.js
export default function Error() {
  return (
    <div>
      <h1>500 - 서버 에러가 발생했습니다</h1>
      <p>관리자에게 문의해주세요.</p>
    </div>
  );
}

getStaticProps 실패 처리

// pages/products/[id].js
export async function getStaticProps({ params }) {
  try {
    const product = await fetchProduct(params.id);
    if (!product) return { notFound: true };
    return { props: { product }, revalidate: 60 };
  } catch (error) {
    console.error('getStaticProps error:', error);
    // 빌드는 계속 진행하되, 해당 페이지는 나중에 생성
    return { revalidate: 10 };
  }
}

외부 서비스 로그

Vercel 배포

# Vercel 대시보드에서 로그 확인
# 1. Project → Deployments
# 2. 최근 배포 클릭
# 3. "Functions" 탭에서 서버 로그 확인

# 또는 CLI에서
vercel logs production

클라우드 로깅 서비스

# Google Cloud Logging
gcloud logging read "resource.type=cloud_run_revision" --limit 50 --format json

# AWS CloudWatch
aws logs tail /aws/lambda/nextjs-api --follow

에러 메시지 로깅 개선

// lib/errorLogger.js
export function logError(error, context = {}) {
  const timestamp = new Date().toISOString();
  const message = `[${timestamp}] ${error.message}`;
  const stackTrace = error.stack;

  console.error(message, stackTrace, context);

  // 선택적: 외부 서비스로 전송 (Sentry, LogRocket 등)
  // sentryClient.captureException(error, { extra: context });
}

// pages/api/data.js
try {
  const data = await fetchData();
  return res.json(data);
} catch (error) {
  logError(error, { endpoint: '/api/data', method: req.method });
  return res.status(500).json({ error: 'Internal Server Error' });
}

디버깅 팁

# 1. NODE_ENV 확인
echo $NODE_ENV

# 2. 빌드 아티팩트 확인
ls -la .next/

# 3. 환경 변수 확인
env | grep -E 'NODE|PORT|DATABASE'

# 4. 프로세스 상태 확인
ps aux | grep node

# 5. 디스크 공간 확인
df -h

Next.js 에러는 보통 명확한 에러 메시지를 주므로, 로그를 집중해서 읽으면 원인을 빠르게 찾을 수 있다. 배포 후 처음 몇 분은 로그를 주시하는 습관이 좋다.