버전 9부터 Next.js 앱을 배포해왔습니다. getServerSideProps가 새로운 핫한 기능이던 시절 말이죠. 지난 1년간 3개의 대규모 프로덕션 애플리케이션을 Next.js 16의 App Router로 마이그레이션했고, SSR과 React Server Components를 언제 사용할지에 관한 모든 실수를 저질렀습니다. 이 가이드는 마이그레이션을 시작하기 전에 제가 원했던 문서입니다.

SSR vs RSC에 관한 대화는 과장광고, 불완전한 정신모델, 솔직히 말해 혼란스러운 문서로 인해 뒤틀려 있습니다. 이들은 경쟁하는 기술이 아닙니다 — 애플리케이션의 다른 계층에서 다른 문제를 해결하는 상호보완적 도구입니다. 하지만 특정 시나리오에서 어떤 도구를 사용해야 할지 아는 것? 그곳이 진정한 엔지니어링 판단이 살아숨쉬는 곳입니다.

실제 프로덕션 수치, 실제 코드 패턴, 그리고 컨퍼런스에서는 아무도 이야기하지 않는 트레이드오프를 포함해 제가 배운 모든 것을 안내하겠습니다.

목차

SSR vs RSC in Next.js 16: A Production Decision Guide

기초 이해하기

세부사항으로 들어가기 전에, 명확한 정신모델을 구축해봅시다. 이것은 당신이 생각하는 것보다 훨씬 중요합니다 — 용어가 겹치기 때문에 SSR과 RSC를 혼동하는 시니어 엔지니어들을 봤습니다.

**Server Side Rendering (SSR)**은 렌더링 전략입니다. 컴포넌트 트리가 HTML로 변환되는 시점위치를 결정합니다. SSR을 사용하면, 모든 요청이 서버에 도달하고, 전체 컴포넌트 트리를 HTML로 렌더링하고, 클라이언트에 보내고, React가 전체 트리를 하이드레이션해서 상호작용하도록 합니다.

**React Server Components (RSC)**는 컴포넌트 타입입니다. 클라이언트에 무엇이 전송되는지를 결정합니다. Server Components는 서버에서 실행되고 렌더링된 출력을(HTML이 아닌 직렬화된 React 트리로) 클라이언트에 보냅니다. 하이드레이션되지 않습니다. JavaScript를 브라우저에 보내지 않습니다.

차이를 보이나요? SSR은 렌더링 타이밍에 관한 것입니다. RSC는 컴포넌트 경계와 어떤 코드가 어디로 배포되는지에 관한 것입니다.

App Router가 포함된 Next.js 16.2에서는 실제로 둘 다 동시에 사용하고 있습니다. 모든 페이지 요청은 Server Components와 Client Components를 포함하는 컴포넌트 트리의 서버 측 렌더링을 포함합니다. RSC 계층은 어떤 컴포넌트가 하이드레이션 JavaScript를 필요로 하는지 결정하고, SSR 계층은 HTML이 어떻게, 언제 생성되는지 결정합니다.

구성 모델

내가 너무 오래 걸려서 이해한 핵심 통찰: App Router에서 Server Components가 기본값입니다. 'use client'로 클라이언트 동작에 opt in합니다. 이는 Pages Router 모델을 뒤집습니다.

// App Router에서 기본값으로 Server Component입니다
// 이 컴포넌트에 대해 JavaScript가 브라우저로 전송되지 않습니다
async function ProductPage({ params }: { params: { id: string } }) {
  const product = await db.product.findUnique({ where: { id: params.id } });
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      {/* 이 Client Component 아일랜드는 독립적으로 하이드레이션됩니다 */}
      <AddToCartButton productId={product.id} price={product.price} />
    </div>
  );
}
// components/AddToCartButton.tsx
'use client';

import { useState } from 'react';

export function AddToCartButton({ productId, price }: Props) {
  const [loading, setLoading] = useState(false);
  // 오직 이 컴포넌트의 JS만 브라우저로 전송됩니다
  return <button onClick={handleAdd}>Add to Cart — ${price}</button>;
}

Next.js 16에서 SSR이 작동하는 방식

App Router의 SSR은 Pages Router의 getServerSideProps와 같은 짐승이 아닙니다. 실행 모델이 근본적으로 변했습니다.

Next.js 16에서, dynamic = 'force-dynamic'을 설정하거나 Server Component에서 cookies(), headers(), 또는 searchParams를 사용하면, 다음과 같이 Next.js에 말하고 있습니다: "이 페이지는 정적으로 생성될 수 없습니다. 모든 요청에 대해 신선하게 렌더링하세요."

// app/dashboard/page.tsx
import { cookies } from 'next/headers';

export const dynamic = 'force-dynamic';

export default async function Dashboard() {
  const session = await cookies();
  const userId = session.get('userId')?.value;
  const data = await fetchDashboardData(userId);
  
  return <DashboardLayout data={data} />;
}

렌더링 파이프라인은 다음과 같습니다:

  1. 요청이 서버에 도달합니다
  2. Next.js가 RSC 트리를 위에서 아래로 실행합니다
  3. Server Components가 비동기 작업을 해결합니다(데이터 페칭 등)
  4. 렌더링된 RSC 페이로드가 직렬화됩니다
  5. SSR이 이를 초기 응답을 위해 HTML로 변환합니다
  6. 클라이언트가 HTML + RSC 페이로드 + Client Component JS를 받습니다
  7. React가 Client Component 경계만 하이드레이션합니다

단계 3-6은 스트리밍을 통해 발생할 수 있으며, 이를 아래에서 자세히 다루겠습니다.

React Server Components 작동 방식

RSC는 단순히 "서버에서 실행되는 컴포넌트"가 아닙니다. 근본적으로 다른 실행 모델을 나타냅니다.

Server Component가 렌더링되면, 그 출력은 UI의 직렬화된 설명입니다 — JSON 같은 트리 구조와 유사합니다. 이 페이로드는 Server Components의 렌더링된 출력(HTML 같은 노드로)과 Client Components에 대한 참조(모듈 포인터 plus 그들의 직렬화된 props로)를 포함합니다.

이는 다음을 의미합니다:

  • Server Components는 데이터베이스, 파일 시스템, 서버 전용 API에 직접 액세스할 수 있습니다
  • 컴포넌트 수준에서 async/await을 사용할 수 있습니다
  • 그들의 코드, 의존성, 그리고 import는 클라이언트 번들에 절대 나타나지 않습니다
  • useState, useEffect, 또는 브라우저 API를 사용할 수 없습니다
  • Client Components에 함수를 props로 전달할 수 없습니다(함수는 직렬화 불가능합니다)

마지막 요점이 사람들을 자주 따돌립니다. 당신은 이것을 할 수 없습니다:

// ❌ 이것은 에러를 throw합니다
async function ServerParent() {
  const handleClick = () => console.log('clicked');
  return <ClientChild onClick={handleClick} />;
}

핸들러를 Client Component 자체로 이동하거나 Server Actions을 사용해야 합니다.

SSR vs RSC in Next.js 16: A Production Decision Guide - architecture

성능 비교: 실제 프로덕션 수치

Next.js 16.2에서 Pages Router(전통적 SSR)에서 App Router(RSC + SSR)로 마이그레이션하는 동안 세 개의 프로덕션 애플리케이션에 대한 제어된 벤치마크를 실행했습니다. 실제 숫자는 다음과 같습니다.

테스트 환경

  • AWS us-east-1, t3.xlarge 인스턴스
  • Prisma를 통한 PostgreSQL, Redis 캐시 계층
  • 30일 기간 동안 Web Vitals RUM 데이터를 통해 측정됨
  • 3개 앱에 걸쳐 약 230만 월간 페이지 뷰
지표 Pages Router (SSR) App Router (RSC) 변화량
TTFB (p50) 320ms 180ms -43.7%
TTFB (p95) 890ms 410ms -53.9%
FCP (p50) 1.2s 0.8s -33.3%
LCP (p50) 2.1s 1.4s -33.3%
TTI (p50) 3.8s 1.9s -50.0%
INP (p75) 180ms 95ms -47.2%
전송된 총 JS 387KB 142KB -63.3%
하이드레이션 시간 (p50) 450ms 120ms -73.3%

TTI와 하이드레이션 개선이 여기의 헤드라인 수치입니다. 컴포넌트 트리의 70%에 대한 컴포넌트 JavaScript를 더 이상 배포하지 않으면, 브라우저가 할 일이 훨씬 줄어듭니다.

하지만 여기 뉘앙스가 있습니다: TTFB는 RSC 자체 때문이 아니라 스트리밍 때문에 개선되었습니다. App Router가 HTML 응답을 스트리밍하므로, 브라우저가 전체 페이지가 렌더링되기 전에 바이트를 받기 시작합니다. Pages Router에서는 getServerSideProps가 완전히 완료되어야 HTML이 전송되었습니다.

번들 크기 영향

RSC가 가장 밝게 빛나는 부분이며, 가장 많은 오해가 있는 부분입니다.

전통적 SSR 설정에서, 모든 컴포넌트는 하이드레이션을 위해 JavaScript를 클라이언트로 배포합니다 — 컴포넌트가 절대 상호작용하지 않는다고 해도 말입니다. 생각해보세요: 당신의 제품 설명, 당신의 블로그 게시물 본문, 당신의 바닥글 네비게이션. 모든 렌더링 로직이 브라우저로 배포되므로 React가 그것을 "하이드레이션"할 수 있고 서버 HTML이 일치하는지 확인할 수 있습니다.

RSC를 사용하면, 이러한 컴포넌트는 어떤 JavaScript도 배포하지 않습니다.

우리의 전자상거래 클라이언트 중 하나의 경우, 번들이 어떻게 분류되었는지:

컴포넌트 범주 Pages Router 번들 App Router 번들 절감
레이아웃/Chrome 45KB 0KB (Server Component) 100%
제품 표시 38KB 0KB (Server Component) 100%
네비게이션 22KB 8KB (상호작용 부분만) 63.6%
검색 31KB 28KB (대부분 클라이언트) 9.7%
카트/체크아웃 67KB 62KB (대부분 클라이언트) 7.5%
타사 라이브러리 184KB 44KB 76.1%
합계 387KB 142KB 63.3%

그 타사 라이브러리 행은 거대합니다. date-fns, marked, sanitize-html 같은 라이브러리 — Server Components에서만 사용되면, 클라이언트 번들에 0 비용입니다. 우리는 Server Component에서 이미지 처리를 위해 sharp를 사용하는 페이지가 있었습니다. 그것은 1.2MB 라이브러리인데 브라우저가 그 존재조차 알지 못합니다.

스트리밍과 워터폴 패턴

스트리밍은 App Router의 비결 무기이며, 데이터 페칭 워터폴에 대해 생각하는 방식을 근본적으로 변화시킵니다.

이전의 워터폴 문제

Pages Router SSR:

요청 → getServerSideProps (모든 데이터) → 렌더링 → HTML 전송 → JS 다운로드 → 하이드레이션
       |__________ 800ms ___________|   200ms   |__ 0ms __|__ 300ms __|__ 450ms __|

모든 것이 초기 데이터 페칭에 차단됩니다. 3개의 API에서 데이터가 필요하면, getServerSideProps에서 병렬로 실행되거나 워터폴이 있습니다.

Suspense를 포함한 스트리밍

Suspense를 포함한 App Router:

요청 → 셸 렌더링 → HTML 스트리밍 (즉시) → 데이터 섹션 스트리밍 → JS 다운로드 → 하이드레이션 (부분)
      |__ 50ms __|    |_____ 0ms _____|       |____ 진행 중 ____|   |_ 병렬 _|__ 120ms __|

중요한 차이: 브라우저가 즉시 HTML 받기를 시작합니다. Suspense 경계는 페이지의 어떤 부분이 준비될 때 스트리밍으로 들어올지를 정의합니다.

import { Suspense } from 'react';

export default function ProductPage({ params }) {
  return (
    <div>
      {/* 즉시 배포됨 */}
      <Header />
      <ProductHero productId={params.id} />
      
      {/* 준비될 때 스트리밍됨 */}
      <Suspense fallback={<ReviewsSkeleton />}>
        <ProductReviews productId={params.id} />
      </Suspense>
      
      {/* 독립적으로 스트리밍됨 */}
      <Suspense fallback={<RecommendationsSkeleton />}>
        <Recommendations productId={params.id} />
      </Suspense>
    </div>
  );
}

Suspense 경계는 독립적으로 스트리밍됩니다. 추천에 2초가 걸리지만 리뷰에 200ms가 걸리면, 리뷰가 먼저 나타납니다. 사용자는 빈 화면 또는 전체 스켈레톤 대신 점진적 콘텐츠 로딩을 봅니다.

새로운 워터폴 피하기

하지만 RSC는 자신의 고유한 워터폴 위험을 소개합니다. 부모-자식 서버 컴포넌트 데이터 페칭은 순차 워터폴을 만들 수 있습니다:

// ❌ 순차 워터폴
async function Parent() {
  const user = await getUser(); // 200ms
  return <Child userId={user.id} />; // Parent가 해결될 때까지 시작할 수 없음
}

async function Child({ userId }) {
  const orders = await getOrders(userId); // 300ms
  return <OrderList orders={orders} />;
}
// 합계: 500ms

수정은 데이터 페칭을 가능한 한 깊게 밀어내고 병렬 페칭 패턴을 사용하는 것입니다:

// ✅ Suspense를 포함한 병렬
async function Parent() {
  const userPromise = getUser();
  return (
    <>
      <Suspense fallback={<UserSkeleton />}>
        <UserProfile promise={userPromise} />
      </Suspense>
      <Suspense fallback={<OrdersSkeleton />}>
        <UserOrders promise={userPromise} />
      </Suspense>
    </>
  );
}

실제로 작동하는 캐싱 전략

Next.js 16은 커뮤니티가 버전 14와 15의 복잡성에 대해 (타당하게) 불평한 후에 캐싱을 완전히 개편했습니다. 현재 모델이 어떻게 보이는지, SSR vs RSC가 어떻게 작동하는지입니다.

`fetch`를 포함한 요청 수준 캐싱

fetch를 사용하는 Server Components는 요청별로 캐싱을 설정할 수 있습니다:

// 60초 동안 캐시됨 (ISR 동작)
const data = await fetch('https://api.example.com/products', {
  next: { revalidate: 60 }
});

// 캐시 없음, 모든 요청에 대해 신선함 (SSR 동작)
const data = await fetch('https://api.example.com/user/profile', {
  cache: 'no-store'
});

// 온디맨드 재검증을 위해 태그로 캐시됨
const data = await fetch('https://api.example.com/products/123', {
  next: { tags: ['product-123'] }
});

세그먼트 수준 캐싱

단일 페이지 내에서 렌더링 전략을 혼합할 수 있습니다:

// 정적 레이아웃 (빌드에서 캐시됨)
export default function Layout({ children }) {
  return <div><Nav />{children}<Footer /></div>;
}

// 동적 페이지 (모든 요청에 대해 신선함)
export const dynamic = 'force-dynamic';
export default async function Page() { /* ... */ }

캐싱이 까다로워지는 경우

진짜 함정: 라우트 세그먼트의 어떤 컴포넌트가 동적 함수(cookies(), headers(), searchParams)를 사용하면, 전체 세그먼트가 동적이 됩니다. 깊게 중첩된 Server Component에서 하나의 캐시되지 않은 페칭이 전체 페이지를 동적으로 만듭니다.

이것이 우리에게 프로덕션에서 문제가 되었습니다. 우리는 ISR-캐시되어야 하는 제품 페이지가 있었는데, 깊게 중첩된 RecentlyViewed 컴포넌트가 쿠키를 읽고 있었습니다. 전체 페이지가 동적이 되었고, TTFB가 50ms에서 400ms로 뛰어올랐으며, 우리는 2주 동안 알아채지 못했습니다.

수정: 동적 컴포넌트를 Suspense 경계 뒤에 격리하거나 클라이언트 측에서 페칭하는 Client Components로 이동합니다.

의사결정 프레임워크: 각각을 언제 사용할지

3개의 프로덕션 앱을 마이그레이션한 후, 내가 사용하는 의사결정 프레임워크가 있습니다. "SSR vs RSC"보다는 "어떤 렌더링 전략을 어떤 컴포넌트에 대해"에 관한 것입니다.

Server Components(기본값)을 사용하는 경우:

  • 컴포넌트가 데이터를 표시하지만 상호작용이 필요하지 않은 경우
  • 서버 전용 리소스(DB, 파일시스템, 비공개 API)를 사용하는 경우
  • 컴포넌트가 무거운 라이브러리를 import하는 경우(마크다운 파서, 구문 강조자)
  • SEO가 콘텐츠에 대해 중요한 경우(검색 엔진이 완전한 HTML을 얻음)
  • 콘텐츠를 정적으로 분석하거나 캐시할 수 있는 경우

Client Components를 사용하는 경우:

  • useState, useEffect, useRef, 또는 다른 React 훅이 필요한 경우
  • 브라우저 API(localStorage, geolocation, IntersectionObserver)가 필요한 경우
  • 이벤트 핸들러(onClick, onChange, onSubmit)가 필요한 경우
  • 브라우저 컨텍스트가 필요한 타사 라이브러리를 사용하는 경우
  • 실시간 업데이트(WebSocket, polling)가 필요한 경우

SSR(force-dynamic)을 사용하는 경우:

  • 콘텐츠가 사용자/세션별로 개인화된 경우
  • 데이터가 ISR에 너무 자주 변경되는 경우
  • 요청 시간 정보(인증 상태, 지역 위치 헤더)가 필요한 경우
  • SEO가 여전히 서버 렌더링 HTML을 필요로 하는 경우

정적 생성을 사용하는 경우:

  • 콘텐츠가 거의 변경되지 않는 경우(마케팅 페이지, 문서, 블로그 게시물)
  • 성능이 중요한 경우(CDN 엣지에서 캐시됨)
  • 모든 사용자에 대해 콘텐츠가 같은 경우

대부분의 경우 우리는 대략 다음의 비율로 끝나게 됩니다: 60% Server Components(정적), 20% Server Components(동적/SSR), 15% Client Components, 그리고 5% Suspense 경계를 포함한 혼합 패턴.

Pages Router에서의 마이그레이션 패턴

기존 Next.js 앱을 마이그레이션하는 중이면, 모든 것을 한 번에 변환하려 하지 마세요. 그것이 극적으로 실패하는 것을 봤습니다. 작동하는 증분 접근 방식은 다음입니다:

단계 1: 공존

Next.js 16은 pages/app/ 디렉토리를 동시에 지원합니다. app/에서 새로운 라우트를 시작하고 기존 라우트는 그대로 두세요.

단계 2: 레이아웃 마이그레이션

먼저 레이아웃을 이동하세요. _app.tsx_document.tsxapp/layout.tsx가 됩니다. 보통 가장 쉬운 승리입니다 — 레이아웃은 완벽한 Server Components입니다.

단계 3: 정적 페이지 먼저

가장 간단한 정적 페이지를 마이그레이션합니다. 마케팅 페이지, about 페이지, 블로그 게시물. 이것들은 간단한 Server Component 변환입니다.

단계 4: 동적 페이지

getServerSideProps를 사용하는 페이지를 변환합니다. 이곳이 가장 많은 마찰이 있을 것입니다. 특히 데이터 페칭 패턴과 인증 주변입니다.

단계 5: 클라이언트 상호작용

상호작용 아일랜드를 Client Components로 추출합니다. 이것이 가장 어려운 부분입니다 — 최소 클라이언트 경계를 식별해야 합니다.

// 이전: Pages Router에서 모든 것이 기본값으로 "클라이언트"였습니다
// 이후: 명시적 경계

// app/products/[id]/page.tsx (Server Component)
export default async function ProductPage({ params }) {
  const product = await getProduct(params.id);
  return (
    <article>
      <h1>{product.name}</h1>
      <ProductGallery images={product.images} /> {/* 클라이언트 */}
      <div dangerouslySetInnerHTML={{ __html: product.description }} /> {/* 서버 */}
      <PricingWidget product={product} /> {/* 클라이언트 */}
      <Suspense fallback={<Skeleton />}>
        <RelatedProducts categoryId={product.categoryId} /> {/* 서버 */}
      </Suspense>
    </article>
  );
}

마이그레이션 전략을 계획할 도움이 필요하면, 우리 팀이 이것을 충분히 많이 해서 지뢰가 어디인지 알고 있습니다 — 연락주세요하고 당신의 특정 아키텍처를 통해 이야기할 수 있습니다.

기술 SEO 함의

JavaScript 렌더링을 검색 엔진이 어떻게 처리하는지 12년 이상 지켜본 저로서, RSC 모델은 SSR 자신 이후로 기술 SEO에 일어난 최고의 일입니다.

이유는 다음과 같습니다:

Server Components는 서버에서 완전한 HTML을 렌더링합니다. Googlebot은 어떤 JavaScript도 실행하지 않고 전체 콘텐츠를 얻습니다. 이것은 새로운 것이 아닙니다 — SSR도 이렇게 했습니다. 하지만 RSC는 훨씬 적은 클라이언트 측 JavaScript로 이것을 수행하며, 이는 Core Web Vitals에 직접적으로 영향을 미칩니다.

Google은 INP(상호작용 다음 페인트)가 2024년 3월부터 순위 신호임을 확인했습니다. 우리의 프로덕션 데이터는 RSC 헤비한 페이지가 동등한 SSR 페이지보다 INP에서 47% 더 나은 점수를 얻는 것으로 보여줍니다. 더 적은 JavaScript = 더 적은 메인 스레드 경합 = 더 나은 INP.

스트리밍은 크롤 동작에 영향을 미칩니다. Googlebot은 2023년부터 HTTP 스트리밍을 지원하지만, 타임아웃이 있습니다. 만약 당신의 가장 느린 Suspense 경계가 15초 걸리면, Googlebot이 대기하지 않을 수 있습니다. 중요한 SEO 콘텐츠를 Suspense 경계 외부에 두거나, Suspense 폴백이 의미 있는 콘텐츠를 포함하는지 확인하세요.

SEO가 주요 관심사인 클라이언트의 경우, 우리는 종종 App Router와 쌍을 이루는 우리의 헤드리스 CMS 개발 접근 방식을 추천합니다 — 콘텐츠는 CMS에 있고, Server Components를 통해 렌더링되며, 불필요한 JavaScript를 브라우저에 배포하지 않습니다. 검색 성능을 위한 모든 세계의 최고입니다.

Astro는 귀하의 사이트가 주로 콘텐츠 기반이고 최소한의 상호작용이 있는 경우에도 고려할 가치가 있습니다. 하지만 풍부한 상호작용 기능이 있는 애플리케이션의 경우, Next.js 16과 RSC가 甘美한 자리에 맞춥니다.

FAQ

SSR과 Next.js 16의 RSC 사이의 차이는 무엇입니까?

SSR (Server Side Rendering)은 페이지 HTML이 생성되는 시간을 결정하는 렌더링 전략입니다 — 모든 요청에서, 서버에서. React Server Components(RSC)는 어떤 코드가 브라우저로 배포되는지를 결정하는 컴포넌트 유형입니다. App Router에서 함께 작동합니다: RSC는 어떤 것이 클라이언트 JavaScript를 필요로 하는지 정의하고, SSR은 HTML 생성을 처리합니다. 당신은 일반적으로 둘 다 동시에 사용하고 있습니다.

React Server Components가 Server Side Rendering을 대체합니까?

아닙니다. RSC와 SSR은 경쟁하지 않으며, 상호보완합니다. Next.js 16의 App Router에서, 모든 페이지는 초기 HTML 응답을 위해 SSR을 사용합니다. RSC는 그 페이지 내에서 어떤 컴포넌트가 하이드레이션을 위해 JavaScript를 클라이언트로 보내야 하는지 결정합니다. 완전히 SSR된 페이지가 있을 수 있습니다 (클라이언트 JS 없음)은 모두 Server Components로 만들어졌거나 둘의 혼합입니다.

React Server Components는 번들 크기를 얼마나 줄입니까?

우리의 프로덕션 측정에서, RSC 기반 App Router 페이지는 동등한 Pages Router 구현에 비해 평균 63% 작은 JavaScript 번들을 기록했습니다. 절감은 당신의 컴포넌트 트리에 크게 달려 있습니다 — 많은 표시 전용 콘텐츠가 있는 페이지가 가장 큰 이득을 보고, 매우 상호작용적인 페이지(대시보드, 편집기)는 더 작은 개선을 봅니다.

기존 Next.js 앱을 App Router로 마이그레이션해야 합니까?

당신의 문제점에 달려 있습니다. 당신의 Core Web Vitals가 큰 JavaScript 번들로 인해 손상되거나, TTFB가 순차 데이터 페칭으로 인해 높으면, 마이그레이션이 그만한 가치가 있습니다. 당신의 Pages Router 앱이 잘 작동하고 당신의 팀이 생산적이면, 긴급함이 없습니다. Next.js는 두 라우터를 동시에 지원하므로, 점진적으로 마이그레이션할 수 있습니다.

Next.js 16의 Server Components에서 캐싱은 어떻게 작동합니까?

Next.js 16은 캐싱 모델을 크게 단순화했습니다. Server Components는 정적으로 캐시될 수 있습니다(정적 데이터의 기본값), 시간 기반으로 재검증되거나(ISR), 모든 요청별로 신선하게 렌더링될 수 있습니다(동적). next: { revalidate }로 페칭 수준에서 또는 export const dynamic으로 라우트 세그먼트 수준에서 이를 제어합니다. 조심하세요: 세그먼트에서 하나의 동적 함수가 전체 세그먼트를 동적으로 만듭니다.

Server Components는 SEO에 영향을 미칩니까?

Server Components는 SEO에 탁월합니다. 그들은 서버에서 완전한 HTML을 렌더링하므로, 검색 엔진이 JavaScript를 실행하지 않고 인덱스할 수 있습니다. 추가적으로, 감소된 클라이언트 측 JavaScript는 Core Web Vitals 점수를 개선하며, 특히 INP와 TTI는 순위 신호입니다. 한 가지 주의: Suspense 경계 뒤의 콘텐츠는 점진적으로 스트리밍되므로, 중요한 SEO 콘텐츠가 느린 데이터 페칭 뒤에 있지 않은지 확인하세요.

React Server Components를 헤드리스 CMS와 함께 사용할 수 있습니까?

절대적으로 — 이것은 최고의 쌍 중 하나입니다. Server Components는 API 키나 CMS SDK 코드를 클라이언트에 노출하지 않고 컴포넌트 수준에서 직접 CMS 콘텐츠를 페칭할 수 있습니다. Contentful SDK, Sanity 클라이언트, 또는 Prismic의 @prismicio/client 같은 라이브러리는 서버에 완전히 있습니다. 웹훅을 통한 ISR 또는 온디맨드 재검증과 결합하면, 빠르고 캐시 가능한 페이지를 얻으면서 브라우저로 불필요한 클라이언트 JavaScript를 배포하지 않습니다.

프로덕션에서 RSC 사용할 때 가장 큰 함정은 무엇입니까?

내가 부딪힌 세 가지 가장 큰 문제: (1) 중첩된 Server Components에서 실수로 워터폴 데이터 페칭 — React DevTools와 서버 타이밍 헤더로 프로필하고 수정합니다. (2) 중첩된 컴포넌트에서 cookies() 또는 headers()를 사용하여 캐시된 페이지를 실수로 동적으로 만들기. (3) Server에서 Client Components로 직렬화 불가능한 데이터(함수, 클래스 인스턴스, Dates)를 전달할 때 prop 직렬화 오류. 좋은 린팅 규칙과 컴포넌트 경계 컨벤션을 초반에 구축하세요.