반응형
streaming SSR
dynamic rendering으로 빌드된 페이지에서 10초 걸리는 api를 서버에서 호출하면 사용자의 첫 로드가 그만큼 느려진다. 이를 해결하기 위해 nextjs13 이상부터는 suspense를 사용해 쉽게 streaming SSR을 적용할 수 있다.
import { Suspense } from 'react';
import SlowComponent from './SlowComponent';
export default function Home() {
return (
<div>
<h1>Welcome to my page</h1>
<Suspense fallback={<div>Loading slow component...</div>}>
<SlowComponent />
</Suspense>
</div>
);
}
// app/SlowComponent.js
async function fetchSlowData() {
// 10초 지연을 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 10000));
return "Data loaded after 10 seconds";
}
export default async function SlowComponent() {
const data = await fetchSlowData();
return <div>{data}</div>;
}
이렇게 오래 걸리는 컴포넌트를 suspense로 묶어 예약해 두고 나머지를 먼저 rendering 해서 클라이언트에게 내려 주면 된다. 대신 서버 리소스 사용이 높다. 예약된 컴포넌트는 완성하면 객체로 클라이언트로 내려준다.
nextjs 서버컴포넌트에서 fetch 시 기본 옵션으로 캐싱되니 이를 사용해 리소스를 아낄 수 있겠다.
CSR
이 방식은 static rendering 된 페이지에서도 사용 가능하다. suspense로 감싸진 컴포넌트를 서버에서 빈 값으로 두고 클라이언트에서 렌더링 하는 방식이다.
mejai.kr 프로젝트에서 프리티어 ec2로 올린 nextjs서버 자원을 아끼기 위해 이 방식을 사용했다.
"use client";
import React, { Suspense } from "react";
import UserInfoBox from "@/app/summoner-page/_components/userInfoBox";
import TierBox from "@/app/summoner-page/_components/tierBox";
import { useSearchParams } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { fetchUserInfo } from "@/lib/fetchFunc";
import ErrorPage from "@/app/summoner-page/_components/errorPage";
import JandiBox from "@/app/summoner-page/_components/jandiBox";
import Spinner from "@/components/ui/spinner";
import { AxiosError } from "axios";
function AwaitPage() {
const params = useSearchParams();
const id = params.get("id") || "";
const tag = params.get("tag") || "";
const { error, isLoading } = useQuery({
queryKey: ["userInfo", { id, tag }],
queryFn: fetchUserInfo,
staleTime: 1000 * 60 * 15, // 15분으로 staletime 설정
gcTime: 1000 * 60 * 15,
});
if (error) {
return <ErrorPage error={error as AxiosError} />;
}
if (isLoading) {
return (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
);
}
return (
<>
<UserInfoBox id={id} tag={tag} />
<TierBox id={id} tag={tag} />
<JandiBox />
</>
);
}
export default function SummonerPage() {
return (
<Suspense fallback={<div>Loading...</div>}>
<AwaitPage />
</Suspense>
);
}
이렇게 클라이언트 컴포넌트에서 suspense를 사용하면 서버에선 dynamic rendering시에도 클라이언트에서 fetch 하게 된다.
정리하자면
- 서버 컴포넌트에서의 Suspense:
- 서버에서 렌더링이 시작되고, Suspense로 감싼 부분을 만나면 해당 부분의 데이터가 준비될 때까지 기다린다.
- 준비된 데이터와 함께 HTML을 생성하여 클라이언트로 스트리밍한다.
- 클라이언트는 부분적으로 완성된 HTML을 받아 렌더링 하고, 나머지 부분은 서버에서 추가로 전송된다.
- 클라이언트 컴포넌트에서의 Suspense:
- 초기 HTML은 서버에서 생성되지만, 데이터 fetching은 클라이언트에서 이루어진다.
물론 두 방식 모두 클라이언트에 도착한 html엔 suspense로 감싸진 부분은 텅 비어있으니 SEO를 고려해야 한다.
렌더링 방식, 원하는 사용자 경험에 따라 적절히 suspense를 사용해 보자.
반응형
'<frontend> > next.js' 카테고리의 다른 글
nextjs api기능으로 목업api만들기 (0) | 2024.07.01 |
---|---|
Next.js프로젝트에 구글 애드센스 달기 (0) | 2024.06.28 |
서버리스 nextjs (0) | 2024.05.22 |
서버사이드에서 query param 접근하기 (0) | 2024.05.15 |
next.js 블로그 제작 후기 (5) | 2023.12.25 |