1. 이 글의 맥락

현업에서 성능 개선의 일환으로 초기 화면 렌더링 속도를 개선하는 방법을 찾아보고 있습니다. 그 방식으로 Entry Point File의 크기를 줄이는 것이 있음을 알게 되었습니다. 이 글의 대부분의 개선은 hyewon님이 해주시고 저는 결과를 분석하는 역할을 했습니다.

 

 

Entry Point File은 프로젝트를 실행하면 나오는 첫 페이지를 담당하는 파일을 뜻합니다. 제가 개선할 초기 페이지입니다. 해당 화면에서 보이지 않는 라이브러리와 컴포넌트는 추후 필요할 때 불러오게 하여 성능을 개선할 수 있습니다.

 

프로덕트의 모든 초기 렌더링을 이번에 개선하기에는 시간상으로 어렵습니다. 아무래도 평소에 시간을 내어 더욱 해나가야 할 것 같습니다. 이번 시간에는 개선 방식을 알아보는 것에 집중합니다.

 

2. 개선해가는 과정

개선하는 방법으로 lazy-loading 을 사용할 것입니다. lazy loading은 필요한 데이터를 지연하여 로딩하는 방식을 말합니다. 기본적으로 웹 페이지를 로드할 때 모든 데이터를 한 번에 로드하는 것이 아니라, 사용자가 필요로 하는 데이터만 로드하는 방식입니다. 이미지, JavaScript 파일, CSS 파일 등을 필요한 시점에 로드하여 성능을 향상시킬 수 있습니다.

 

저는 NextJS를 사용하는 중이어서 이에 맞는 bundle-analyzer를 받아와 lazy-loading을 구현해봅니다.
https://www.npmjs.com/package/@next/bundle-analyzer

 

2-1. 사용 방법

npm i -D @next/bundle-analyzer

 

일단 자신의 NextJS 프로젝트에 bundle-analyzer를 받습니다.

 

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: true,
  openAnalyzer: true,
});

const nextConfig = {
// config 내용
}

module.exports = withBundleAnalyzer(nextConfig)

openAnalyzer : true를 하면 build시 무조건 analyzer가 열립니다. 계속 analyzer가 열리는 것이 귀찮다면 특정 환경에서면 열리도록 하는 방법이 있습니다. 구글에 검색하시면 금방 아실거에요 🤷‍♂️

 

npm run build

 

이제 빌드를 하여 analyzer를 체크해봅니다.

 

2-2. 빌드 결과

 

프로젝트 실행 후 첫 화면에 보이지 않아도 되는, 즉 lazy loading을 적용할 수 있는 라이브러리들과 컴포넌트를 표시해두었습니다. 빨간색 테두리들은 최초 렌더링 시 필요 없는 라이브러리들을 뜻합니다.

 

빨간색 테두리 내부의 초록색 테두리는 예외입니다. node_modules 는 하나하나 빨간색 테두리 하는게 어려워서.. 이렇게 해두었습죠.

 

계산을 해보니 lazy loading 할 라이브러리의 총 크기는 1951.82kb, 즉 1.9MB 정도 되었습니다.

 

2-3. 그래서 무엇을 지연로딩 할까?

 

analyzer만 보면 잘 모르실테니 lazy loading을 적용할 컴포넌트들을 표시해봅니다. 이 컴포넌트 및 라이브러리들은 굳이 맨 첫 화면에 보이지 않아도 됩니다. react-datetime, momentjs 등 현재 사용하지 않는 라이브러리는 삭제했습니다.

 

import dynamic from 'next/dynamic';

const Example = dynamic(() => import('@/components/Example'), {
  ssr: false,
});

 

lazy loading 적용을 원하는 컴포넌트에서 이런 식으로 사용하면 됩니다. ssr: false 는 서버 사이드 렌더링(SSR)을 비활성화하는 속성입니다. 서버에서도 미리 페이지를 조립하지 않도록 하고, 사용자가 필요할 때에만 불러오도록 합니다.

 

3. 분석 결과

 

Entry Point File의 크기를 40% 정도 축소시킬 수 있었습니다. (1002KB -> 604KB) 여전히 bundle file 전체의 크기는 큽니다. 총 크기가 3.48MB에서 3.36MB로 줄어들은 정도 뿐입니다.

 

bundle의 모든 파일 크기를 줄일 수 없지만 엔트리 포인트 파일의 크기를 줄일 수 있는 것 만으로 큰 효과라고 생각합니다.

브라우저에서 Entry Point File을 다운로드하고 해석하는 데에는 시간이 소요되므로, 파일의 크기가 작을수록 다운로드 및 해석 속도가 빨라질 것이기 때문입니다.

 

 

라이트 하우스 지표는 이렇게 바뀌었습니다. 라이트 하우스 지표에 대해서 모르거나 잊으신 분들을 위해서 한 번 설명해드리겠습니다.

  • Largest Contentful Paint(LCP) : 사용자가 웹 페이지에서 가장 큰 콘텐츠 요소를 볼 수 있는 시간을 측정하는 지표
  • Total Blocking Time(TBT) : 페이지 로딩 중에 발생하는 블로킹 시간의 총 합을 측정하는 지표. 블로킹 시간은 JavaScript 실행이 페이지 로딩을 방해하여 사용자 인터랙션에 대한 응답 시간을 늦추는 시간을 의미.
  • Speed Index(SI) : 페이지가 얼마나 빠르게 시각적으로 완전히 로드되는지

 

사실 LCP와 TBT의 지표는 현재 좋은 수준은 아닙니다. 이는 앞으로 개선해나가야 할 숙제라고 생각합니다. LCP는 2.5초 이하, TBT는 1000ms 이하가 좋은 지표라고 합니다. SI 역시 1초에서 3초 사이가 좋은 지표라고 합니다.

그래도 지표가 각각 20%, 30%, 17% 정도 개선되었음이 보입니다. 지표에 대한 자세한 인사이트는 아래를 참고하세요~
https://developer.chrome.com/docs/lighthouse/performance/performance-scoring?hl=ko

 

4. 더욱 개선하고 싶은 것들

성능 개선 방식을 연구해봤지만 아직 궁금한 점이 많습니다.

  1. 번들 총 크기가 3.48MB에서 3.36MB로 줄어들었는데, 더욱 줄일 수 있는 방법이 있지 않을까 합니다. node modules의 안에 있는 번들들의 크기를 압축할 수 없을까요?
    ➡ 팀원들과 논의 결과 npm 을 yarn으로 바꾸는 것이 어떨까 하는 의견이 나왔습니다.
  2. 이번에는 컴포넌트를 분리하지 않고 dynamic import만을 붙였습니다. 거대한 컴포넌트를 분리하고 필요 없는 컴포넌트들은 lazy loading을 시키면 렌더링 속도 개선에 많은 도움이 될 것 같습니다.
재밌게 살고 싶은 앤드류