PPR(Partial Prerendering)은 한 라우트를 빌드 때 만든 정적 셸(Static Shell)과 요청 때 채우는 동적 구멍으로 나눠, 셸은 즉시 보내고 구멍은 스트리밍으로 채우는 렌더링 모델이다. Next.js 16에서 Cache Components(cacheComponents: true)를 켜면 기본 동작이다(2026 기준).
빌드 때 컴포넌트를 렌더하면서 각각을 이렇게 처리한다.
- 순수 계산, 모듈 import처럼 미리 끝낼 수 있는 것 → 셸에 포함
- 'use cache'로 캐시한 것 → 결과를 셸에 포함
- Suspense로 감싼 요청 시점 데이터(쿠키, 헤더, 매 요청 새 데이터) → fallback만 셸에 넣고 내용은 요청 때 스트리밍
export default function Page() {
return (
<>
<ProductInfo /> {/* 캐시된 상품 정보: 셸 */}
<Suspense fallback={<CartSkeleton />}>
<Cart /> {/* 쿠키를 읽는 장바구니: 요청 때 */}
</Suspense>
</>
)
}- 정적 페이지의 속도와 동적 페이지의 최신성을 한 라우트에서 함께 얻는다
- Suspense로 감싸지도 캐시하지도 않은 요청 시점 데이터에 접근하면 개발·빌드 때 에러가 난다. Suspense 경계를 어디에 둘지가 설계의 핵심이다
- 셸은 첫 로드용 HTML(HyperText Markup Language)과 클라이언트 이동용 RSC 페이로드로 만들어진다
멈췄다가 이어서 렌더하기(resume)
빌드 때 렌더가 동적 구멍에 닿으면 에러로 끝내지 않고 그 자리에서 멈추고(postpone), 어디까지 렌더했는지를 직렬화한 postponedState를 셸과 함께 남긴다. 요청이 오면 셸을 먼저 보내고, 저장한 상태로 멈춘 지점부터 이어서(resume) 렌더해 구멍을 스트리밍으로 채운다. 브라우저는 도착한 조각을 fallback 자리에 끼워 넣는다.
빌드: 셸 HTML + postponedState(+ 정적 부분의 RSC 페이로드) 저장
요청: 셸 즉시 전송 → postponedState로 동적 부분만 이어서 렌더 → 스트리밍- 셸과
postponedState는 한 쌍이다. 재검증 때도 함께 다시 만들어지고, 짝이 어긋나면 동적 출력이 틀어진다 - 기본
next start는 셸 전송과 동적 렌더를 한 번에 처리한다. 셸을 CDN(Content Delivery Network)에 따로 두고 오리진에 "이어서 렌더"만 요청하는 별도 resume 프로토콜은 어댑터·플랫폼 구현용이다 - 응답 헤더 표식이나 조각을 끼워 넣는 인라인 스크립트의 이름은 Next.js·React 내부 구현이라 버전에 따라 바뀔 수 있다. 디버깅 단서로만 쓴다
- Next.js 16.0에서
cacheComponents플래그가 이전의ppr·useCache·dynamicIO실험 플래그를 하나로 묶었다. PPR도 이 플래그로 켠다(2026 기준) - 셸이 정상인데 resume된 조각만 이상하면, 정적으로 확정되는 래퍼(sticky 컨테이너 등)를 경계 바깥으로 옮겨 셸에 넣는 것이 대개 맞는 위치이기도 하다
instant: 즉시 이동 검증
경계를 잘못 두면 첫 로드는 괜찮은데 형제 라우트로 이동할 때만 즉시 전환이 깨지는 문제가 생긴다. 클라이언트 이동은 공유 layout 아래만 다시 렌더해서 첫 로드와 다른 초기 UI가 나오기 때문이다. 세그먼트에서 export const instant = true를 내보내면 Next.js가 그 세그먼트로의 이동을 막는 코드를 개발 오버레이에 알려 준다. 고칠 곳은 대개 'use cache'로 캐시하거나 Suspense로 감싸는 것이다. 막혀도 되는 상위 layout은 false로 제외한다. Cache Components 앱은 기본으로 모든 페이지를 개발 모드에서 경고 수준으로 검증하고, 이 기본값 설정(experimental.instantInsights)은 아직 실험 기능이다(Next.js 16.3 기준). DevTools의 Navigation Inspector로 이동을 멈춰 놓고 초기 UI를 볼 수 있다.
Partial Prefetching (16.3, opt-in)
partialPrefetching: true(cacheComponents 필수, 기본 false)를 켜면 <Link>가 링크마다 따로 prefetch하지 않고, 라우트마다 URL에 의존하지 않는 App Shell 하나를 받아 같은 라우트로 가는 모든 링크가 재사용한다. params·searchParams에 달린 내용은 이동 뒤에 채워지고, 미리 받아야 하는 링크만 prefetch={true}로 더 받는다. 이 App Shell 덕분에 generateStaticParams에 없던 URL도 첫 방문에 셸을 바로 보낸다.
용어 비교는 CSR·SSR·SSG·ISR, 이전 캐시 모델은 Next.js 캐시 계층, 숨겨진 라우트를 보존하는 동작은 Activity와 숨겨진 라우트 보존.
출처: Next.js - Caching: Static, cached, and streaming · Next.js - instant · Next.js - Ensuring instant navigations · Next.js - partialPrefetching · Next.js - ISR with Cache Components · Next.js - PPR Platform Guide · Next.js - cacheComponents