특별한 파일인 loading.js
가 React Suspense
와 함께 의미있는 로딩 UI를 만들 수 있도록 도와줍니다. 이 방식을 사용하면 라우트 세그먼트의 콘텐츠가 로드되는 동안 서버로부터 일시적인 로드 상태(instant loading state)를 보여줄 수 있습니다. 해당 콘텐츠의 렌더링이 끝나면 새 콘텐츠는 자동으로 교체됩니다.
일시적인 로드 상태(instant loading state)는 페이지 탐색 시 즉시 표시되는 대체 UI입니다. 스켈레톤 및 스피너와 같은 로딩 표시기 또는 표지 사진, 제목 등과 같이 이후 화면의 작지만 의미 있는 부분을 미리 렌더링할 수 있습니다. 이를 통해 사용자는 애플리케이션이 응답하고 있음을 이해하고 더 나은 사용자 경험을 제공할 수 있습니다.
폴더 안에 loading.js
파일을 추가하여 로딩 상태를 만듭니다.
/* app/dashboard/loading.js */
export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}
같은 폴더에서 loading.js
는 layout.js
내부에 들어가게됩니다. 그리고 loading.js
는 Suspense 경계에서 page.js
파일과 그 아래의 모든 자식을 자동으로 감쌉니다.
loading.js 외에도 자체 UI 컴포넌트에 대한 Suspense 경계를 직접 생성할 수도 있습니다. app 라우터는 Node.js 및 Edge 런타임 모두에 대해 Suspense를 사용한 Streaming
을 지원합니다.
React와 Next.js에서 Streaming이 작동하는 방식을 알아보려면 SSR(Server-Side Rendering)
과 그 한계점을 이해하는 것이 좋습니다.
SSR에서 사용자가 페이지를 보고 상호 작용하기 전에 완료해야 하는 일련의 단계가 있습니다.
hydrate
(수화)시켜 대화형으로 만듭니다.이러한 단계는 순차적입니다. 즉, 모든 데이터를 가져온 후에만 서버가 페이지의 HTML을 렌더링할 수 있습니다. 그리고 클라이언트에서 React는 페이지의 모든 구성 요소에 대한 코드가 다운로드된 후에만 UI를 hydrate
할 수 있습니다.
React와 Next.js를 사용하는 SSR은 가능한 한 빨리 사용자에게 비대화형 페이지를 표시하여 체감되는 로딩 성능을 개선하는데 도움을 줍니다.
그러나 페이지가 사용자에게 표시되기 전에 서버에서 모든 데이터 fetch를 완료해야 하므로 속도가 여전히 느릴 수 있습니다.
Streaming
을 사용하면 페이지의 HTML을 더 작은 청크로 분해하고 해당 청크를 서버에서 클라이언트로 점진적으로 보낼 수 있습니다.
이렇게 하면 UI가 렌더링되기 전에 모든 데이터가 로드될 때까지 기다리지 않고 페이지의 일부를 더 빨리 표시할 수 있습니다.
Streaming
은 각 컴포넌트를 청크로 생각할 수 있는 React의 컴포넌트 모델에서 잘 작동합니다. 우선 순위가 더 높거나(예: 제품 정보) 데이터에 의존하지 않는 컴포넌트(예: 레이아웃)를 먼저 보낼 수 있으며 React는 더 일찍 수화를 시작할 수 있습니다. 우선 순위가 낮은 컴포넌트(예: 리뷰, 관련 제품)는 데이터 fetch가 끝난 뒤 동일한 서버 요청으로 보낼 수 있습니다.
스트리밍은 TTFB(Time To First Byte)
및 FCP(First Contentful Paint)
를 줄일 수 있으므로 긴 데이터 요청으로 인해 페이지 렌더링이 차단되지 않도록 하려는 경우에 특히 유용합니다. 또한 특히 느린 장치에서 TTI(Time to Interactive)
를 개선하는 데 도움이 됩니다.
<Suspense>
는 비동기 작업(예: 데이터 가져오기)을 수행하는 컴포넌트를 래핑하고 작업이 진행되는 동안 fallback(대체) UI(예: 스켈레톤, 스피너)를 표시한 다음 작업이 완료되면 컴포넌트를 교체하는 방식으로 작동합니다.
/* app/dashboard/page.js */
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
Suspense
를 사용하면 다음과 같은 이점을 얻을 수 있습니다.
Streaming 서버 렌더링
선택적 수화
Next.js는 UI를 클라이언트로 Streaming하기 전에 generateMetadata
내에서 데이터 fetch가 완료될 때까지 기다립니다. 이는 Streaming 응답의 첫 번째 부분에 <head>
태그가 포함되는 것이 보장됩니다.
Streaming은 서버 렌더링이므로 SEO에 영향을 미치지 않습니다. Google의 모바일 친화성 테스트 도구를 사용하여 페이지가 Google 웹 크롤러에 어떻게 표시되는지 확인하고 직렬화된 HTML을 볼 수 있습니다.
원문 - https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming