Next.js 성능 최적화: 완벽한 2026 가이드

지난 4년간 나는 연간 5천만 달러의 매출을 올리는 전자상거래 스토어부터 일일 활성 사용자 10만 명 이상의 SaaS 대시보드까지 다양한 클라이언트의 Next.js 애플리케이션을 최적화해왔다. 내가 배운 것 중 일부는 공식 문서와 정확히 일치한다. 하지만 상당 부분은 그렇지 않다. 이것이 내가 처음 시작했을 때 누군가가 건네주었으면 하는 가이드이며, Next.js 15 및 2026년에 실제로 중요한 패턴들을 반영하여 업데이트되었다.

성능은 마지막에 덧붙이는 기능이 아니다. 첫날부터 내리는 일련의 결정이며, 각 결정은 누적된다. 초기 몇 가지를 놓치면 완전한 재작성을 해야 한다. 제대로 결정하면 앱이 사용자의 로컬 머신에서 실행되는 것처럼 느껴진다.

시작해보자.

목차

Next.js Performance Optimization: The Complete 2026 Guide

Next.js 15 성능 기초 이해하기

Next.js 15(2025년 후반부터 안정화)는 성능이 내부적으로 어떻게 작동하는지에 대해 몇 가지 중대한 변화를 가져왔다. Turbopack 번들러가 개발 및 프로덕션 빌드 모두에서 기본값이 되었다. 앱 라우터가 완전히 성숙해졌다. 그리고 Next.js 14에서 거의 모두를 혼동하게 했던 캐싱 동작이 정리되었다.

여기서 내재화해야 할 것은: Next.js는 여러 렌더링 전략을 제공하고, 주어진 페이지에 대해 잘못된 전략을 선택하는 것이 내가 보는 가장 흔한 성능 실수라는 점이다. 정적 생성, 서버 측 렌더링, 증분 정적 재생성, 부분 사전 렌더링, 스트리밍 -- 각각에는 특정 사용 사례가 있다. 일주일에 한 번 변경되는 마케팅 페이지에 SSR을 사용하는 것은 이유 없이 컴퓨팅 리소스만 낭비하는 것이다.

성능 정신 모델

Next.js 성능을 세 가지 계층으로 생각해보자:

  1. 빌드 타임 결정 -- 사전 렌더링될 것, 동적 부분, 코드 분할 방식
  2. 서버 타임 실행 -- 서버 응답 속도, 캐싱, 엣지 vs 오리진
  3. 클라이언트 타임 경험 -- 번들 크기, 하이드레이션 비용, 상호 작용 준비도

각 계층은 다른 계층을 곱한다. 빠른 서버 응답은 중급 Android 휴대폰에서 하이드레이션에 3초가 걸리는 500KB의 JavaScript를 배송하고 있다면 의미가 없다.

실제로 중요한 것 측정하기

무언가를 최적화하기 전에 측정해야 한다. 그리고 올바른 것들을 측정해야 한다.

Core Web Vitals는 2026년에도 Google의 순위 지정 신호로 남아있지만, 임계값이 더 엄격해졌다. 현재 상황은 다음과 같다:

메트릭 좋음 개선 필요 나쁨
LCP (Largest Contentful Paint) ≤ 2.0s 2.0s – 3.5s > 3.5s
INP (Interaction to Next Paint) ≤ 150ms 150ms – 300ms > 300ms
CLS (Cumulative Layout Shift) ≤ 0.1 0.1 – 0.25 > 0.25
TTFB (Time to First Byte) ≤ 400ms 400ms – 800ms > 800ms

실제로 사용하는 도구들

  • Vercel Speed Insights -- Vercel을 사용 중이라면 이것은 필수다. 합성이 아닌 실제 사용자 데이터.
  • next/bundle-analyzer -- 매주 실행하자. 번들 크기는 살피지 않으면 계속 커진다.
  • Chrome DevTools Performance 탭 -- 여전히 하이드레이션 문제를 디버깅하기 위한 표준.
  • WebPageTest -- 실제 위치에서 실제 기기에서 테스트하기 위해. 필름스트립 보기는 매우 유용하다.
  • Sentry Performance Monitoring -- 실제 API 응답 시간과 프로덕션의 서버 컴포넌트 렌더링 시간 추적.
# 프로젝트에 번들 분석기 추가
npm install @next/bundle-analyzer
// next.config.mjs
import withBundleAnalyzer from '@next/bundle-analyzer';

const config = withBundleAnalyzer({
  enabled: process.env.ANALYZE === 'true',
})({
  // your config here
});

export default config;

ANALYZE=true npm run build를 실행하고 실제로 출력물을 살펴보자. 최소한 하나 이상의 라이브러리가 예상했던 것보다 훨씬 크다는 것을 발견할 것이다.

서버 컴포넌트: 아마도 과소평가되고 있는 최대의 승리

서버 컴포넌트는 현대적 Next.js에서 가장 큰 성능 개선이다. 클라이언트에 0바이트의 JavaScript를 보낸다. 0. HTML은 서버에서 렌더링되고, 브라우저로 스트림되며, 컴포넌트는 절대 하이드레이션되지 않는다.

하지만 사람들이 실수하는 지점이 있다: 'use client'를 너무 서둘러 추가한다. 개발자가 이전 Pages Router 정신 모델에 익숙했기 때문에 컴포넌트의 80%가 클라이언트 컴포넌트인 코드베이스를 검토했다. 모든 'use client' 지시문은 하이드레이션 경계이다. 모든 하이드레이션 경계는 브라우저가 다운로드, 파싱 및 실행해야 하는 JavaScript이다.

따르는 규칙

기본적으로 컴포넌트를 서버 컴포넌트로 유지하자. 다음이 필요한 경우에만 'use client'를 추가하자:

  • 이벤트 핸들러 (onClick, onChange, 등)
  • useState, useEffect, useRef
  • 브라우저 전용 API (localStorage, window, 등)
  • 훅을 사용하는 써드파티 클라이언트 라이브러리

컴포지션 패턴

더 큰 컴포넌트의 작은 부분에 상호 작용이 필요한 경우, 전체 컴포넌트를 클라이언트 컴포넌트로 만들지 말자. 대신:

// app/product/[id]/page.tsx (Server Component)
import { getProduct } from '@/lib/products';
import { AddToCartButton } from '@/components/AddToCartButton';
import { ProductReviews } from '@/components/ProductReviews';

export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await getProduct(params.id);

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      {/* 이 작은 버튼만 클라이언트 컴포넌트 */}
      <AddToCartButton productId={product.id} price={product.price} />
      {/* 이 전체 리뷰 섹션은 서버에 남음 */}
      <ProductReviews productId={product.id} />
    </div>
  );
}
// components/AddToCartButton.tsx
'use client';

export function AddToCartButton({ productId, price }: { productId: string; price: number }) {
  const handleClick = () => {
    // cart logic
  };

  return <button onClick={handleClick}>Add to Cart -- ${price}</button>;
}

이 패턴만으로도 우리가 작업한 프로젝트의 번들 크기에서 40-60%를 줄였다.

Next.js Performance Optimization: The Complete 2026 Guide - architecture

번들 크기 최적화

Next.js 15의 Turbopack은 webpack이 했던 것보다 트리 쉐이킹을 더 잘 처리하지만, 나쁜 가져오기로부터는 구할 수 없다.

명명된 가져오기는 중요하다

// 나쁨 -- 전체 라이브러리 가져오기
import _ from 'lodash';
const sorted = _.sortBy(items, 'name');

// 좋음 -- 필요한 것만 가져오기
import sortBy from 'lodash/sortBy';
const sorted = sortBy(items, 'name');

// 최고 -- lodash가 정말 필요한가?
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name));

2026년의 일반적인 번들 비대화 라이브러리들

라이브러리 일반적 크기 (gzipped) 대체 방안 크기 절감
moment.js 72KB date-fns (tree-shakeable) ~60KB
lodash (전체) 71KB Native JS / lodash-es ~65KB
chart.js 65KB lightweight-charts ~45KB
react-icons (전체) 40KB+ 개별 아이콘 패키지 ~35KB
framer-motion 44KB motion (lite) 또는 CSS ~30KB

무거운 컴포넌트를 위한 동적 가져오기

import dynamic from 'next/dynamic';

const HeavyChart = dynamic(() => import('@/components/Chart'), {
  loading: () => <div className="h-64 animate-pulse bg-gray-100 rounded" />,
  ssr: false, // 브라우저 전용인 경우 서버에서 렌더링하지 않음
});

20KB 이상이고 접힌 부분 위에 없는 것들에는 동적 가져오기를 사용한다. 차트, 리치 텍스트 편집기, 지도, 복잡한 모달 -- 모두 지연 로드된다.

이미지 및 미디어 최적화

next/image 컴포넌트는 Next.js 15에서 상당히 개선되었다. 이제 기본적으로 AVIF를 지원하며 (WebP와 함께), 자동 크기 감지가 더 신뢰할 수 있다.

중요 이미지 최적화

import Image from 'next/image';

// 영웅 이미지 -- 접힌 부분 위, 우선 처리 필요
<Image
  src="/hero.jpg"
  alt="Product showcase"
  width={1200}
  height={630}
  priority // 이 이미지 사전 로드
  sizes="100vw"
  quality={80} // 80은 보통 최적의 포인트
/>

// 접힌 부분 아래 이미지 -- 기본적으로 지연 로드
<Image
  src="/feature.jpg"
  alt="Feature detail"
  width={600}
  height={400}
  sizes="(max-width: 768px) 100vw, 50vw"
  placeholder="blur"
  blurDataURL={feature.blurHash}
/>

`sizes` 속성은 선택사항이 아니다

나는 이것이 항상 건너뛰어지는 것을 본다. 적절한 sizes 속성 없이, 브라우저는 뷰포트에 관계없이 가장 큰 이미지 변형을 다운로드한다. 모바일에서는 375px 화면을 위해 2400px 너비의 이미지를 로드할 수 있다. 사이즈를 지정하자. 매번.

비디오 최적화

비디오의 경우, 거대한 MP4 파일과 함께 <video> 태그를 사용하지 말자. 2026년에는 방법이:

  1. FFmpeg 또는 Mux 같은 서비스를 사용하여 여러 품질로 인코딩
  2. 10초 이상의 모든 항목에 HLS 스트리밍 사용
  3. 짧은 애니메이션의 경우, WebM 또는 심지어 animated AVIF 고려
  4. IntersectionObserver를 사용하여 접힌 부분 아래 비디오 지연 로드

데이터 페칭 및 캐싱 전략

Next.js 15는 14의 혼란스러운 기본값과 비교하여 캐싱을 단순화했다. 핵심 변화: 기본적으로 아무것도 캐시되지 않는다. 명시적으로 캐싱에 옵트인한다. 이것은 훨씬 더 합리적이다.

`use cache` 지시문을 사용한 캐싱

Next.js 15는 use cache 지시문을 도입했다 (현재 canary 상태, 15.2에서 안정화될 것으로 예상):

async function getProducts() {
  'use cache';
  const products = await db.products.findMany();
  return products;
}

fetch API의 경우, 캐싱은 명시적으로 제어된다:

// 캐싱 없음 (Next.js 15의 기본값)
const data = await fetch('https://api.example.com/data');

// 수동으로 재검증될 때까지 캐시
const data = await fetch('https://api.example.com/data', {
  cache: 'force-cache',
});

// 60초마다 재검증
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 60 },
});

콘텐츠 타입별 캐싱 전략

콘텐츠 타입 전략 재검증 예제
마케팅 페이지 정적 (빌드 타임) 배포 시 홈페이지, 소개
제품 목록 ISR 60-300초 카테고리 페이지
사용자 대시보드 동적 (캐시 없음) 매 요청 계정 설정
블로그 게시글 ISR 3600초 CMS 기반 콘텐츠
검색 결과 동적 + 클라이언트 캐시 SWR 패턴 검색 페이지
API 데이터 서버 + CDN 캐시 변함 REST/GraphQL

헤드리스 CMS를 사용하는 프로젝트의 경우, 대부분 우리가 빌드하는 것, ISR with 웹훅 트리거 재검증이 업계 표준이다. 콘텐츠 업데이트는 전체 사이트를 재빌드하지 않고도 몇 초 내에 나타난다.

엣지 런타임 및 미들웨어 성능

엣지 런타임은 사용자 근처의 CDN 노드에서 코드를 실행한다. TTFB가 극적으로 떨어진다 -- 우리는 엣지에서 50-150ms TTFB 대 단일 지역 오리진의 300-800ms를 측정했다.

하지만 주의 사항이 있다: 엣지 런타임은 모든 Node.js API를 지원하지 않는다. fs 없음, 제한된 crypto, 네이티브 모듈 없음. 코드는 전체 Node.js 프로세스가 아닌 V8 isolate에서 실행된다.

엣지를 사용할 때

  • 미들웨어 (인증 확인, 리디렉트, A/B 테스트)
  • 데이터베이스 연결이 필요하지 않은 간단한 API 라우트
  • 정적으로 캐시할 수 없는 개인화가 있는 페이지

엣지를 피할 때

  • 무거운 데이터베이스 쿼리 (연결 풀링은 엣지에서 잘 작동하지 않음)
  • Node.js 특정 라이브러리를 사용하는 라우트
  • 25ms 이상의 CPU 시간이 필요한 것 (엣지 함수는 엄격한 제한이 있음)
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 빠른 지리 기반 리디렉트 -- 엣지에서 실행
  const country = request.geo?.country;

  if (country === 'DE' && !request.nextUrl.pathname.startsWith('/de')) {
    return NextResponse.redirect(new URL('/de' + request.nextUrl.pathname, request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

미들웨어는 가볍게 유지하자. 미들웨어의 모든 밀리초는 모든 단일 페이지 로드에 더해진다.

데이터베이스 및 API 계층 최적화

연결 풀링

Serverless 함수는 계속 스핀 업 및 다운된다. 연결 풀링 없이, 각 호출은 새로운 데이터베이스 연결을 연다. 규모에서 이것은 데이터베이스를 죽인다.

연결 풀러를 사용하자:

  • PgBouncer for PostgreSQL (Supabase and Neon include this)
  • Prisma Accelerate Prisma를 사용 중이라면 (연결 풀 + 전역 캐시 추가)
  • Drizzle with postgres.js 효율적으로 연결을 처리한다

쿼리 최적화 패턴

// 나쁨 -- N+1 쿼리 문제
const posts = await db.post.findMany();
for (const post of posts) {
  post.author = await db.user.findUnique({ where: { id: post.authorId } });
}

// 좋음 -- join을 사용한 단일 쿼리
const posts = await db.post.findMany({
  include: { author: true },
});

// 최고 -- 필요한 필드만 선택
const posts = await db.post.findMany({
  select: {
    id: true,
    title: true,
    slug: true,
    author: {
      select: { name: true, avatar: true },
    },
  },
});

병렬 데이터 페칭

이것은 가장 영향력 있는 패턴 중 하나이며 범죄적으로 과소 사용된다:

// 나쁨 -- 순차적 (총 시간 = 모든 페치의 합)
const products = await getProducts();
const categories = await getCategories();
const banners = await getBanners();

// 좋음 -- 병렬 (총 시간 = 가장 느린 페치)
const [products, categories, banners] = await Promise.all([
  getProducts(),
  getCategories(),
  getBanners(),
]);

나는 이 단일 변화만으로도 페이지 로드 시간을 절반으로 줄이는 것을 봤다.

렌더링 전략 선택

Next.js 15는 다섯 가지 렌더링 전략을 제공한다. 나는 이렇게 결정한다:

부분 사전 렌더링 (PPR)

PPR은 최신이면서도 가장 흥미로운 옵션이다. 빌드 타임에 페이지의 셸을 정적으로 사전 렌더링한 다음, 동적 콘텐츠를 스트림한다. 사용자는 즉시 정적 응답을 보고 개인화된 콘텐츠는 로드되는 동안 나타난다.

// app/page.tsx -- PPR 활성화됨
import { Suspense } from 'react';
import { StaticHero } from '@/components/StaticHero';
import { PersonalizedRecommendations } from '@/components/Recommendations';

export default function HomePage() {
  return (
    <div>
      {/* 정적 셸 -- CDN에서 즉시 제공 */}
      <StaticHero />

      {/* 동적 콘텐츠 -- 스트림됨 */}
      <Suspense fallback={<RecommendationsSkeleton />}>
        <PersonalizedRecommendations />
      </Suspense>
    </div>
  );
}

설정에서 PPR을 활성화하자:

// next.config.mjs
export default {
  experimental: {
    ppr: 'incremental',
  },
};

전자상거래 및 콘텐츠가 풍부한 사이트의 경우, PPR은 두 가지 최고를 제공한다 -- CDN 속도 초기 로드 및 개인화된 콘텐츠.

써드파티 스크립트 관리

써드파티 스크립트는 조용한 성능 킬러이다. 분석, 채팅 위젯, 광고 추적기, A/B 테스트 도구 -- 모두 합쳐진다.

`next/script`를 전략적으로 사용하자

import Script from 'next/script';

// 분석 -- 페이지가 상호 작용 가능한 후 로드
<Script
  src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"
  strategy="afterInteractive"
/>

// 채팅 위젯 -- 유휴 상태일 때 로드
<Script
  src="https://widget.intercom.io/widget/xxxxx"
  strategy="lazyOnload"
/>

// 중요한 A/B 테스트 -- 페인트 전에 로드되어야 함
<Script
  src="https://cdn.optimizely.com/js/xxxxx.js"
  strategy="beforeInteractive"
/>

무자비해지자. 추가하는 모든 스크립트는 사용자에게 시간이 소비된다. 분기별로 써드파티 스크립트를 감사할 것을 권장한다. 최소한 절반의 시간, 누군가가 더 이상 사용하지 않는 도구에 대한 스크립트를 찾을 것이다.

Partytown for Worker-Based Loading

중요하지 않은 써드파티 스크립트의 경우, Partytown을 고려하자. 스크립트를 웹 워커로 이동하여 주 스레드를 자유롭게 유지한다:

<Script
  src="https://example.com/analytics.js"
  strategy="worker" // Partytown을 통해 웹 워커에서 실행
/>

인프라 및 배포

어디에 배포하고 어떻게 배포하는지는 대부분의 개발자가 생각하는 것보다 더 중요하다.

2026년 Next.js를 위한 플랫폼 비교

플랫폼 SSR 지원 엣지 함수 콜드 스타트 시작 가격
Vercel 전체 Yes (전역) ~50ms $20/mo (Pro)
Cloudflare Pages 전체 (OpenNext를 통해) Yes (전역) ~10ms $5/mo
AWS Amplify 전체 제한됨 ~200ms Pay-per-use
Netlify 전체 Yes (Deno) ~100ms $19/mo (Pro)
Self-hosted (Docker) 전체 없음 N/A 서버 비용
Coolify / SST 전체 상황에 따라 ~150ms 서버 비용

Vercel은 여전히 Next.js의 가장 적은 저항 경로이다. 그들은 프레임워크를 빌드하고, 그것을 위해 플랫폼을 최적화한다. 하지만 OpenNext를 사용한 Cloudflare Pages는 2026년에 심각한 경쟁자가 되었으며, 특히 비용 민감한 프로젝트를 위해.

자체 호스팅 배포가 필요한 클라이언트의 경우, 우리는 CDN 뒤의 Docker 컨테이너로 좋은 결과를 얻었다. 더 많은 설정이 필요하지만, 인프라를 완전히 제어한다.

CDN 및 엣지 캐싱

플랫폼에 관계없이, 모든 것 앞에 CDN을 놓자. 정적 자산은 불변 캐시 헤더를 가져야 한다. ISR 페이지는 stale-while-revalidate를 사용해야 한다. API 응답은 적절한 곳에서 캐시해야 한다.

// 캐시할 수 있는 API 라우트의 경우
export async function GET() {
  const data = await getPopularProducts();

  return Response.json(data, {
    headers: {
      'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
    },
  });
}

실제 벤치마크 및 사례 연구

다음은 지난 1년 동안 최적화한 프로젝트에서 나온 실제 수치이다:

전자상거래 사이트 (Shopify Headless + Next.js 15)

  • 이전: LCP 4.2s, INP 380ms, 번들 487KB
  • 이후: LCP 1.4s, INP 89ms, 번들 156KB
  • 핵심 변화: 제품 페이지용 서버 컴포넌트, 이미지 최적화, 4개의 사용하지 않는 써드파티 스크립트 제거, 클라이언트 측 카트에서 서버 액션으로 전환
  • 비즈니스 영향: 전환율 23% 증가

SaaS 대시보드 (Next.js 14 → 15 마이그레이션)

  • 이전: 초기 로드 6.8s, TTI 8.2s
  • 이후: 초기 로드 2.1s, TTI 2.8s
  • 핵심 변화: 앱 라우터로 마이그레이션, 데이터가 많은 테이블에 대한 스트리밍 구현, 혼합 정적/동적 페이지에 대한 PPR 추가, 병렬 데이터 페칭

콘텐츠 플랫폼 (Headless CMS + Next.js)

  • 이전: TTFB 890ms (SSR), LCP 3.1s
  • 이후: TTFB 120ms (ISR + 엣지), LCP 1.1s
  • 핵심 변화: SSR에서 ISR with 온디맨드 재검증으로 전환, 엣지에 배포, CMS 쿼리 최적화

이것들은 선택된 수치가 아니다. 이 가이드의 패턴을 체계적으로 적용할 때 달성 가능한 것들의 대표적인 예이다.

FAQ

Next.js 성능 최적화는 보통 얼마나 비용이 드는가?

애플리케이션의 크기와 복잡성에 따라 크게 다르다. 간단한 사이트의 경우, 1-2주의 집중적인 최적화 스프린트는 극적인 결과를 달성할 수 있다. 깊은 아키텍처 문제가 있는 대규모 애플리케이션의 경우, 4-8주의 리팩토링을 계획하자. ROI는 보통 개선된 전환율과 감소된 인프라 비용을 통해 스스로를 정당화한다.

Next.js 15가 Next.js 14보다 빠른가?

예, 측정 가능하게. Turbopack을 기본 번들러로 사용하면 빌드 시간이 30-50% 단축되고 약간 더 작은 번들이 생성된다. 단순화된 캐싱 모델은 불필요한 서버 로드를 줄인다. 그리고 부분 사전 렌더링은 올바르게 사용할 경우 인지된 성능을 크게 개선한다. 마이그레이션 후 평균 15-25% TTFB 개선을 봤다.

2026년에 Pages Router 또는 App Router를 사용해야 하는가?

App Router, 전적으로. Pages Router는 여전히 작동하고 여전히 지원되지만, 모든 성능 혁신은 App Router에서 일어난다. 서버 컴포넌트, 스트리밍, PPR, 서버 액션 -- 이것들은 Pages Router에서 사용할 수 없다. 새로운 프로젝트를 시작할 경우, Pages Router를 사용할 이유가 없다.

Next.js 번들 크기를 빠르게 줄이려면?

먼저 번들 분석기를 실행하자 -- 정확히 무게가 어디에 있는지 보여준다. 그 다음: 무거운 라이브러리를 더 가벼운 대체 방안으로 교체, 아래 접힌 부분의 컴포넌트에 대해 동적 가져오기 사용, 트리 쉐이킹 가능한 라이브러리에서 명명된 가져오기를 사용 중인지 확인, 'use client' 지시문을 감사하자. 이 네 단계만으로도 보통 번들 크기가 30-50% 감소한다.

호스팅 플랫폼이 정말 Next.js 성능에 영향을 미치는가?

예상보다 더 많다. Vercel의 인프라는 Next.js에 특별히 조정된다 -- 그들의 엣지 네트워크, ISR 구현, 이미지 최적화 CDN은 밀접하게 통합된다. 다른 플랫폼도 잘 작동하지만, Vercel이 자동으로 처리하는 것을 수동으로 구성해야 할 수 있다. 가장 큰 요소는 지리적 분배이다 -- 사용자가 전역적이라면, 플랫폼에 관계없이 엣지 배포 또는 CDN이 필요하다.

내가 보는 가장 큰 Next.js 성능 실수는 무엇인가?

모든 것을 클라이언트 컴포넌트로 만드는 것. 개발자가 최상위 레벨에서 하나의 onClick 핸들러가 필요했기 때문에 전체 페이지 트리가 'use client'로 감싸진 코드베이스를 감사했다. 이것은 브라우저가 모든 것을 다운로드하고 하이드레이션하도록 강제하여, Next.js를 빠르게 만드는 서버 컴포넌트의 이점을 완전히 무효화한다. 클라이언트 컴포넌트가 작은, 잎 레벨 노드가 되도록 컴포넌트 트리를 재구성하자.

부분 사전 렌더링 (PPR)은 일반 ISR과 어떻게 비교되는가?

ISR은 빌드 타임에 전체 페이지를 생성하고 주기적으로 재검증한다. PPR은 빌드 타임에 정적 셸을 사전 렌더링하지만 동적 "구멍"을 남겨두고 요청 타임에 스트리밍을 통해 채운다. PPR은 정적 및 개인화된 콘텐츠를 혼합하는 페이지에 더 낫다 -- 설명은 정적이지만 권장 제품은 개인화된 제품 페이지를 생각해보자. 초기 응답은 순수 정적만큼 빠르지만 동적 콘텐츠는 전체 페이지 로드 없이 나타난다.

Vercel 없이 Next.js 성능을 최적화할 수 있는가?

절대적으로. 이 가이드의 최적화는 호스팅 플랫폼에 관계없이 작동한다. 서버 컴포넌트, 번들 최적화, 이미지 최적화, 캐싱 전략, 병렬 데이터 페칭 -- 이것들은 애플리케이션 레벨의 관심사이다. 엣지 함수 및 빌트인 ISR 지원 같은 플랫폼 특정 기능은 다양하지만, OpenNext 같은 도구는 Cloudflare, AWS 및 다른 플랫폼에서 유사한 성능 특성을 가진 완전한 기능의 Next.js를 실행할 수 있게 한다.