[Next.js] Suspense, Loading UI, Streaming SSR

큰 데이터를 받아거나 네트워크가 느린 환경에서는 어쩔 수 없이 긴 로딩시간이 생길 수 있다.

그럴 때 활용할 수 있는 방법들을 알아보자!

 

느린 데이터 fetch 환경 만들기

1. 크롬에서 속도를 조정할 수 있다.

2. json-server 버전을 낮춰 딜레이를 준다

- "dependencies": { "json-server": "0.17.4" }

- "server": "json-server --watch db.json --delay 5000 --port 4000"

 

 

비동기 데이터 로딩

페이지에 로딩 UI를 띄워두고, 데이터를 로딩하는 방식이다.

'use client';

import React, { useState, useEffect } from 'react';
import { Product } from '@/types';

const HomePage = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [data, setData] = useState<Product[]>([]);

  useEffect(() => {
    setIsLoading(true);
    fetch('http://localhost:4000/products')
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setIsLoading(false);
      });
  }, []);

  // 로딩 중에는 로딩중이라는 안내를 띄워준다
  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      { * 받아온 데이터 보여주기! * }
    </div>
  );
};

export default HomePage;

 

 

캐싱 활용하기

SSG를 사용하는 방법, 데이터가 자주 변경되지 않을 때 로딩 시간을 단축하기위해 사용할 수 있다.

빌드시 데이터를 미리 가져오고, 이를 정적 페이지로 제공한다.

import ProductList from '@/components/ProductList';
import { Product } from '@/types';

const ProductsPage = async () => {
  const response = await fetch('http://localhost:4000/products',
  {cache: "force-cache"});
  const products: Product[] = await response.json();

  return (
    <div>
      <h1>Products</h1>
      <ProductList products={products} />
    </div>
  );
};

export default ProductsPage;

 

 

loading UI

next 에서 loading UI를 위해 기본적으로 제공하는 기능이 있다!

 

1) route segment 폴더(app)에 loading.tsx 파일 생성

2) 로딩시에 노출할 컴포넌트를 반환하는 컴포넌트 작성

export default function Loading() {
  return <>Loading...</>;
}

 

loading.tsx를 사용하는 경우 특정 컴포넌트만 로딩 UI로 표시하지는 못하고, 페이지 전체가 로딩 컴포넌트로 보여진다.

route segment의 루트인 app에 loading.tsx를 생성하는 경우 프로젝트의 모든 페이지에 로딩UI가 적용되고, 특정 페이지 전체에 적용하고 싶으면 그 페이지의 폴더에 loading.tsx를 생성하면 된다!

 

 

Suspense와 streaming SSR

스트리밍은 서버에서 준비된 콘텐츠를 조각내어 클라이언트에 보내는 방식이다.

사용자가 페이지의 일부를 빨리 확인할 수 있으며, 나머지 데이터는 백그라운드에서 계속 로드된다.

데이터를 로딩하는 컴포넌트를 나누고, Suspense를 활용해 loading Ui를 구성한다!

export default async function Home() {
  return (
    <div className="p-8 m-4">
      <h1>Sparta Shop</h1>
      <Suspense fallback={<Loading />}>
        <NewProductList />
      </Suspense>
      <Suspense fallback={<Loading />}>
        <ProductList />
      </Suspense>
    </div>
  );
}