기본값이 뒤집혔어
Next.js 14에서는 서버 컴포넌트의 fetch()가 기본적으로 캐시됐어. 의도를 밝히지 않은 요청이 오래된 데이터를 보여 주는 문제가 많았고, 15부터는 명시적으로 선택하지 않은 요청을 캐시하지 않는 쪽으로 바뀌었어.
| 원하는 전략 | 설정 |
|---|---|
| 무효화할 때까지 캐시 | fetch(url, { cache: 'force-cache' }) |
| N초마다 재검증하는 ISR | fetch(url, { next: { revalidate: 60 } }) |
| 요청형 무효화를 위한 태그 | fetch(url, { next: { tags: ['posts'] } }) |
| 캐시하지 않음 | fetch(url) 또는 { cache: 'no-store' } |
| 라우트 구간의 기본 재검증 시간 | export const revalidate = 60 |
업그레이드 뒤 느려졌다면 의도를 복원해
14에서 올린 앱이 갑자기 느려졌다면 모든 요청을 다시 캐시하기부터 하지 마. 매 렌더마다 최신일 필요가 없는 데이터만 골라 force-cache나 revalidate를 붙여. 이전에는 묵시적으로 받던 캐시를 이제 요구사항에 맞게 선언하는 작업이야.
캐시는 정확성 계약이야
빠르게 만드는 옵션이기 전에, 사용자가 얼마나 오래된 값을 봐도 되는지를 정하는 계약이야. 캐시 여부와 수명을 코드 리뷰에서 설명할 수 없다면 아직 결정한 게 아니야.
각 fetch 옆에 신선도 문장을 적어 봐. “상품 설명은 한 시간 낡아도 된다”, “재고는 결제 직전에 다시 읽는다”처럼 제품 언어로 설명한 뒤 그 값을 revalidate와 no-store로 옮겨. 버전 업그레이드에서는 이전 캐시 동작에 우연히 기대던 라우트를 측정으로 찾아.
모든 요청을 force-cache로 되돌리면 성능은 회복돼 보여도 잘못된 값이 오래 남을 수 있어. 반대로 기본 no-cache를 그대로 두면 공개 콘텐츠까지 매 요청 계산해. 프레임워크의 기본값은 안전한 출발점이지 제품의 신선도 결정을 대신하는 답이 아니야. 시간이 표시되는 테스트 API를 fetch()하고 기본 호출, force-cache, no-store, revalidate의 연속 요청 결과를 비교해. 개발 모드가 아니라 production build에서 서버 로그와 응답 시간을 보고, 14에서 올린 라우트의 의도가 코드에 명시됐는지 확인해.