문서
App Router 사용하기
로딩 UI 및 스트리밍

로딩 UI 및 스트리밍

특별한 파일 loading.jsReact Suspense를 사용하여 의미 있는 로딩 UI를 만드는 데 도움을 줍니다. 이 규칙을 사용하면 라우트 세그먼트의 콘텐츠가 로드되는 동안 서버에서 즉각적인 로딩 상태를 표시할 수 있습니다. 렌더링이 완료되면 새 콘텐츠가 자동으로 교체됩니다.

로딩 UI

즉각적인 로딩 상태

즉각적인 로딩 상태는 네비게이션 시 즉시 표시되는 대체 UI(Fallback UI)입니다. 스켈레톤과 스피너와 같은 로딩 인디케이터를 미리 렌더링하거나, 커버 사진, 제목 등과 같은 향후 화면의 작지만 의미 있는 부분을 미리 렌더링할 수 있습니다. 이는 사용자가 앱이 응답하고 있음을 이해하는 데 도움이 되며 더 나은 사용자 경험을 제공합니다.

폴더 내에 loading.js 파일을 추가하여 로딩 상태를 생성합니다.

loading.js 특별 파일
app/dashboard/loading.tsx
export default function Loading() {
  // Loading 내부에 Skeleton을 포함한 모든 UI를 추가할 수 있습니다.
  return <LoadingSkeleton />;
}
app/dashboard/loading.js
export default function Loading() {
  // Loading 내부에 Skeleton을 포함한 모든 UI를 추가할 수 있습니다.
  return <LoadingSkeleton />;
}

같은 폴더에서 loading.jslayout.js 내부에 중첩됩니다. 이는 자동으로 page.js 파일과 그 아래의 모든 하위 요소를 <Suspense> 경계로 감싸게 됩니다.

loading.js 개요

알아두면 좋은 점:

  • 서버 중심 라우팅을 사용하더라도 네비게이션은 즉각적입니다.
  • 네비게이션은 중단 가능합니다. 즉, 다른 라우트로 이동할 때 현재 라우트의 콘텐츠가 완전히 로드될 때까지 기다릴 필요가 없습니다.
  • 새로운 라우트 세그먼트가 로드되는 동안 공유 레이아웃은 계속 상호작용 가능한 상태로 유지됩니다.

권장사항: Next.js가 이 기능을 최적화하므로 라우트 세그먼트(레이아웃 및 페이지)에 loading.js 규칙을 사용하세요.

Suspense를 사용한 스트리밍

loading.js 외에도 자체 UI 컴포넌트에 대해 수동으로 Suspense 경계를 만들 수 있습니다. App Router는 Node.js 및 Edge 런타임 모두에 대해 Suspense를 사용한 스트리밍을 지원합니다.

알아두면 좋은 점:

  • 일부 브라우저는 스트리밍 응답을 버퍼링합니다. 응답이 1024바이트를 초과할 때까지 스트리밍된 응답이 보이지 않을 수 있습니다. 이는 일반적으로 "hello world" 애플리케이션에만 영향을 미치며 실제 애플리케이션에는 영향을 미치지 않습니다.

스트리밍이란 무엇인가?

React와 Next.js에서 스트리밍이 어떻게 작동하는지 이해하려면 서버 사이드 렌더링(SSR) 과 그 한계를 이해하는 것이 도움이 됩니다.

SSR을 사용하면 사용자가 페이지를 보고 상호작용할 수 있기 전에 완료해야 할 일련의 단계가 있습니다:

  1. 먼저 주어진 페이지에 대한 모든 데이터를 서버에서 가져옵니다.
  2. 그런 다음 서버는 페이지의 HTML을 렌더링합니다.
  3. 페이지의 HTML, CSS, JavaScript가 클라이언트로 전송됩니다.
  4. 생성된 HTML과 CSS를 사용하여 비대화형 사용자 인터페이스가 표시됩니다.
  5. 마지막으로 React가 사용자 인터페이스를 하이드레이션하여 대화형으로 만듭니다.
스트리밍 없는 서버 렌더링을 보여주는 차트

이러한 단계는 순차적으로 이루어지며, 각 단계가 완료될 때까지 다음 단계로 넘어갈 수 없습니다. 즉, 서버는 모든 데이터가 가져와진 후에만 페이지의 HTML을 렌더링할 수 있습니다. 그리고 클라이언트에서는 페이지의 모든 컴포넌트에 대한 코드가 다운로드된 후에만 React가 UI를 하이드레이트할 수 있습니다.

React와 Next.js를 사용한 SSR은 가능한 한 빨리 사용자에게 비대화형 페이지를 보여줌으로써 인지된 로딩 성능을 향상시키는 데 도움이 됩니다.

스트리밍 없는 서버 렌더링

그러나 페이지를 사용자에게 보여줄 수 있기 전에 서버의 모든 데이터 가져오기가 완료되어야 하므로 여전히 느릴 수 있습니다.

스트리밍을 사용하면 페이지의 HTML을 더 작은 청크로 나누고 이러한 청크를 서버에서 클라이언트로 점진적으로 전송할 수 있습니다.

스트리밍을 사용한 서버 렌더링 작동 방식

이를 통해 모든 데이터가 로드되기를 기다리지 않고도 페이지의 일부를 더 빨리 표시할 수 있습니다.

스트리밍은 각 컴포넌트를 청크로 간주할 수 있기 때문에 React의 컴포넌트 모델과 잘 작동합니다. 우선순위가 높은 컴포넌트(예: 제품 정보) 또는 데이터에 의존하지 않는 컴포넌트(예: 레이아웃)를 먼저 보낼 수 있으며, React는 더 일찍 하이드레이션을 시작할 수 있습니다. 우선순위가 낮은 컴포넌트(예: 리뷰, 관련 제품)는 데이터가 가져와진 후 동일한 서버 요청에서 전송될 수 있습니다.

스트리밍을 사용한 서버 렌더링을 보여주는 차트

스트리밍은 특히 긴 데이터 요청이 페이지 렌더링을 차단하는 것을 방지하고자 할 때 유용합니다. Time To First Byte (TTFB)First Contentful Paint (FCP)를 줄일 수 있기 때문입니다. 또한 특히 느린 기기에서 Time to Interactive (TTI)를 개선하는 데 도움이 됩니다.

예시

<Suspense>는 비동기 작업(예: 데이터 가져오기)을 수행하는 컴포넌트를 감싸고, 작업이 진행되는 동안 대체 UI(예: 스켈레톤, 스피너)를 표시한 다음 작업이 완료되면 컴포넌트를 교체하는 방식으로 작동합니다.

app/dashboard/page.tsx
import { Suspense } from "react";
import { PostFeed, Weather } from "./Components";
 
export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>피드 로딩 중...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>날씨 로딩 중...</p>}>
        <Weather />
      </Suspense>
    </section>
  );
}
app/dashboard/page.js
import { Suspense } from "react";
import { PostFeed, Weather } from "./Components";
 
export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>피드 로딩 중...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>날씨 로딩 중...</p>}>
        <Weather />
      </Suspense>
    </section>
  );
}

Suspense를 사용하면 다음과 같은 이점을 얻을 수 있습니다:

  1. 스트리밍 서버 렌더링 - 서버에서 클라이언트로 HTML을 점진적으로 렌더링합니다.
  2. 선택적 하이드레이션 - React는 사용자 상호작용을 기반으로 어떤 컴포넌트를 먼저 대화형으로 만들지 우선순위를 정합니다.

더 많은 Suspense 예제와 사용 사례는 React 문서를 참조하세요.

SEO

  • Next.js는 클라이언트에 UI를 스트리밍하기 전에 generateMetadata 내부의 데이터 가져오기가 완료될 때까지 기다립니다. 이는 스트리밍된 응답의 첫 부분에 <head> 태그가 포함되도록 보장합니다.
  • 스트리밍은 서버에서 렌더링되므로 SEO에 영향을 미치지 않습니다. Google의 Rich Results Test 도구를 사용하여 페이지가 Google 웹 크롤러에 어떻게 나타나는지 확인하고 직렬화된 HTML을 볼 수 있습니다(출처).

상태 코드

스트리밍 시 요청이 성공적이었음을 나타내기 위해 200 상태 코드가 반환됩니다.

서버는 여전히 스트리밍된 콘텐츠 내에서 클라이언트에 오류나 문제를 전달할 수 있습니다. 예를 들어, redirectnotFound를 사용할 때 그렇습니다. 응답 헤더가 이미 클라이언트로 전송되었기 때문에 응답의 상태 코드를 업데이트할 수 없습니다. 이는 SEO에 영향을 미치지 않습니다.