Next.js · Cache Components

한 페이지, 영역별 캐시 경계

같은 페이지 안에서도 영역마다 캐시 전략이 다릅니다. 헤더는 데이터가 없어 정적 셸에 들어가고, 상품 목록은 컴포넌트 첫 줄의 'use cache'로 렌더 결과째 캐시되며, 환율 배너는 cookies()를 읽어 요청마다 렌더하되 getRate() 함수 결과만 재사용하고, 맞춤 추천은 캐시 없이 매번 서버에서 렌더해 스트리밍됩니다. 한 번의 요청을 재생하면 네 영역의 데이터가 원천 → 캐시 계층 → 화면으로 어떤 경로를 지나 언제 도착하는지, 캐시가 비어 있을 때 무엇이 늦어지는지가 보입니다.

개념 시각화 · Next.js 16.3.2 기준 · 시간(ms)은 시연용이며 실측이 아닙니다공식 문서 ↗
0ms / 1600ms
정적 셸'use cache' 컴포넌트'use cache' 함수동적 · 캐시 없음
시나리오 · 요청이 올 때 캐시 상태

캐시 채워짐 (평소)—요청 도착 — 정적 셸(헤더 + 캐시된 상품 목록)이 곧바로 나간다

// app/page.tsx
export default function Page() {
return (
<>
<Header />// 데이터 없음 → 정적 셸
<ProductList />// 컴포넌트 첫 줄 'use cache' → 출력 캐시, 셸에 포함
<Suspense fallback={<BannerSkeleton />}>
<PriceBanner />// cookies() 읽어 요청마다 렌더 · getRate()만 캐시
</Suspense>
<Suspense fallback={<ListSkeleton />}>
<Recommendations />// cookies() + DB → 캐시 없음, 스트리밍
</Suspense>
</>
)
}
app/_components/ProductList.tsx — 캔버스·코드 지도에 마우스를 올리면 바뀝니다
export async function ProductList() {
'use cache'
cacheLife('hours'); cacheTag('products')
const items = await db.products.list()
return <Grid items={items} />
}

영역마다 캐시 경계가 다르다 — 항목·코드·캔버스 행은 같은 색과 호버로 연결됩니다.

  • 헤더 · 내비게이션 — 정적 셸app/_components/Header.tsx · caching.md · 정적 셸

    데이터를 읽지 않는 순수 마크업이라 빌드 시점에 HTML로 미리 만들어진다. 요청이 오면 정적 셸의 일부로 첫 바이트와 함께 도착한다 — 캐시 지시자를 붙일 필요조차 없다.

  • 상품 목록 — 'use cache' 컴포넌트app/_components/ProductList.tsx · use-cache.md · cacheLife.md · cacheTag.md

    컴포넌트 함수 첫 줄에 'use cache'를 두면 렌더 결과(RSC 페이로드) 자체가 캐시된다. cacheLife('hours')로 수명을, cacheTag('products')로 갱신 손잡이를 단다. 캐시가 채워져 있으면 정적 셸에 포함돼 헤더와 같은 순간에 도착하고, 비어 있으면 DB까지 다녀와 저장한 뒤에야 셸이 완성된다.

  • 환율 배너 — 'use cache' 함수app/_components/PriceBanner.tsx · lib/rates.ts · use-cache.md:126 (cookies는 밖에서 읽어 인자로)

    컴포넌트는 cookies()로 사용자의 통화를 읽어야 해서 요청마다 렌더된다(Suspense 안). 대신 비싼 데이터 함수 getRate(currency)에만 'use cache'를 붙여 반환값을 재사용한다 — 통화를 인자로 넘기므로 통화별로 캐시 키가 갈린다. cookies()를 함수 안에서 읽으면 next-request-in-use-cache 오류가 난다.

  • 맞춤 추천 — 동적 · 캐시 없음app/_components/Recommendations.tsx · caching.md · Suspense 스트리밍

    사용자마다 결과가 다르고 매 요청 최신이어야 해서 캐시하지 않는다. cookies()로 세션을 읽고 DB를 조회해 서버에서 렌더링한 뒤 <Suspense> 경계 안으로 스트리밍된다 — 그동안 사용자는 fallback 스켈레톤을 본다.