네 가지 캐시: 요청, 데이터, 전체 경로와 라우터
각 캐시 계층의 상호 작용과 App Router 앱에서 오래된 데이터가 발생하는 위치를 파악하는 방법을 배웁니다.
네 가지 캐시: 요청, 데이터, 전체 경로와 라우터은(는) CoddyKit의 무료 Next.js 15 Fullstack (App Router + Server Actions) 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Next.js 15 Fullstack (App Router + Server Actions) 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Next.js 15 Fullstack (App Router + Server Actions) 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Four Caches, One Mental Model
Next.js 15 App Router has four distinct caching layers. Stale data almost always comes from one of them, so naming them precisely is the first step to debugging.
- Request Memoization — dedupes identical
fetchcalls within a single render pass. - Data Cache — persists
fetchresults across requests and deployments (server-side, durable). - Full Route Cache — stores the rendered HTML + RSC payload of statically-rendered routes at build time.
- Router Cache — an in-memory client-side cache of RSC payloads for visited routes.
They flow roughly client → server: Router Cache lives in the browser; the other three live on the server.
Layer 1: Request Memoization
Request Memoization is React's built-in deduplication of fetch during a single server render. If three components request the same URL with the same options, the network call fires once; the rest reuse the in-flight promise.
- Scope: a single render pass (one request). It is not shared across requests.
- Key: the
fetchURL + options. - Benefit: you can call
getUser()in the layout and the page without prop-drilling or refetching.
Only fetch is memoized automatically. For non-fetch work (DB clients, ORMs), wrap it in React's cache().
import { cache } from 'react';
import { db } from '@/lib/db';
// Without fetch, memoize manually so layout + page share one query
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
// Both calls in the same render hit the DB only once
async function Layout({ id }: { id: string }) {
const user = await getUser(id);
return user.name;
}Memoization Is Per-Render, Not Durable
A common misconception: people think Request Memoization persists data. It does not. The moment the render finishes, the memo cache is discarded.
- It exists only to avoid duplicate work within one render tree.
- The next incoming request starts with an empty memo cache.
- Durability across requests is the job of the Data Cache, not memoization.
So if you see fresh data on request A but the same render reuses it, that's memoization. If data survives a server restart, that's the Data Cache.
Layer 2: The Data Cache
The Data Cache persists fetch results on the server across requests, users, and even deployments (until revalidated). In Next.js 15 the default changed: fetch is now uncached (no-store) unless you opt in.
cache: 'force-cache'→ store and reuse the result.next: { revalidate: N }→ store, serve stale up to N seconds, then refresh.cache: 'no-store'→ never store; always hit the source.
This is the layer most responsible for “why is my data stale?” when you forgot it's cached.
// Next.js 15: opt INTO the Data Cache explicitly
async function getProducts() {
// Revalidate at most every 60s (ISR-style)
const res = await fetch('https://api.shop.com/products', {
next: { revalidate: 60 },
});
return res.json();
}
async function getCart() {
// Per-user, never cache
const res = await fetch('https://api.shop.com/cart', {
cache: 'no-store',
});
return res.json();
}Tagging and Targeted Revalidation
The Data Cache supports cache tags so you can invalidate exactly the entries tied to a piece of data — ideal after a Server Action mutates the database.
- Attach tags at fetch time:
next: { tags: ['products'] }. - Invalidate by tag with
revalidateTag('products'). - Invalidate by path with
revalidatePath('/products').
Both functions run on the server (Server Action or Route Handler) and purge the Data Cache and the affected Full Route Cache entries.
'use server';
import { revalidateTag } from 'next/cache';
import { db } from '@/lib/db';
export async function createProduct(formData: FormData) {
await db.product.create({
data: { name: String(formData.get('name')) },
});
// Purge every fetch tagged 'products' → next read is fresh
revalidateTag('products');
}Layer 3: The Full Route Cache
The Full Route Cache stores the fully rendered output of a route — both the HTML and the React Server Component (RSC) payload — on the server at build time. It only applies to statically rendered routes.
- A route is static unless it uses dynamic APIs (
cookies(),headers(),searchParams) or an uncachedfetch. - Static routes are served instantly from this cache with no re-render.
- Dynamic routes skip the Full Route Cache and render on every request.
Think of it as the rendered-output sibling of the Data Cache, which holds raw fetch data.
Static vs Dynamic: What Triggers Each
Whether a route lands in the Full Route Cache depends on what it touches during render. Reading a dynamic API opts the whole route into dynamic rendering.
- Using
cookies(),headers(),draftMode(), orsearchParams→ dynamic. - A
fetchwithcache: 'no-store'→ dynamic. - Exporting
export const dynamic = 'force-dynamic'→ dynamic, always.
You can force the other direction with export const dynamic = 'force-static' to keep a route cached.
// This page becomes DYNAMIC because it reads cookies()
import { cookies } from 'next/headers';
export default async function Dashboard() {
const store = await cookies(); // Next.js 15: cookies() is async
const theme = store.get('theme')?.value ?? 'light';
// Full Route Cache is skipped; rendered fresh per request
return <main data-theme={theme}>Welcome back</main>;
}Layer 4: The Router Cache (Client)
The Router Cache is an in-memory, client-side store of RSC payloads for routes the user has visited or prefetched. It makes back/forward navigation instant and avoids refetching on soft navigations.
- It lives in the browser and is cleared on a full page reload.
- It's why clicking a
<Link>back to a page shows the previous payload, even if the server data changed. - In Next.js 15, the default
staleTimefor page segments is 0, so dynamic pages are refetched on navigation by default.
This is the layer that confuses people most: the server already revalidated, but the client still shows old UI from its Router Cache.
Clearing the Router Cache After a Mutation
Server-side revalidation alone doesn't update the client's Router Cache. After a Server Action, you must invalidate the client cache too.
revalidatePath/revalidateTaginside a Server Action also marks the Router Cache stale, so the next navigation refetches.router.refresh()fromuseRouterclears the Router Cache and re-renders server components for the current route.- A full page reload wipes the Router Cache entirely.
Pairing a Server Action's revalidateTag with the cookie-based action response keeps both server and client fresh.
'use client';
import { useRouter } from 'next/navigation';
export function RefreshButton() {
const router = useRouter();
return (
<button onClick={() => router.refresh()}>
Reload server data
</button>
);
}Tracing a Request Through All Four
Follow a single navigation to see the layers cooperate:
- 1. Router Cache (client): if a fresh payload exists, render it instantly — stop here.
- 2. Full Route Cache (server): static route? serve the cached HTML/RSC.
- 3. Data Cache (server): dynamic render reads
fetchresults from here if cached/unexpired. - 4. Request Memoization: within that render, duplicate fetches collapse into one.
Stale data originates at whichever layer answered first. Debug top-down: rule out the Router Cache before blaming the server.
A Pure-TS Memoization Analogy
Request Memoization is conceptually a per-render memo keyed by arguments. Here's the same idea in plain TypeScript you can run and reason about — it dedupes concurrent calls by caching the in-flight promise.
The real Next.js version resets this map each render; this demo keeps it for the program's lifetime.
function memoize<T>(fn: (k: string) => Promise<T>) {
const inFlight = new Map<string, Promise<T>>();
return (key: string): Promise<T> => {
if (!inFlight.has(key)) inFlight.set(key, fn(key));
return inFlight.get(key)!;
};
}
let calls = 0;
const load = memoize(async (k: string) => {
calls++;
return `data:${k}`;
});
async function main() {
const [a, b] = await Promise.all([load('x'), load('x')]);
console.log(a, b, 'network calls =', calls);
}
main();Quick Check: Stale UI After a Server Action
You run a Server Action that creates a product and calls revalidateTag('products'). The server's Data Cache and Full Route Cache are now correct, but a user who navigates back via a <Link> still sees the old list. Which layer is serving the stale data?
Recap: The Four Caches
You can now place any stale-data bug at the right layer:
- Request Memoization — per-render fetch dedup; discarded after the render; extend to non-fetch with
cache(). - Data Cache — durable, cross-request fetch storage; opt in with
force-cache/revalidate; purge withrevalidateTag/revalidatePath. - Full Route Cache — rendered HTML/RSC for static routes; dynamic APIs opt out.
- Router Cache — client-side RSC payloads for visited routes; clear with revalidation or
router.refresh().
Debug top-down (client → server): rule out the Router Cache first, then Full Route, Data, and finally memoization.
자주 묻는 질문
“네 가지 캐시: 요청, 데이터, 전체 경로와 라우터” 강의는 무료인가요?
네 — “네 가지 캐시: 요청, 데이터, 전체 경로와 라우터” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Next.js 15 Fullstack (App Router + Server Actions) 강의 전체를 잠금 해제할 수 있습니다. Next.js 15 Fullstack (App Router + Server Actions) 강의에는 총 4개의 강의가 포함되어 있습니다.
“네 가지 캐시: 요청, 데이터, 전체 경로와 라우터”에서 뭘 배우나요?
각 캐시 계층의 상호 작용과 App Router 앱에서 오래된 데이터가 발생하는 위치를 파악하는 방법을 배웁니다. 브라우저에서 직접 실행하는 실습 코드로 Next.js 15 Fullstack (App Router + Server Actions)을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Next.js 15 Fullstack (App Router + Server Actions)을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Next.js 15 Fullstack (App Router + Server Actions)은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“네 가지 캐시: 요청, 데이터, 전체 경로와 라우터” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Next.js 15 Fullstack (App Router + Server Actions) 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Next.js 15 Fullstack (App Router + Server Actions) 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 네 가지 캐시: 요청, 데이터, 전체 경로와 라우터
- 시간 기반 및 주문형 재검증 전략
- 세밀한 캐시 무효화를 위한 태그 기반 무효화
- 선택적 제외: 동적 렌더링과 no-store