Next.js App Router 캐싱 4계층 완전 정리
긴 글, 핵심만 먼저 — 이 글을 3줄로 정리해드려요.
Next.js App Router의 캐싱은 처음 보면 마법 같고, 조금 쓰다 보면 저주 같다. "왜 데이터가 안 바뀌지?"의 90%는 캐시 때문이다. 네 개 층을 분리해서 이해하면 대부분의 미스터리가 풀린다.
1. Request Memoization
같은 렌더링 패스 안에서 동일한 fetch를 여러 컴포넌트가 호출하면 실제 요청은 한 번만 나간다. 레이아웃과 페이지에서 같은 유저 정보를 각각 불러도 괜찮은 이유다. 이건 끄고 싶어도 끌 수 없고, 사실 끌 이유도 없다.
2. Data Cache
fetch 결과를 서버에 지속 저장하는 층이다. 문제의 주범이기도 하다.
// 매번 새로 가져오기
fetch(url, { cache: "no-store" });
// 60초마다 갱신
fetch(url, { next: { revalidate: 60 } });
Prisma처럼 fetch를 안 쓰는 데이터 소스는 이 캐시를 안 탄다. 대신 라우트 세그먼트의 export const revalidate가 페이지 단위로 같은 역할을 한다.
3. Full Route Cache
빌드 타임에 정적으로 렌더된 HTML/RSC 페이로드를 통째로 캐싱한다. 동적 API(cookies, headers)를 쓰는 순간 해당 라우트는 여기서 빠진다. revalidatePath를 호출하면 이 캐시가 무효화된다.
4. Router Cache
브라우저 메모리에 있는 클라이언트 캐시다. 뒤로 가기가 즉시 뜨는 이유이자, 서버에서 revalidate를 해도 화면이 안 바뀌는 것처럼 보이는 이유다. router.refresh()가 이 캐시를 날린다.
디버깅 순서
데이터가 안 바뀌면 아래 순서로 의심하자. ① Router Cache(새로고침으로 확인) ② Full Route Cache(revalidatePath 호출 여부) ③ Data Cache(fetch 옵션) ④ 진짜 데이터 문제. 경험상 ①에서 절반, ②에서 나머지 절반이 잡힌다.