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

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

export default function Loading() {
// Loading 내부에 Skeleton을 포함한 모든 UI를 추가할 수 있습니다.
return <LoadingSkeleton />;
}
export default function Loading() {
// Loading 내부에 Skeleton을 포함한 모든 UI를 추가할 수 있습니다.
return <LoadingSkeleton />;
}
같은 폴더에서 loading.js
는 layout.js
내부에 중첩됩니다. 이는 자동으로 page.js
파일과 그 아래의 모든 하위 요소를 <Suspense>
경계로 감싸게 됩니다.

알아두면 좋은 점:
- 서버 중심 라우팅을 사용하더라도 네비게이션은 즉각적입니다.
- 네비게이션은 중단 가능합니다. 즉, 다른 라우트로 이동할 때 현재 라우트의 콘텐츠가 완전히 로드될 때까지 기다릴 필요가 없습니다.
- 새로운 라우트 세그먼트가 로드되는 동안 공유 레이아웃은 계속 상호작용 가능한 상태로 유지됩니다.
권장사항: Next.js가 이 기능을 최적화하므로 라우트 세그먼트(레이아웃 및 페이지)에
loading.js
규칙을 사용하세요.
Suspense를 사용한 스트리밍
loading.js
외에도 자체 UI 컴포넌트에 대해 수동으로 Suspense 경계를 만들 수 있습니다. App Router는 Node.js 및 Edge 런타임 모두에 대해 Suspense를 사용한 스트리밍을 지원합니다.
알아두면 좋은 점:
- 일부 브라우저는 스트리밍 응답을 버퍼링합니다. 응답이 1024바이트를 초과할 때까지 스트리밍된 응답이 보이지 않을 수 있습니다. 이는 일반적으로 "hello world" 애플리케이션에만 영향을 미치며 실제 애플리케이션에는 영향을 미치지 않습니다.
스트리밍이란 무엇인가?
React와 Next.js에서 스트리밍이 어떻게 작동하는지 이해하려면 서버 사이드 렌더링(SSR) 과 그 한계를 이해하는 것이 도움이 됩니다.
SSR을 사용하면 사용자가 페이지를 보고 상호작용할 수 있기 전에 완료해야 할 일련의 단계가 있습니다:
- 먼저 주어진 페이지에 대한 모든 데이터를 서버에서 가져옵니다.
- 그런 다음 서버는 페이지의 HTML을 렌더링합니다.
- 페이지의 HTML, CSS, JavaScript가 클라이언트로 전송됩니다.
- 생성된 HTML과 CSS를 사용하여 비대화형 사용자 인터페이스가 표시됩니다.
- 마지막으로 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(예: 스켈레톤, 스피너)를 표시한 다음 작업이 완료되면 컴포넌트를 교체하는 방식으로 작동합니다.
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>
);
}
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를 사용하면 다음과 같은 이점을 얻을 수 있습니다:
- 스트리밍 서버 렌더링 - 서버에서 클라이언트로 HTML을 점진적으로 렌더링합니다.
- 선택적 하이드레이션 - React는 사용자 상호작용을 기반으로 어떤 컴포넌트를 먼저 대화형으로 만들지 우선순위를 정합니다.
더 많은 Suspense 예제와 사용 사례는 React 문서를 참조하세요.
SEO
- Next.js는 클라이언트에 UI를 스트리밍하기 전에
generateMetadata
내부의 데이터 가져오기가 완료될 때까지 기다립니다. 이는 스트리밍된 응답의 첫 부분에<head>
태그가 포함되도록 보장합니다. - 스트리밍은 서버에서 렌더링되므로 SEO에 영향을 미치지 않습니다. Google의 Rich Results Test 도구를 사용하여 페이지가 Google 웹 크롤러에 어떻게 나타나는지 확인하고 직렬화된 HTML을 볼 수 있습니다(출처).
상태 코드
스트리밍 시 요청이 성공적이었음을 나타내기 위해 200
상태 코드가 반환됩니다.
서버는 여전히 스트리밍된 콘텐츠 내에서 클라이언트에 오류나 문제를 전달할 수 있습니다. 예를 들어, redirect
나 notFound
를 사용할 때 그렇습니다. 응답 헤더가 이미 클라이언트로 전송되었기 때문에 응답의 상태 코드를 업데이트할 수 없습니다. 이는 SEO에 영향을 미치지 않습니다.