[NextJS] 성능 개선 하기 (lighthouse)

JaeHong Jeong·2023년 7월 30일
0

NextJS

목록 보기
3/4
post-thumbnail

개요

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

💡 lighthouse 측정 항목

FCP(First Contentful Paint): 브라우저가 첫 번째 DOM의 콘텐츠를 렌더링 하는 데 걸리는 시간.

LCP(Largest Contenful Paint): 뷰포트에서 가장 큰 콘텐츠 요소가 화면에 렌더링 될 때까지 걸리는 시간

TBT(Total Blocking Time): 웹 페이지가 사용자의 입력에 응답하지 못하도록 차단된 시간

SI(Speed Index): 콘텐츠가 시각적으로 표시되는 데까지 걸리는 시간

Cumulative Layout Shift: 느린 로딩, 비동기 동작, 동적 DOM 변경 등으로 레이아웃이 변하는 시간

최적화 하기

1. Dynamic import 적용하기

💡 Dynamic Import

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

2. 이미지 최적화 하기

  • Lazy Loading
    • 이미지가 로드되는 시점을 임의로 지연시키는 것. 예를 들어 상단에는 이미지가 없고 하단에만 이미지가 있을 경우, 스크롤이 상단에 위치해 있는 동안엔 이미지를 로딩시키지않는 것
    • next/image의 경우 lazy loading이 기본으로 장착되어 있어, 이를 끄고 싶으면 prioritytrue로 설정하거나 loading 속성에 “eager” 값을 설정하면 된다.
    • 뷰포트에 바로 들어오는 메인 이미지인 경우 lazy loading을 하면 스캐너에서 이미지를 숨기기 대문에 브라우저에서 이미지를 늦게 요청하게 되고 LCP가 늦어진다
💡 next/Image의 priority

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** 지정

3. 폰트를 컴포넌트 단위로 분리, 적용

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

결과

  • 성능 점수 : 14점 상승, LCP : 3.5s 단축, TBT, SI : 성능 향상
profile
반갑습니다.

0개의 댓글