Lighthouse를 활용하여 LCP 점수 개선하기.
Tags: webdev, javascript, vite, react
Published: 2024-05-23 12:24:19

필자의 Lighthouse Performance 점수는 이렇다.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmkiqohwcpa0flwh4qjh.png)

me: 🤦‍♂️

개선해야 할 항목들은 다음과 같다.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zhuw2fffc02j4v9dz6bl.png)

Lighthouse 점수가 웹 성능에 절대적인 성능으로 귀결되지는 않는다. 그렇지만 개발 이후 성능 최적화에 좋은 지표가 되므로 가장 중요한 `LCP` 점수를 높여 보려고 한다.

> `LCP(Largest Contentful Paint)`
> - 최대 콘텐츠 렌더링 시간 즉, 뷰포트에서 가장 큰 콘텐츠 엘리먼트가 나타날 때 측정한다.
> - 페이지의 주요 내용이 화면에 렌더링이 완료되는 시기를 결정하는데 사용된다.

&nbsp;

## 이미지 최적화

**Largest Contentful Paint element - 12,350ms**

현재 콘텐츠에 이미지를 포함하고 있으며, 이미지는 LCP에 큰 영향을 미칠 수 있기 때문에 이미지를 최적화하고 적절한 포맷을 사용하는 것이 중요하다고 한다.

### 적절한 포맷 사용
- JPEG, PNG 대신 WebP 로 최신 포맷을 사용하여 개선 시도.

### 이미지 크기 최적화
- 불필요하게 큰 이미지를 줄이고 필요한 크기만 사용.
- 이미지 가로 크기를 1200px -> 400px 로 수정 (비율 유지)

### 지연 로드
- 화면에 보이지 않는 이미지들은 lazy-loading 하여 불필요한 렌더링이 발생하지 않도록 시도.
```html
<img src="image.webp" loading="lazy" alt="example image">
```

**결과**
- Before
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7svr849j8j9kzeurn0p.png)

- After
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yeepe9p8ydx79g2ic994.png)

Load Delay는 `9,507ms` -> `6,060ms` 으로 약 `37%` 개선되었으며, 
Load Time도  `3,740ms` -> `2,380ms` 으로 약 `37%` 개선되었다.

&nbsp;

## 폰트 최적화

**Enable text compression - Potential savings of 9,691 KiB**

다음으로는 웹 폰트 로딩이 페이지 렌더링을 차단하지 않도록 최적화를 수행하였다.

`font-display: swap` 속성을 사용하여 폰트 로딩 중에도 텍스트가 표시되도록 하였다.
```css
@font-face {
    font-family: "Noto Sans KR", sans-serif;
    src: url(https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300..600) format("woff2");
    font-display: swap;
  }
```

`swap`은 웹폰트가 로드되기 전에 사용자에게 기본 시스템 폰트로 텍스트를 표시합니다. 그리고 웹폰트 로딩이 완료되면 텍스트가 웹폰트로 교체된다. 이 방식은 사용자 경험을 향상시키는 데 일반적으로 사용되는 값이라고 한다.

&nbsp;

## JavaScript 최적화

**Minify JavaScript - Potential savings of 2,832 KiB**

JavaScript 파일이 크거나 로딩이 차단되면 LCP에 악영향을 미칠 수 있다고 한다.
그래서 찾은 개선 방법으로 코드 분할(Code Spliting)과 지연 로딩(Lazy-Loading)이 있다고 한다.

### 코드 분할 및 지연 로드
- 필자는 이미 이전에 현재 사용자가 보는 페이지에 따라 필요한 JavaScript 파일을 다운로드 받도록 하기 위해 React의 lazy함수를 통해 지연 코드 분할을 수행하였다.
```tsx
lazy(() => import('some-large-module').then(module => {
  // Use the module
}));
```

### 사용하지 않는 모듈 제거
추가적으로 수행한 최적화 방법으로 사용하지 않는 JS 및 모듈 파일들을 제거하거나 최소화 하였다. 이유는 필요 없는 파일들을 다운 받는 과정에서 필수적인 다운로드가 blocking 으로 지연되면 LCP 역시 저하될 수 있기 때문이다.

무거운 라이브러리를 Spread로 import 하지 않고 필요한 모듈만 import 하여 가져와 사용하도록 수정하였다. -> [블로그 글 참고](https://dev.to/hxxtae/lighthousereul-hwalyonghan-tree-shaking-8ie)

&nbsp;

## Vite 설정 파일 수정

이전 까지의 최적화를 수행하여도 LCP의 점수에는 큰 영향이 없었다. 그래서 ChatGPT한테 추가적인 방법을 물어보았더니 다음과 같은 방법을 제시해 주었다.

> `vite-plugin-compression`을 사용하여 Vite 설정을 통해 빌드된 파일을 미리 압축하는 방법으로 LCP를 단축 시킬 수 있다. 
> 이는 빌드된 파일을 `Gzip` 및 `Brotli` 형식으로 미리 압축하여 서버가 이를 효율적으로 제공할 수 있도록 한다.

### vite-plugin-compression 설치
```bash
npm install vite-plugin-compression --save-dev
```

### Vite 설정 파일 수정 (vite.config.js)
```javascript
// vite.config.js
import compression from 'vite-plugin-compression';

export default defineConfig({
  plugins: [
    react(),
    compression({ algorithm: 'gzip' }), // Gzip 압축 활성화
    compression({ algorithm: 'brotliCompress', ext: '.br' }) // Brotli 압축 활성화
  ]
});
```

위 설정을 저장한 후 프로젝트를 빌드하면 Gzip 및 Brotli 형식으로 압축된다.

&nbsp;

## 최종결과

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h9egsgu22d44oyybnw0g.png)

결과는 드라마틱 하지 않았지만 전반적으로 개선된 것을 확인할 수 있다.

> FCP : `6.8s` -> `5.1s`
> LCP : `12.4s` -> `8.7s`
> TBT : `30ms` -> `0ms`
> CLS : `0.001`
> SI : `7.5s` -> `5.5s`

그럼 왜 점수가 많이 오르지 않을까? 이 사실을 같은 항해99 멤버인 `원민님`에게 답을 얻을 수 있었다.

일반적으로 개발 환경에서는 디버깅을 쉽게 할 수 있도록 여러가지 추가적인 리소스를 요청하는데 이것이 Lighthouse 점수에 계산되기 때문이다.

여기에서 말하는 리소스라는 것은 개발 과정에서 발생한 오류를 추적할 수 있는 로그 같은 로직적인 부분일 수 있고, 디버깅 시 정확한 코드 위치를 판단할 수 있는 소스맵 같은 파일일 수도 있다.

그래서 프로덕션 빌드 시 이와 같은 파일들은 사용자에게 제공할 필요가 없고 성능 최적화를 위해 리소스들이 모두 빠지게 되므로 프로덕션 환경에서는 개발 환경 보다 성능 상 이점을 누릴 수 있을 것이며 Lighthouse Performance 점수 또한 높을 것이다.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sh25iv78fc4swqlrxomh.png)

Lighthouse 점수를 개선해야 한다면, 최종 사용자 입장에서 볼 수 있는 프로덕션 환경에서 개선하는 것이 더 적절하다고 할 수 있다.

&nbsp;

## 마무리

LCP를 개선하는 과정을 통해서 얻은 내용을 정리하면 이렇다.

- Lighthouse 실행 시점 마다 점수의 격차가 크다.
- 최적화된 이미지를 저장하고, 불러오고, 보여주는 것이 LCP에 영향이 크다.
- vite.config.js 에서 크기가 큰 모듈들을 코드 분할하여 불필요한 JS를 제거해 주는 것으로도 LCP 향상에 도움이 된다.
- Lighthouse 측정은 프로덕션 환경에서 진행하는 것을 권장한다.
- LCP는 데이터가 불러오는 속도에 영향을 많이 받기 때문에 SSR 방식도 고려하면 좋을 것 같다.

앞서 말했던 것 처럼 LCP 점수가 절대적이거나 중요한 것은 아니다. 하지만 높은 점수는 사용자에게 좋은 경험을 제공하는 것은 분명하다.

그리고 LCP 시간은 2.5s 이하면 좋다고 한다. 앞으로 최적화를 진행할 때 기준을 두고 진행하면 좋을 것 같다.

긴 글 읽어주셔서 감사합니다.

---

[참고한 글]
[Lighthouse를 기반으로 LCP 성능 개선하기](https://velog.io/@aborrencce/Lighthouse%EB%A5%BC-%EA%B8%B0%EB%B0%98%EC%9C%BC%EB%A1%9C-LCP-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0)
[Light House에서 LCP 측정항목 개선해서 성능점수 올리기](https://velog.io/@my_suwan/%EB%82%B4%EA%B0%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EC%B5%9C%EC%A0%81%ED%99%94%EB%A5%BC-%EC%A7%84%ED%96%89%ED%96%88%EB%8D%98-%EB%B0%A9%EB%B2%95%EB%93%A4-With-React)
[라이트하우스와 함께한 성능 개선 고군분투기](https://velog.io/@edie_ko/lighthouse-performance#4-image-lazy-loading)
[내가 프로젝트에서 최적화를 진행했던 방법들 (With React+Vite)](https://velog.io/@my_suwan/%EB%82%B4%EA%B0%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EC%B5%9C%EC%A0%81%ED%99%94%EB%A5%BC-%EC%A7%84%ED%96%89%ED%96%88%EB%8D%98-%EB%B0%A9%EB%B2%95%EB%93%A4-With-React)