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>
  );
};

 

 

적절한 로딩 화면을 구성해줘야겠지만, 성능적으로, 유저경험적으로 나아진것을 확인