다락책방 프로젝트 구현 작업을 어느정도 마무리를 하고 담당 페이지 성능 검사를 하였는데 좋지 않은 점수를 받고 있었고 여러 시도 끝에 나름 성능을 개선하였다. 그 중 마이페이지(책장 탭)에서 성능을 개선한 방법을 정리해보겠다.
💡 lighthouse 측정 항목FCP(First Contentful Paint): 브라우저가 첫 번째 DOM의 콘텐츠를 렌더링 하는 데 걸리는 시간.
LCP(Largest Contenful Paint): 뷰포트에서 가장 큰 콘텐츠 요소가 화면에 렌더링 될 때까지 걸리는 시간
TBT(Total Blocking Time): 웹 페이지가 사용자의 입력에 응답하지 못하도록 차단된 시간
SI(Speed Index): 콘텐츠가 시각적으로 표시되는 데까지 걸리는 시간
Cumulative Layout Shift: 느린 로딩, 비동기 동작, 동적 DOM 변경 등으로 레이아웃이 변하는 시간
ES2020의 문법으로 모듈을 빌드 타임이 아닌 런타임에 불러오도록 한다. 따라서 번들 파일을 분리하면서첫 렌더링의 성능을 개선할 수 있다. 그래서 보통 첫 렌더링에 보여지지 않는 컴포넌트(모달 등)에 적용할 수 있다.
next/dynamic
을 사용하면 간편하게 dynamic import
를 적용할 수 있다.Modal
은 사용자가 특정 게시물을 클릭했을 때만 렌더링되면 되므로 dynamic import
를 적용해주었다.// 적용 전
import Modal from '@/components/common/Modal';
// 적용 후
import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('@/components/common/Modal'));
next/image
의 경우 lazy loading
이 기본으로 장착되어 있어, 이를 끄고 싶으면 priority
를 true
로 설정하거나 loading
속성에 “eager”
값을 설정하면 된다.lazy loading
을 하면 스캐너에서 이미지를 숨기기 대문에 브라우저에서 이미지를 늦게 요청하게 되고 LCP
가 늦어진다true인 경우 이미지가 높은 우선순위로 간주되어 미리 로딩되며 지연로딩이 자동으로 비활성화 된다. 뷰포트 사이즈에 따라 LCP에 해당하는 이미지가 다를 수 있으므로 여러 이미지에 적용할 수 있도록 고려되어야 한다.
<Image
src={data.photoUrl}
alt={data.nickname}
width='72'
height='72'
priority
/>
//바로 보이는 프로필 사진은 **priority** 지정
<Image
src={data.thumbnail}
alt={data.title}
width='120'
height='152'
loading='lazy'
/>
//스크롤에 벗어나는 섬네일 이미지 **lazy** 지정
next/font/local
즉, 로컬에서 가져온 폰트가 용량이커서 다운로드하는데 시간이 오래걸렸다._app.tsx
에서 전역으로 적용하던 폰트를 layout
으로 분리하여 해당 폰트를 사용하는 pages
에서만 적용하였다적용 전
// app.tsx
import localFont from 'next/font/local';
(...)
const prettyNight = localFont({
src: '../../public/fonts/Cafe24Oneprettynight-v2.0.woff2',
weight: '400',
variable: '--prettyNight',
});
(...)
<main className={`${notoSans.className} ${lato.variable} ${prettyNight.variable} h-full`} >
(...)
적용 후
// 폰트 레이아웃 생성
import localFont from 'next/font/local';
import { ReactNode } from 'react';
const prettyNight = localFont({
src: '../../public/fonts/Cafe24Oneprettynight-v2.0.woff2',
weight: '400',
variable: '--prettyNight',
});
function PrettyNightFontLayout({ children }: { children: ReactNode }) {
return <div className={${prettyNight.variable} h-full}>{children}</div>;
}
export default PrettyNightFontLayout;
// 해당 폰트를 사용하는 page(BookRecordPage)에 적용
BookRecordPage.getLayout = function getLayout(page: ReactElement) {
return <PrettyNightFontLayout>{page}</PrettyNightFontLayout>;
};