| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- observability
- Infra
- Kubernetes
- reliability
- frontend
- 버전관리
- database
- JavaScript
- HTTP
- backend
- architecture
- Debugging
- Security
- CSS
- Ops
- auth
- version-control
- react
- web
- Operations
- SRE
- DevOps
- CI
- NextJS
- API
- aws
- Git
- Microservices
- 성능
- Performance
- Today
- Total
고민보단 실천을
Next.js App Router 캐싱 정리: fetch cache, revalidate, no-store로 데이터 최신화하기 본문
Next.js App Router 캐싱 정리: fetch cache, revalidate, no-store로 데이터 최신화하기
Just-Do-It 2026. 2. 28. 14:59Next.js App Router 캐싱 정리: fetch cache, revalidate, no-store로 데이터 최신화하기
Next.js App Router에서 가장 많이 검색되는 문제는 이겁니다: 배포했는데 데이터가 안 바뀐다. 원인은 대개 캐싱/재검증(revalidate) 설정이 의도와 다르게 걸려서입니다. 이 글은 App Router 기준으로 fetch 캐싱을 실무 관점에서 정리합니다.
핵심 개념 3개
App Router에서 데이터 최신화는 보통 3가지 축으로 제어합니다: 1) fetch 캐시 정책 2) revalidate 재검증 간격 3) 라우트/세그먼트 단위의 동적 렌더링 강제입니다. 여기서 가장 영향력이 큰 건 fetch 옵션입니다.
옵션/핵심 요소(3~6개)
| 항목 | 의미 | 언제 쓰는지(실무 상황) |
|---|---|---|
next: { revalidate: N } | N초마다 재검증 | 뉴스/피드처럼 완전 실시간은 아니지만 자주 바뀌는 데이터 |
cache: 'no-store' | 항상 최신 요청 | 사용자별 데이터, 결제/권한 등 캐시하면 사고 나는 데이터 |
revalidateTag/revalidatePath | 원하는 시점에 무효화 | CMS 발행/관리자 업데이트 후 즉시 반영해야 할 때 |
| Dynamic 렌더링 | 정적 최적화 방지 | 요청마다 달라져야 하는 페이지(세션/쿠키 기반) |
| CDN 캐시 | 엣지/프록시 캐시 영향 | 서버 코드는 맞는데 사용자에겐 여전히 옛 데이터가 보일 때 |
실전 예시: 목록은 60초 캐시, 상세는 즉시 최신
목록 페이지는 60초 단위로만 갱신해도 충분한 경우가 많고, 상세 페이지는 수정 직후 바로 최신이 보여야 하는 경우가 많습니다. 이때 fetch 단위로 정책을 분리합니다.
// app/posts/page.tsx (Server Component)
const res = await fetch('https://api.example.com/posts', { next: { revalidate: 60 } })
const posts = await res.json()
// app/posts/[id]/page.tsx (Server Component)
const res2 = await fetch(`https://api.example.com/posts/${params.id}`, { cache: 'no-store' })
const post = await res2.json()문제 상황(정확히 1개)
상황: 관리자에서 글을 수정했는데, Next.js 페이지에는 몇 분 동안 예전 내용이 계속 보인다.
원인: 목록/상세 중 어딘가의 fetch가 의도치 않게 캐시되고 있고, 무효화(revalidate)가 발생하지 않는다. 특히 기본값을 '항상 최신'으로 착각하는 경우가 많다.
해결: 즉시 최신이 필요한 요청은 cache: 'no-store'로 고정하고, 캐시가 필요한 요청은 next: { revalidate: N }로 명시한다. 업데이트 직후 반영이 필요하면 태그/경로 기반 무효화(revalidateTag/revalidatePath)를 붙인다.
예방 팁: 페이지 단위가 아니라 데이터 요청(fetch) 단위로 캐시 정책을 문서화하고, '이 데이터는 캐시 가능/불가'를 팀 규칙으로 정한다.
