
다락책방 프로젝트 구현 작업을 어느정도 마무리를 하고 담당 페이지 성능 검사를 하였는데 좋지 않은 점수를 받고 있었고 여러 시도 끝에 나름 성능을 개선하였다. 그 중 마이페이지(책장 탭)에서 성능을 개선한 방법을 정리해보겠다.
💡 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>;
};
