PinTogether / frontend

pintogether web frontend dev
https://pintogether-frontend.vercel.app
1 stars 1 forks source link

[Refactor] Server Components 적극 활용하기 #179

Open jwo1024 opened 1 week ago

jwo1024 commented 1 week ago

Rendering: Server Components

처음에는 Next.js 의 특징인 서버 컴포넌트를 잘 활용해 보자 생각하며 시작하였으나, useState, useEffect 와 같은 client side에서만 사용 가능한 hook을 포기할 수 없어 점점 클라이언트 컴포넌트의 지분이 많아지고, 서버 컴포넌트를 활용했다 할만한 부분이 없어졌다. (다만 OpenGraph 관련해서는 Server Rendering 의 이점을 누리며 활용하고 있다.)

그래서 이번에 리펙토링를 하면서 클라이언트 컴포넌트를 최소화하고 서버 컴포넌트의 지분을 키울 것이다. 리펙토링 후에는 현재 lighthouse의 낮은 점수의 Performance가 개선될 것이라 기대한다.

Benefits of Server Rendering

Rendering: Server Components

jwo1024 commented 1 week ago

Data Fetching

일단 server component 에서 data를 fetch 해올 수 있다는 것을 생각하지 못하고 있었다. open-graph 를 적용하면서 이점을 활용하지 않았다는 것을 깨달았다. 서버컴포넌트에서 Data Fetching을 하도록 수정한다면, 자연스레 Caching 도 적용될까(?) 리펙토링을 진행해보자.

  1. 페이지 정보에 대한 GET은 page.js 에서 하도록 수정하자.
    1. ex) 페이지 정보 == 컬렉션 페이지의 컬렉션 정보, 컬렉션이 가지고 있는 핀 정보, 댓글 정보

다른 페이지에 비해서 퍼포먼스 점수가 제일 낮은 ‘컬렉션 조회 페이지’ 부터 적용시켜보았다.

기존 코드

// Server Component
// /app/(default)/collection/[id]/page.tsx
export default function Page({ params }: PageParams) {
  return <CollectionPage collectionId={params.id} />;
}

// Client Component
// /src/containers/collection/CollectionPage.tsx
export default function CollectionPage({
  collectionId,
}: {
  collectionId: number;
}) 
    ...
  useEffect(() => {
    getCollectionData();
    getPinData();
    getReplyData();
  }, []);
  ...
};

변경 후 코드

// Server Component
// /app/(default)/collection/[id]/page.tsx
export default async function Page({ params }: PageParams) {
    // 컬렉션 조회 페이지에서 필요한 데이터를 서버컴포넌트에서 가져온다.
  const collectionId = params.id;
  const { collectionInfo, errorMessage: collectionInfoErrMsg } =
    await fetchGetCollectionInfo(collectionId);
  const { pinList, errorMessage: pinListErrMsg } =
    await fetchGetCollectionAllPins(collectionId);

  return collectionInfo && pinList && collectionInfoErrMsg === "" ? (
    <CollectionPage
      collectionId={collectionId}
      collectionInfo={collectionInfo}
      pinList={pinList}
      pinListErrMsg={pinListErrMsg}
    />
  ) : (
    <CollectionNotFoundPage collectionInfoErrMsg={collectionInfoErrMsg} />
  );
}

// Client Component
// /src/containers/collection/CollectionPage.tsx
export function CollectionPage({
  collectionId,
  collectionInfo,
  pinList,
  pinListErrMsg,
}: {
  collectionId: number;
  collectionInfo: CollectionDetail;
  pinList: PinForPlace[];
  pinListErrMsg: string;
}) {
    ...
};

서버 컴포넌트에서 데이터를 가져오면서, Next.js app router의 기능 ‘Loading UI’ 또한 자연스럽게 적용할 수 있었다.

  • Loading UI 란 .. UI 로딩 및 스트리밍 loading.js 특수 파일은 React Suspense를 사용하여 의미 있는 로딩 UI를 만드는 데 도움이 됩니다.. 이 규칙을 사용하면 경로 세그먼트의 콘텐츠가 로드되는 동안 서버에서 즉시 로드 상태를 표시할 수 있습니다 . 렌더링이 완료되면 새 콘텐츠가 자동으로 교체됩니다.

‘컬렉션 조회 페이지’에는 댓글을 달 수 있는 기능이 있다. 처음에는 댓글 내역도 서버컴포넌트인 Page 에서 가져오려고 하였다. 하지만 댓글의 경우에는 잦은 업데이트가 필요하다고 판단하여 CollectionReplyInput컴포넌트 자체에서 데이터를 가져오고 관리하도록 수정하였다.

(컬렉션 조회 페이지안의 메뉴 각각을 page로 만들어 버리는 것도 좋겠다는 생각이 들었다. 컴포넌트의 의존성도 약해지고 좋지 않을까?)

기타

차이

기대했던것과 달리 가시적인 차이는 없다 초기 페이지 로드 및 [콘텐츠가 포함된 첫 페인트(FCP)](https://web.dev/fcp/) 시간도 동일하다

아무래도 performance의 점수가 낮게 나오는 것은 이미지 영향이 큰 것 같다.

PageSpeed Insights - 변경전

PageSpeed Insights - 변경후

변경전 변경후