[Next.js] 스트리밍
데이터 페치가 오래 걸리면 컴포넌트가 늦게 렌더링되어 사용자는 UI를 볼 수 없습니다.
이 문제를 해결하기 위해 데이터를 불러오는 동안 플레이스홀더 UI를 보여주는 것을 스트리밍이라고 합니다.
페이지 전체를 스트리밍하는 방법과, 특정 컴포넌트를 스트리밍 하는 방법이 있습니다.
페이지 전체 스트리밍
페이지와 같은 레벨에 loading.tsx
파일을 생성합니다. 그러면 페이지에서 데이터를 불러오는 동안 스트리밍이 자동으로 적용됩니다.
하지만, 이는 하위 페이지에도 모두 적용됩니다. 이를 방지하기 위해 라우트 그룹을 사용할 수 있습니다.
// /dashboard, /dashboard/customers, /dashboard/invoices 세 경로 모두 스트리밍이 적용됨
app
ㄴ dashboard
ㄴ loading.tsx
ㄴ page.tsx
ㄴ customers
ㄴ page.tsx
ㄴ invoices
ㄴ page.tsx
// /dashboard 경로에만 스트리밍이 적용됨
app
ㄴ dashboard
ㄴ (overview)
ㄴ loading.tsx
ㄴ page.tsx
ㄴ customers
ㄴ page.tsx
ㄴ invoices
ㄴ page.tsx
컴포넌트 스트리밍
특정 컴포넌트를 렌더링하기 위한 데이터를 가져오는 속도가 느려 전체 페이지 로드가 느려지는 경우, 그 컴포넌트를 위한 데이터만 스트리밍할 수도 있습니다.
이를 위해 Suspense
를 사용합니다.
다음과 같은 코드가 있다고 가정합니다.
export default async function Page() {
// 느린 데이터 페칭
const invoices = await getInvoices();
// ...
return <LatestInvoices invoices={invoices} />
}
export default async function LatestInvoices({ invoices }: { invoices: Invoice[] }) {
return {invoices.map(...)}
}
위 코드에서 LatestInvoices
컴포넌트를 스트리밍하려고 합니다.
가장 먼저 데이터를 가져오는 코드, 데이터를 전달하는 코드를 제거합니다. (물론 현재 오류가 발생합니다.)
export default async function Page() {
return <LatestInvoices />
}
LatestInvoices
컴포넌트가 더이상 props를 받지 않으므로 이를 제거합니다.
export default async function LatestInvoices() {
return {invoices.map(...)}
}
이제 해당 컴포넌트에서 데이터를 가져옵니다.
export default async function LatestInvoices() {
const invoices = await getInvoices();
return {invoices.map(...)}
}
해당 컴포넌트를 렌더링할 때 Suspense
를 추가합니다.
export default async function Page() {
return (
<Suspense fallback={<LatestInvoicesSkeleton />}>
<LatestInvoices />
</Suspense>
)
}
즉, 데이터를 페치하는 코드를 컴포넌트 내부에 이동시키고, 컴포넌트를 렌더링할 때 Suspense
로 감싸면 됩니다.