OLD/NextJs v13
[문제-해결] Suspense, Streaming 활용하기
joseph0926
2023. 11. 5. 10:05
문제
NextJs ^13을 사용중이지만, 첫 페이지에서 서버로부터 데이터를 많이 불러오고 있었지만, 그거에 대한 적절한 조치가 없어 초기 로딩이 매우 느렸음
// layout.tsx
const MainLayout = async ({ children }: { children: React.ReactNode }) => {
const profile = await getCurrentUser();
const games = await getGames();
const servers = await getServers();
return (
<main className="relative bg-light-850 dark:bg-dark-100">
<MainNavbar profileId={profile?.id} servers={servers} games={games} />
<div className="flex">
<LeftSidebar profileId={profile?.id} games={games} servers={servers} />
<section className="flex min-h-screen flex-1 flex-col px-6 pb-6 pt-36 max-md:pb-14 sm:px-14">
<div className="mx-auto w-full max-w-5xl">{children}</div>
</section>
<RightSidebar />
</div>
</main>
);
};
// page.tsx
const MainPage = async () => {
const { posts } = await getPosts({});
const profile = await getCurrentUser();
return (
<>
<div className="flex w-full flex-col-reverse justify-between gap-4 sm:flex-col sm:items-center">
<h1 className="h1-bold text-dark100_light900">All Posts</h1>
<Link href="/create-post" className="flex justify-end max-sm:w-full">
<Button className="primary-gradient min-h-[46px] px-4 py-3 !text-light-900">게시글 작성</Button>
</Link>
</div>
<div className="mt-11 flex justify-between gap-5 max-sm:flex-col sm:items-center">
<LocalSearchbar route="/" iconPosition="left" imgSrc="/assets/icons/search.svg" placeholder="search,,," otherClassName="flex-1" />
<Filter filters={HomePageFilters} otherClassName="min-h-[56px] sm:min-w-[170px]" containerClassName="hidden max-md:flex" />
</div>
<HomeFilters />
<div className="mt-10 flex w-full flex-col gap-6">
{posts.length > 0 ? (
posts.map((q) => (
<PostCard
key={q.id}
id={q.id}
title={q.title}
tags={q.tags}
author={q.author}
comments={q.comments}
upvotes={0}
views={q.views}
createdAt={q.createdAt}
profileId={profile?.id}
/>
))
) : (
<NoResults
title="해당 게시글을 찾을 수 없습니다,,,"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem deleniti doloribus fugiat aspernatur"
href="/create-post"
linkTitle="게시글 작성하러가기"
/>
)}
</div>
개념 점검
해결
// layout.tsx
const MainLayout = async ({ children }: { children: React.ReactNode }) => {
return (
<main className="relative bg-light-850 dark:bg-dark-100">
<Suspense>
<LayoutWrapper isNav />
</Suspense>
<div className="flex">
<Suspense
fallback={
<section className="background-light900_dark200 light-border custom-scrollbar sticky left-0 top-0 h-screen flex-col justify-between overflow-y-auto border-r p-6 pt-36 shadow-light-300 dark:shadow-none max-sm:hidden lg:w-[266px]"></section>
}
>
<LayoutWrapper isNav={false} />
</Suspense>
<section className="flex min-h-screen flex-1 flex-col px-6 pb-6 pt-36 max-md:pb-14 sm:px-14">
<div className="mx-auto w-full max-w-5xl">{children}</div>
</section>
<RightSidebar />
</div>
</main>
);
};
const LayoutWrapper = async ({ isNav }: { isNav: boolean }) => {
const profile = await getCurrentUser();
const games = await getGames();
const servers = await getServers();
return isNav ? (
<MainNavbar profileId={profile?.id} servers={servers} games={games} />
) : (
<LeftSidebar profileId={profile?.id} games={games} servers={servers} />
);
};
// page.tsx
const MainPage = async () => {
return (
<>
<div className="flex w-full flex-col-reverse justify-between gap-4 sm:flex-col sm:items-center">
<h1 className="h1-bold text-dark100_light900">All Posts</h1>
<Link href="/create-post" className="flex justify-end max-sm:w-full">
<Button className="primary-gradient min-h-[46px] px-4 py-3 !text-light-900">게시글 작성</Button>
</Link>
</div>
<div className="mt-11 flex justify-between gap-5 max-sm:flex-col sm:items-center">
<LocalSearchbar route="/" iconPosition="left" imgSrc="/assets/icons/search.svg" placeholder="search,,," otherClassName="flex-1" />
<Filter filters={HomePageFilters} otherClassName="min-h-[56px] sm:min-w-[170px]" containerClassName="hidden max-md:flex" />
</div>
<HomeFilters />
<React.Suspense
fallback={
<div className="mt-10 flex w-full flex-col gap-6">
<Skeleton className="h-[50px] w-full" />
<Skeleton className="h-[50px] w-full" />
<Skeleton className="h-[50px] w-full" />
</div>
}
>
<Posts />
</React.Suspense>
</>
);
};
const Posts = async () => {
const profile = await getCurrentUser();
const { posts } = await getPosts({});
return (
<div className="mt-10 flex w-full flex-col gap-6">
{posts.length > 0 ? (
posts.map((q) => (
<PostCard
key={q.id}
id={q.id}
title={q.title}
tags={q.tags}
author={q.author}
comments={q.comments}
upvotes={0}
views={q.views}
createdAt={q.createdAt}
profileId={profile?.id}
/>
))
) : (
<NoResults
title="해당 게시글을 찾을 수 없습니다,,,"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem deleniti doloribus fugiat aspernatur"
href="/create-post"
linkTitle="게시글 작성하러가기"
/>
)}
</div>
);
};
적절한 로딩 화면을 구성해줘야겠지만, 성능적으로, 유저경험적으로 나아진것을 확인