본문 바로가기
C.W.K.
Stream
Lesson 07 of 09 · published

스트리밍과 Suspense로 먼저 보여 주기

~22 min · streaming, Suspense, loading

Level 0호기심
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

첫 바이트가 가장 느린 데이터에 묶이지 않게 해

스트리밍이 없으면 서버는 페이지에 필요한 모든 데이터가 준비될 때까지 HTML의 첫 바이트도 보내지 못해. 스트리밍을 쓰면 공통 셸과 빠른 부분을 먼저 보내고, 느린 컴포넌트는 데이터가 풀리는 순서대로 뒤이어 보낼 수 있어.

방법경계의 크기
loading.tsx라우트 구간 전체를 프레임워크가 Suspense로 감싸.
<Suspense fallback>개발자가 고른 컴포넌트 하나씩 독립적으로 스트리밍해.

사용자는 완성 순서대로 화면을 받아

페이지 헤더와 내비게이션은 곧바로 보이고, 느린 차트 자리에는 skeleton이 나타나. 차트 데이터가 도착하면 그 자리만 실제 내용으로 바뀌어. 위쪽 화면을 다시 렌더링하거나 전체 페이지 spinner로 가릴 필요가 없어.

경계를 너무 잘게 쪼개지는 마

모든 작은 컴포넌트에 fallback을 넣으면 화면이 조각조각 깜빡이고 레이아웃이 흔들릴 수 있어. 함께 이해되는 정보는 함께 도착하게 묶고, 실제 지연 원인이 다른 영역 사이에 경계를 둬.

페이지를 사용자가 이해하는 덩어리로 나누고, 실제 지연 시간이 다른 덩어리 사이에 Suspense를 둬. fallback은 최종 크기와 비슷하게 만들어 레이아웃 이동을 막고, 중요한 제목과 행동은 느린 차트 뒤에 숨기지 마. 느린 네트워크에서 도착 순서를 직접 녹화해 보면 경계가 맞는지 보여.

스트리밍은 느린 query를 빠르게 만들지 않아. 기다림을 더 유용하게 보이게 할 뿐이야. 같은 데이터 폭포와 비싼 계산을 그대로 둔 채 skeleton만 늘리면 서버 비용과 완성 시간은 변하지 않아. 원인 최적화와 지각 성능을 별개로 측정해. 느린 경계를 나눌 때는 가장 먼저 의미 있는 내용을 어디까지 보여 줄지도 정해. 대체 화면이 많아도 사용자가 무엇을 기다리는지 모르면 스트리밍의 이점이 사라져. header·목록·느린 chart에 서로 다른 지연을 넣고 route loading.tsx 하나와 컴포넌트 Suspense 여러 개를 비교해. 첫 바이트, 각 영역 도착, 전체 완료 시간을 기록하고 skeleton 크기가 실제 콘텐츠와 달라 CLS를 만드는지도 확인해.

보조 기술에도 loading 상태가 전달되게 aria-busy와 이해 가능한 대체 문구를 검토해. 시각적 skeleton만 움직이고 화면 읽기 도구에는 아무 변화가 없으면 스트리밍 경험이 사용자마다 달라져.

Code

구간 전체 스트리밍·tsx
// app/dashboard/loading.tsx — 즉시 보임
export default function Loading() {
  return <DashboardSkeleton />;
}

// app/dashboard/page.tsx — 데이터 도착 시 스트리밍
export default async function Dashboard() {
  const data = await fetchSlow();
  return <DashboardContent data={data} />;
}
세밀한 Suspense 경계·tsx
import { Suspense } from 'react';

export default function Dashboard() {
  return (
    <div className="space-y-6">
      <h1>Dashboard</h1>
      <Suspense fallback={<StatsSkeleton />}>
        <Stats />
      </Suspense>
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart />
      </Suspense>
      <Suspense fallback={<TableSkeleton />}>
        <RecentOrders />
      </Suspense>
    </div>
  );
}

External links

Exercise

느린 데이터가 많은 페이지를 세 Suspense 경계로 나눠. 변경 전후의 첫 바이트, 첫 화면, 전체 완료 시간을 기록해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.