Next.js
Next.js 배포할 때 캐시로 인한 문제를 피하는 방법
Next.js 애플리케이션 배포 후 이전 버전의 캐시가 남아서 구버전이 계속 보이는 문제를 디버깅하고 해결하는 방법을 다룬다.
Next.js를 배포했는데 사용자가 여전히 구버전을 본다는 신고가 들어오면, 보통 캐시 문제다. 클라이언트의 브라우저 캐시, CDN 캐시, 서버의 정적 파일 캐시가 모두 다르게 작동하기 때문이다.
로컬에서 먼저 확인한다
배포 전에 로컬에서 빌드 결과를 확인하자:
# 1. 캐시 정리
rm -rf .next
# 2. 빌드
npm run build
# 3. 빌드 결과 확인
ls -la .next/static
# 4. 파일명 해시 확인
ls .next/static/chunks/
# 파일명에 해시가 포함되는가? (예: main.a1b2c3d4.js)
Next.js는 기본적으로 프로덕션 빌드 시 파일명에 컨텐츠 해시를 붙인다. 파일 내용이 변경되면 파일명도 변경되므로 캐시 무효화가 된다.
배포 환경에서의 캐시 설정 확인
응답 헤더 확인
# 정적 파일의 캐시 설정 확인
curl -i 'https://yoursite.com/_next/static/chunks/main.abc123.js' | grep -E '(Cache-Control|ETag|Last-Modified)'
# 예상되는 응답 헤더:
# Cache-Control: public, max-age=31536000, immutable
# (1년 캐시, 내용이 절대 변경되지 않음)
# HTML 페이지의 캐시 설정 확인
curl -i 'https://yoursite.com/' | grep -E '(Cache-Control|ETag)'
# 예상되는 응답 헤더:
# Cache-Control: public, max-age=0, must-revalidate
# (캐시하지 않거나 매번 검증)
만약 정적 파일이 캐시되지 않는다면, 사용자가 매번 새로 다운로드해야 해서 느려진다.
next.config.js에서 캐시 설정
// next.config.js
module.exports = {
// 정적 파일 캐시 최대 나이 설정 (기본값: 1년)
staticPageGenerationTimeout: 60,
// ISR(Incremental Static Regeneration) 캐시
isr: {
maxConcurrentPrevalidates: 2,
},
// Vercel 배포 시
outputFileTracing: true,
};
브라우저 개발자 도구에서 캐시 확인
# 또는 브라우저의 개발자 도구에서:
# 1. Network 탭 열기
# 2. JS 파일 클릭
# 3. Headers 탭에서 Cache-Control 확인
# 4. 304 Not Modified는 캐시 사용 중이라는 뜻
구버전이 계속 보인다면:
- Disable cache 체크박스를 켜고 새로고침
- Ctrl+Shift+Delete (또는 Cmd+Shift+Delete)로 캐시 완전 삭제
CDN 캐시 무효화 (Vercel, Netlify, Cloudflare 등)
Vercel
# 배포 후 자동으로 캐시 무효화됨
# 하지만 수동으로 하려면:
vercel env ls # 환경 확인
vercel deploy --prod # 강제 배포
Netlify
# 배포 후 캐시 완전 무효화
netlify deploy --prod --trigger
# 또는 대시보드에서
# Site settings → Build & deploy → Deploy contexts → Production branch → Clear deploy cache
Cloudflare
# 캐시 정리
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
-H "Authorization: Bearer <token>" \
-d '{"files":["https://yoursite.com/*"]}'
# 또는 대시보드에서
# Caching → Purge Cache → Purge Everything
로컬 테스트: 프로덕션 빌드 시뮬레이션
# 1. 빌드
npm run build
# 2. 프로덕션 모드에서 실행
NODE_ENV=production npm start
# 3. 브라우저에서 접속
# http://localhost:3000
# 4. 캐시 헤더 확인
curl -i http://localhost:3000/
배포 전 체크리스트
# 1. 빌드가 성공하는가?
npm run build
# 2. 빌드 에러가 없는가? (모든 페이지가 생성됨)
ls .next/server/pages/
# 3. 정적 파일이 생성되는가?
ls .next/static/chunks/
# 4. 파일명에 해시가 있는가?
ls .next/static/chunks/ | grep -E '[a-f0-9]{8,}\.js'
# 5. .next 폴더를 배포 전에 정리했는가?
rm -rf .next && npm run build
이미 배포된 경우의 응급 조치
# 1. 사용자의 캐시 정리
# -> 사용자에게 Ctrl+Shift+Delete 하도록 안내
# 2. 서버의 캐시 정리
rm -rf .next
npm run build
# 재배포
# 3. CDN 캐시 정리
# Cloudflare/Netlify 대시보드에서 Purge Cache
# 4. 소프트 배포 (클라이언트 코드 변경 없음)
# -> 필요시 서버 재시작
특정 페이지만 캐시 무효화
// pages/api/revalidate.js
export default async function handler(req, res) {
// 토큰 검증
if (req.query.secret !== process.env.REVALIDATE_TOKEN) {
return res.status(401).json({ message: 'Invalid token' });
}
// 특정 페이지 재생성
await res.revalidate('/blog/my-post');
return res.json({ revalidated: true });
}
그 후 배포 후 수동으로 호출:
curl 'https://yoursite.com/api/revalidate?secret=<token>'
환경 변수와 빌드 시간 데이터
빌드 시점의 환경 변수가 하드코딩되는 경우도 있다:
// pages/index.js
// 이 코드는 빌드 시점에 API_URL이 고정된다
const API_URL = process.env.API_URL;
export default function Home() {
return <p>API: {API_URL}</p>;
}
배포 환경마다 다르려면 클라이언트 사이드에서 읽어야 한다:
// next.config.js
module.exports = {
publicRuntimeConfig: {
apiUrl: process.env.API_URL,
},
};
// pages/index.js
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
export default function Home() {
return <p>API: {publicRuntimeConfig.apiUrl}</p>;
}
Next.js의 캐시 문제는 파일명 해시, 응답 헤더, CDN 설정이 모두 조화를 이루어야 해결된다. 배포 전에 로컬에서 충분히 테스트하고, 배포 후에는 개발자 도구에서 캐시 상태를 확인하는 습관이 중요하다.