번역 자료 / / 2025. 5. 24. 10:54

[nextjs 번역] 부분 프리렌더링(Partial Prerendering) 사용하기

원문: Getting Started: Partial Prerendering | Next.js


이 기능은 현재 실험적(Experimental)이며, 변경될 수 있으므로 프로덕션 환경에서는 권장되지 않습니다. 테스트 후 피드백을 GitHub에 남겨주세요.

부분 프리렌더링(Partial Prerendering, PPR)은 하나의 라우트에서 정적(static)과 동적(dynamic) 콘텐츠를 결합할 수 있는 렌더링 전략입니다. 이를 통해 초기 페이지 성능을 높이면서도 개인화된 동적 데이터를 지원할 수 있습니다.

  • 사용자가 라우트에 접근하면 서버는 정적 콘텐츠로 구성된 shell을 먼저 전송하여 빠른 초기 로드를 보장합니다.
  • shell에는 동적 콘텐츠가 들어갈 자리(hole)가 남아 있으며, 이 부분은 비동기로 로드됩니다.
  • 동적 콘텐츠는 병렬로 스트리밍되어 전체 페이지 로드 시간을 단축합니다.

🎥 참고: PPR이 필요한 이유와 동작 방식 YouTube(10분)

부분 프리렌더링의 동작 방식

Next.js의 다양한 렌더링 전략을 이해하면 PPR의 동작 원리를 쉽게 파악할 수 있습니다.

정적 렌더링(Static Rendering)

정적 렌더링은 HTML을 빌드 타임 또는 재검증 시점에 미리 생성하여, 결과를 캐시에 저장하고 여러 사용자/요청에 공유합니다. PPR에서는 라우트의 정적 shell(레이아웃, 요청 시점 데이터에 의존하지 않는 컴포넌트 등)을 미리 렌더링합니다.

동적 렌더링(Dynamic Rendering)

동적 렌더링은 요청 시점에 HTML을 생성하여, 개인화된 콘텐츠를 제공합니다. 다음 API를 사용하는 컴포넌트는 동적으로 간주됩니다:

  • cookies
  • headers
  • connection
  • draftMode
  • searchParams prop
  • unstable_noStore
  • fetch({ cache: 'no-store' })

PPR에서 이러한 API를 사용하면 해당 컴포넌트는 정적으로 렌더링될 수 없다는 React 에러가 발생하며, Suspense로 감싸서 런타임에 렌더링을 미룰 수 있습니다.

Suspense

React Suspense는 특정 조건이 충족될 때까지 애플리케이션의 일부 렌더링을 미루는 기능입니다. PPR에서는 Suspense를 동적 경계(dynamic boundary)로 사용합니다.

빌드 타임에 Next.js는 정적 콘텐츠와 fallback UI를 미리 렌더링하고, 동적 콘텐츠는 사용자가 라우트에 접근할 때까지 지연(postponed)됩니다.

// app/page.js
import { Suspense } from 'react'
import StaticComponent from './StaticComponent'
import DynamicComponent from './DynamicComponent'
import Fallback from './Fallback'

export const experimental_ppr = true

export default function Page() {
  return (
    <>
      <StaticComponent />
      <Suspense fallback={<Fallback />}>
        <DynamicComponent />
      </Suspense>
    </>
  )
}

스트리밍(Streaming)

스트리밍은 라우트를 여러 청크로 분할하여, 준비된 부분부터 클라이언트로 점진적으로 전송하는 방식입니다. 이를 통해 전체 콘텐츠가 렌더링되기 전에도 일부 페이지를 즉시 볼 수 있습니다.

PPR에서는 Suspense로 감싼 동적 컴포넌트가 서버에서 병렬로 스트리밍됩니다. 정적 HTML과 스트리밍되는 동적 부분이 하나의 HTTP 요청으로 전송되어 네트워크 오버헤드를 줄이고, 초기 로드와 전체 성능을 모두 향상시킵니다.

부분 프리렌더링 활성화하기

next.config.ts 파일에 ppr 옵션을 추가하면 PPR을 활성화할 수 있습니다.

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    ppr: 'incremental',
  },
}

export default nextConfig

'incremental' 값은 특정 라우트에만 PPR을 적용할 수 있게 해줍니다:

// app/dashboard/layout.tsx
export const experimental_ppr = true

export default function Layout({ children }: { children: React.ReactNode }) {
  // ...
}

experimental_ppr를 명시하지 않은 라우트는 기본적으로 PPR이 비활성화됩니다. 각 라우트의 최상위 세그먼트에만 추가하면 하위 레이아웃/페이지에도 적용됩니다. 하위 세그먼트에서 PPR을 비활성화하려면 experimental_ppr = false로 설정하세요.

예시

동적 API 사용 예시

동적 API(예: cookies 등)를 사용하는 컴포넌트는 동적으로 렌더링됩니다. PPR을 계속 사용하려면 해당 컴포넌트를 Suspense로 감싸세요.

// app/user.tsx
import { cookies } from 'next/headers'

export async function User() {
  const session = (await cookies()).get('session')?.value
  return '...'
}
// app/page.tsx
import { Suspense } from 'react'
import { User, AvatarSkeleton } from './user'

export const experimental_ppr = true

export default function Page() {
  return (
    <section>
      <h1>This will be prerendered</h1>
      <Suspense fallback={<AvatarSkeleton />}>
        <User />
      </Suspense>
    </section>
  )
}

동적 props 전달 예시

컴포넌트는 실제로 값을 읽을 때만 동적으로 렌더링됩니다. 예를 들어, searchParams를 prop으로 전달하고, 하위 컴포넌트에서 접근하면 해당 컴포넌트만 동적으로 렌더링됩니다.

// app/page.tsx
import { Table, TableSkeleton } from './table'
import { Suspense } from 'react'

export default function Page({ searchParams }: { searchParams: Promise<{ sort: string }> }) {
  return (
    <section>
      <h1>This will be prerendered</h1>
      <Suspense fallback={<TableSkeleton />}>
        <Table searchParams={searchParams} />
      </Suspense>
    </section>
  )
}
// app/table.tsx
export async function Table({ searchParams }: { searchParams: Promise<{ sort: string }> }) {
  const sort = (await searchParams).sort === 'true'
  return '...'
}

Next Steps

부분 프리렌더링의 config 옵션에 대해 더 알아보세요.


출처: https://nextjs.org/docs/app/getting-started/partial-prerendering

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유