최근 들어 성능 최적화에 대한 고민을 많이 해보고 싶어서, 성능 최적화를 해봐야 겠다는 생각이 들었습니다.
예전에 진행했던 프로젝트 중 하나가, Lighthouse 점수가 낮게 나와서 몇 가지 개선을 통해 성능 개선을 하려고 합니다.
이 프로젝트는 Next 12 버전을 이용하여 개발되었고, HTTP/2 프로토콜을 사용하고 있습니다.
앱 서비스이기 때문에, Lighthouse에서 모바일 기준으로 성능 검사를 진행하였습니다.
Lighthouse를 이용한 성능 점수 확인
이 페이지는 이미지가 굉장히 많습니다.
상단에 배너 슬라이드를 통해 여러 개의 이미지가 보여지고, 뷰포트 아래에도 섹션별로 많은 이미지를 로드합니다.
LCP도 무려 15.8초가 걸립니다.. 일단은 이미지 로드에 대한 개선이 확실히 필요할 것 같습니다.
이미지 요청 개수는 41개이고, 총 크기는 15.8MB 입니다.
어떤 이미지는 1MB가 넘어갑니다.
Next/image를 사용하지 않은 컴포넌트가 많았는데요.
Next/image를 통해서 개선하고자 합니다.
Next/Image를 사용하려는 이유
첫번째로 Lazy loading이 있습니다.
지연 로딩이라고 부르는 이것은 이미지 또는 JS 파일을 나중에 로드하는 것을 의미합니다.
사용자가 보게 되는 첫 화면에서는 화면 밖의 이미지를 미리 불러올 필요가 없습니다.
Next/Image를 사용하여 이미지를 로드하는 경우, 기본적으로 지연 로딩이 적용이 됩니다.
이미지가 많은 페이지에서는 Next/Image를 사용하는 것이 브라우저에 초기 이미지 요청 갯수를 확연하게 줄여줄 수 있습니다.
두번째로 WebP 이미지를 생성해줍니다.
WebP 이미지 포맷은 구글에서 만든 것으로 JPEG (손실 압축)과 비손실 압축 (PNG, GIF) 등의 이미지를 대체할 수 있습니다.
또한 이미지 파일 크기도 20~30% 정도 줄일 수 있다고 합니다.
Next.js는 이미지 최초 요청 시, 이미지를 WebP 포맷으로 변환하여 생성하고 원본 이미지 대신 변환된 이미지를 제공합니다.
그 밖에 placeholder 이미지를 제공하거나, priority 옵션, sizes 등을 제공할 수 있는데요.
크게 두 가지 이유를 통해 브라우저의 이미지 로딩 속도를 개선해볼 수 있겠습니다.
Next/Image로 생성되는 img 요소의 srcset, sizes 지정하기
Next/Image로 생성된 img 요소를 검사해보면 다음과 같이 렌더링이 됩니다.
srcset
에 굉장히 많은 이미지 링크들이 매핑이 되고 있는데요.
사용자의 화면 크기에 따라 최적화된 이미지를 제공하는 것입니다.
하지만 이미지 최초 요청 시, 필요한 이미지보다 많은 이미지를 생성할 수 있어서, 최초 이미지 로딩 시간이 지연될 수 있고 Next 서버 쪽에도 불필요한 이미지로 인한 용량이 증가할 수 있습니다.
next.config.js
설정파일에 images
에서 이 문제점을 해결할 수 있는데요.
imagesSizes
와 deviceSizes
를 지정해주면 됩니다.
imagesSizes
는 디바이스 크기보다 작은 이미지 사이즈를 정의한 배열이고, deviceSizes
는 디바이스 크기를 정의한 배열입니다.
이 두 개가 합쳐져서 이미지들이 생성되고, srcset에도 이미지 링크들이 매핑되는 것입니다.
// next.config.js
const nextConfig = {
images: {
imageSizes: [33, 50],
deviceSizes: [420, 768, 1080, 1200, 1920, 2048],
},
...
},
next.config.js
를 수정하고 확인해보면, srcset
에 필요한 이미지 링크들만 제공할 수 있습니다.
sizes
도 사용해서 이미지 크기가 디바이스 크기보다 작은 경우, 더 작은 사이즈의 이미지를 로딩하도록 처리했습니다.
// 예시
<Image
src={data.bannerImage}
alt={data.title}
layout="responsive"
width={768}
height={450}
sizes="(max-width: 420px) 420px, (max-width: 768px) 768px"
/>
CSS 프리로드 하기
폰트가 나중에 로드되어 문제가 생길 수 있다는 진단이 있었습니다.
글꼴이 바뀌는 현상이 일어나거나 CLS 문제를 일으킬 소지가 있어 보입니다.
CSS-in-JS를 사용하여 폰트를 로드하는 코드를 커스텀 앱 페이지 (_app)에 넣어주었는데요.
JS가 로드되고 나서 폰트를 로드하는 것으로 추정됩니다.
// _app.tsx
...
<Head>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui, viewport-fit=cover"
/>
<link rel="preload" href="/fonts/campton.css" as="style" />
<title>opt</title>
</Head>
커스텀 앱 페이지를 수정해줄 필요가 있겠습니다.
폰트를 로드하는 부분을 CSS 파일로 분리한 뒤, 커스텀 앱에 Next.js에서 제공하는 Head 태그를 사용해서 프리로드하도록 코드를 수정했습니다.
이제 브라우저에서 폰트와 관련된 CSS를 우선적으로 로드하게 됩니다.
성능 개선 결과는 ?
초기 화면을 로딩하는 데 다운로드한 총 이미지 크기가 428kB 입니다.
개선 전에는 15.8MB 였는데, 15.8MB -> 428kB로 최적화된 것입니다..!
(15.8 * 1024) / 428 -> 약 1/38 크기로 줄어들었습니다.
개선 후에 가장 큰 이미지 크기는 42.6kB입니다. (1MB에 비해 이미지 사이즈가 굉장히 줄어들었습니다.)
그럼 Lighthouse 점수는 어떻게 될까요?
모바일 기준으로 59점으로 소폭 상승했네요.
FCP 1.0s -> 0.8s
LCP 15.8s -> 7.4s
TBT 70 -> 100
CLS 0.211 -> 0.247
Speed Index 18.6s -> 5.4s
LCP와 Speed Index가 많이 개선되었습니다.
모바일 기준으로 후한 성능 점수를 받지 못했네요.
다른 부분을 더 개선할 필요가 있어 보입니다.
PC 기준으로 검사해보면 성능 점수가 무려 94점이 나옵니다.. !
모바일 기준은 매우 엄격하네요.
다른 부분도 좀 더 개선해보도록 하겠습니다.
참고
'Next.js' 카테고리의 다른 글
[Next.js] 페이지 성능 개선하기 3 - API Prefetch 하기 (0) | 2023.08.22 |
---|---|
[Next.js] 페이지 성능 개선하기 2 - 번들 사이즈 개선, Tree Shaking (0) | 2023.08.21 |
[Next.js] Custom App 페이지를 이용해서 페이지 로그(log) 기록하기 (0) | 2023.03.18 |
[Next.js] Dynamic Import와 Suspense를 같이 쓰지 말기 (0) | 2023.03.09 |
[Next.js] Recoil, Expectation Violation: Duplicate atom key 이슈 (0) | 2022.11.13 |