크롬 브라우저 시크릿 모드에서 Lighthouse를 이용해 성능 검사를 실시했습니다.

홈페이지는 모든 페이지 중 가장 정적인 페이지입니다. SSR을 적극 활용한 결과 전반적으로 목표로 한 점수를 나타냈습니다. 과거 팀 프로젝트로 진행하던 시기에는 Next.js App Router에 대한 이해도가 적어 SSR이 제대로 활용되지 않았습니다. 클라이언트를 로드한 뒤 데이터를 불러오는 구조로 인해 여행 카드 컴포넌트에서 레이아웃 시프트가 발생해 스켈레톤 UI의 도입을 고민했습니다. 하지만 SSR을 통해 사전에 데이터를 불러옴으로써 스켈레톤과 같은 로딩 UI 없이도 레이아웃 시프트를 방지할 수 있었습니다.
프로필 페이지

검색 페이지

프로필 페이지와 검색 페이지도 홈페이지와 마찬가지로 SSR을 통해 레이아웃 시프트가 발생하지 않고 전반적으로 준수한 점수를 나타냅니다.
v1.0.0

v1.0.4

일정 상세 페이지는 모든 페이지 중 가장 무거운 기능을 가진 페이지입니다. 페이지의 레이아웃이 클라이언트의 뷰포트를 감지해 크기를 계산하는 로직을 포함하고 있어 TBT, CLS, SI 점수가 다른 페이지보다 낮습니다. 한편 v1.0.0에 비해 v1.0.4에서는 CLS가 개선된 모습을 확인할 수 있습니다. 다음 요소를 수정하여 의도치 않은 여러 레이아웃 시프트를 제거했습니다.
<aside>
이미지 영역 고정 크기 지정
이미지 존재 여부, 주변 요소 크기에 상관없이 고정된 크기를 가지도록 지정해 일정 블록이 항상 일정한 크기를 유지하도록 변경했습니다.
// v1.0.0
// ...
<div className="w-[5.75rem]">
{/* 이미지 */}
</div>
// ...
// v1.0.4
// ...
<div className="w-[5.75rem] shrink-0">
{/* 이미지 */}
</div>
// ...
일정 블록 숨김 로직 제거
일정 블록 중 PlanBlock 컴포넌트에서 구글 이미지를 아직 가져오지 않았거나 존재하지 않을 경우 렌더링하지 않는 로직을 제거해 항상 UI가 렌더링되도록 변경했습니다.
// v1.0.0
// ...
const { data } = useGetGooglePlacesPhotos(...);
if (!data) return;
return (
<CoreBlock
index={index}
name={name}
category={category}
memo={memo}
imageUrl={data?.body.photoUri}
startAt={startAt}
duration={duration}
onClick={onClick}
{...props}
/>
);
}
// v1.0.4
// ...
const { data } = useGetGooglePlacesPhotos(...);
return (
<CoreBlock
index={index}
name={name}
category={category}
memo={memo}
imageUrl={data?.body.photoUri}
startAt={startAt}
duration={duration}
onClick={onClick}
{...props}
/>
);
}
</aside>
v1.0.0