특수 파일인 loading.js
는 리액트 서스펜스 기능과 함께 의미있는 로딩 UI를 만드는데 도와줍니다.
해당 컨벤션 내에선 라우트 세그먼트의 컨텐츠가 로딩 되는 동안 서버에서 즉시 로딩 스테이트가 보이게 됩니다.
새로운 컨텐츠는 렌더링이 완료 되면 로딩UI와 바로 스왑 됩니다.
즉시 로딩 스테이트(Instant Loading States)는 네비게이션시 바로 보이는 fallback UI입니다.
스켈레톤 혹은 스피너 같은 로딩 인디케이터를 프리-렌더링 하거나 커버 이미지, 제목 같은 작지만 미래에 렌더링 될 스크린에 의미있는 요소들을 프리-렌더링 합니다.
이를 통해 유저들은 앱이 즉각적으로 반응한다고 생각하고 더 좋은 UX를 제공할 수 있습니다.
loading.js
파일을 추가하여 로딩 스테이트를 만들어 보세요.
export default function Loading() {
// 스켈레톤 같은 어떤 UI도 로딩 스테이트로 사용할 수 있습니다.
return <LoadingSkeleton />
}
동일 폴더 내에서 loading.js
파일은 layout.js
파일 내부에 포함 됩니다.
loading.js
파일은 자동으로 page.js
파일을 자식 컴포넌트로 가지게 되고 page.js
내부의 컴포넌트들은 전부 <Suspense>
경계 내부에 들어오게 됩니다.
참고 사항:
- 서버 중심 라우팅이지만 네비게이션은 즉시 실행 됩니다.
- 네비게이션은 중단 가능합니다. 이는 다른 라우트로 이동하는 과정에서 변경 되는 라우트의 컨텐츠가 전부 로딩 될 때까지 기다릴 필요가 없다는 의미입니다.
- 공유 레이아웃은 새로운 라우트 세그먼트가 로딩 되기 전에도 인터랙티브함을 유지합니다.
추천 사항:
Next.js에서 자동으로 최적화를 진행해 주기 때문에loading.js
파일 컨벤션을 사용하여 레이아웃,페이지 같은 라우트 세그먼트에 로딩UI를 적용하세요.
loading.js
와 더불어 수동으로 커스텀 UI 컴포넌트를 위한 커스텀 서스펜스 바운더리를 생성할 수 있습니다.
App Router는 Node.js와 Edge runtimes에서 스트리밍과 서스펜스를 지원하고 있습니다.
리액트와 Next.js에서 어떻게 스트리밍이 동작하는지 알아보기 위해 서버 사이드 렌더링(SSR)과 한계점에 대해 이해할 필요가 있습니다.
SSR을 사용하면 유저가 페이지를 보고 인터랙션을 할 수 있을 때까지 완료 되어야 하는 몇가지 단계가 있습니다.
각각의 단계들은 모두 절차대로 진행 되며 동기적으로 처리 됩니다.
다르게 표현하자면 서버는 필요한 데이터가 전부 모여있을 때에만 HTML을 렌더링 할 수 있습니다.
클라이언트 쪽에서 리액트는 페이지에 필요한 모든 컴포넌트가 다운로드 된 이후에 하이드레이션을 진행할 수 있습니다.
리액트와 Next.js에서 사용하는 SSR은 유저에게 스태틱한 UI를 되도록 빠르게 보여주기 때문에 로딩 퍼포먼스를 향상시키는데 도움을 줍니다.
그러나 페이지가 유저에게 보여지기 전에 서버에서 필요한 모든 데이터가 패치되어야 하기 때문에 여전히 느립니다.
스트리밍은 페이지의 HTML을 작은 청크로 분해하고 서버에서 클라이언트로 해당 청크들을 점진적으로 보낼 수 있도록 합니다.
스트리밍을 통해 페이지의 일부분을 UI가 렌더링 되기 전에 모든 데이터가 패치 되길 기다릴 필요 없이 더 빠르게 보여줄 수 있습니다.
스트리밍은 리액트 컴포넌트 모델과 잘 맞아 떨어지는데, 그 이유는 각각의 컴포넌트를 청크로 간주할 수 있기 때문입니다.
제품 정보와 같은 높은 우선순위를 가지고 있거나 레이아웃 같이 데이터에 의존하지 않는 컴포넌트는 먼저 서버에서 보낼 수 있고 리액트는 하이드레이션 작업을 더 빨리 진행할 수 있습니다.
리뷰, 관련 상품과 같은 낮은 우선순위를 가진 컴포넌트는 데이터가 패치 된 이후 서버 리퀘스트에 따라 서버에서 보내질 수 있습니다.
스트리밍은 페이지의 오래 걸리는 데이터 리퀘스트로 인해 블로킹이 발생하는 걸 막고싶을 때 더 효율적이며 TTFB(Time To First Byte)와 FCP(First Contentful Paint)를 줄여줄 수 있습니다.
성능이 낮아 느린 디바이스에선 TTI(Time To Interactive)도 개선할 수 있습니다.
<Suspense>
컴포넌트는 데이터를 패치하는 비동기 작업이나 스켈레톤, 스피너 같은 fallback UI를 보여주는 컴포넌트를 감싸서 해당 동작을 실행 하는 동안 작동 되며 동작이 완료 된 이후엔 원래 컴포넌트로 스왑시켜주게 됩니다.
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>
)
}
서스펜스를 사용하면 아래의 장점을 얻을 수 있습니다.
서스펜스에 대한 예제와 사용 케이스에 대해 더 알고 싶다면 리액트 문서를 확인해 보세요.
generateMetadata
함수가 데이터를 패치하여 완료할때 까지 기다립니다. 이는 첫 스트리밍 결과물이 <head>
태그를 포함할 수 있도록 하기 위함입니다.