- PageSpeed Insights
AI가 번역한 다른 언어 보기
durumis AI가 요약한 글
- 웹 성능 측정 도구(PageSpeed Insights, Lighthouse)를 활용하여 웹사이트 성능을 개선하면 검색엔진 노출에 유리하며, Web Core Vitals 지표가 중요하다.
- CLS, LCP, FCP, FID 등의 지표 개선을 위해 UI 스켈레톤, 이미지 최적화, 렌더링 차단 요소 제거, 웹 폰트 최적화, 자바스크립트 비동기 처리 등을 적용할 수 있다.
- 각 지표 개선 방법과 함께 웹 성능 향상을 위한 다양한 기술(gzip 압축, 웹 워커 등)을 소개하며, 향후 관련 내용을 심화적으로 다룰 예정이다.
구글에서 제공하는 사이트를 통해
웹 성능을 손쉽게 측정할 수 있다.
https://pagespeed.web.dev/
에 url을 입력하여 측정하거나, 크롬 확장 프로그램으로 lighthouse를 설치하여 측정할 수 있다.
지표를 개선하면 무엇이 좋은가?
검색엔진에 더 잘 노출될 수 있다. SEO로 인해 서버 사이드 렌더링을 많이 사용하는데,
만약 위의 성능지표가 낮다면 그 효과가 뚝 떨어질 것이다.
어떻게 개선하는가?
web.dev 사이트에서 각 지표를 어떻게 개선할 수 있는지 상세하게 설명되어 있다.
특히 3개의 지표가 점수에 큰 부분을 차지하고 있다. (Web Core Vitals)
지표를 개선하기 위해 시도했던 것들을 기록하고자 한다.
local에서 측정하려면?
export default () => { if (process.client && 'PerformanceObserver' in window) { let cumulativeLayoutShiftScore = 0; let largestContentfulPaint = 0; let firstInputDelay = 0; let firstContentfulPaint = 0;
// CLS const clsObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach((entry) => { if (!entry.hadRecentInput) { cumulativeLayoutShiftScore += entry.value; } }); }); // LCP const lcpObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); const lastEntry = entries[entries.length - 1]; largestContentfulPaint = lastEntry.startTime; }); // FID const fidObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); const firstEntry = entries[0]; firstInputDelay = firstEntry.processingStart - firstEntry.startTime; }); // FCP const fcpObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); const firstEntry = entries[0]; firstContentfulPaint = firstEntry.startTime; }); clsObserver.observe({ type: 'layout-shift', buffered: true }); lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true }); fidObserver.observe({ type: 'first-input', buffered: true }); fcpObserver.observe({ type: 'paint', buffered: true }); window.addEventListener('onload', () => { console.log('Final CLS Score:', cumulativeLayoutShiftScore); console.log('LCP:', largestContentfulPaint); console.log('FID:', firstInputDelay); console.log('FCP:', firstContentfulPaint); }); } };
위 코드를 삽입한다. nuxt의 경우 plugins에 넣어놓고 console log를 확인한다.
Cumulative Layout Shift (CLS)
렌더링되면서 레이아웃이 변화되는 정도를 측정한 것이다.
예를들어, API응답을 통해 이미지 URL을 받아 400x400 이미지를 렌더링하는 페이지가 있다고 가정하면,
어떤 레이아웃의 위치는 이미지가 렌더링됨에 따라 400px만큼 밀릴 것이다.
이것은 사용자가 예측하지 않은 이동으로, UX에 안좋을 뿐더러 브라우저 또한 화면을 렌더링하는데
간접적으로 악영향을 받는다.
개선하기 위한 쉬운 방법으로 2가지가 있다.
1. UI 스켈레톤 적용하기
- 대부분의 사이트에서 이미지 크기 또는 컴포넌트 크기에 맞는 스켈레톤을 적절히 그려낸다. 화면을 개발할 때 이 부분을 고려한다. vue의 경우 v-if와 v-else를 활용하여 각 요소에 맞는 스켈레톤을 그려낸다.
2. 높이를 예약하기
- 스켈레톤에 너무 많은 스타일이 들어가면 유지보수가 복잡해지고, 역효과가 날 수 있다. 단순히 렌더링 될 요소의 min-height를 잡아주는 것만으로도 효과를 볼 수 있다.
- 반응형 웹 등 이미지 크기가 디바이스별로 다른 경우, height를 고정적으로 잡기 어렵다. 이 경우는 가장 작은 디바이스를 기준 또는 vh나 vw를 활용할 수 있다.
3. 이미지 요소에는 width와 height를 명시하기
- 이미지 요소에 width와 height를 다음과 같이 넣을 수 있다.
<img src="image.jpg" width="400" height="400">
실제 이미지 크기가 400x400이 아니더라도 브라우저에게 이미지의 예상 크기를 알린다.
4. 웹 폰트로 인한 레이아웃 변화를 방지하기 위해 font-display: swap를 사용하기
- 대체 폰트를 표기함으로써 레이아웃 밀림을 방지한다.
Largest Contentful Paint(LCP)
화면에서 가장 큰 부분을 차지하고 있는 요소가 렌더링되는데 걸리는 시간이다. 보통은 이미지일텐데, 직관적으로 로딩속도를 빠르게 하면 된다.
1. 이미지 크기를 줄인다.
- 렌더링 크기에 비해 이미지 크기가 크지 않은지 체크한다.
- webp 등의 확장자로 이미지 렌더링을 최적화한다. webp는 손실 압축을 사용할 경우 JPEG 포맷보다 30% 정도 파일 크기가 작고, 무손실 압축을 사용할 경우 png보다 20%정도 크기가 작다.
- 스크롤 위치에 따라 로딩되는 lazy loading도 고려할 수 있다.
2. 캐싱이나 CDN을 활용한다.
3. 렌더링 차단 요소를 제거한다.
- 대부분의 측정 지표가 영향을 받으므로, 꼭 제거한다. 브라우저는 화면을 렌더링할 때 head 태그 쪽 script들을 먼저 가져온 다음 렌더링하므로, head 태그 내의 스크립트(일반적으로 외부 스크립트)가 먼저 렌더링 되어야 하는지 체크한다. 관련하여 async와 defer 태그를 활용하면 손쉽게 관리할 수 있다.
* head 내의 스크립트 속성과 관련해서는 추후 다른 글을 쓰겠다.
4. 번들 크기를 최소화한다.
- 이미지가 렌더링되는 속도가 느린 것과 별개로, 서버로부터 전달이 느리면 이것을 개선할 필요가 있다.
nginx와 같은 프록시 서버를 사용한다면 gzip 압축을 활용할 수 있다.
* nginx 설정과 관련해서는 추후 또 다른 글을 쓰겠다.
First Contentful Paint(FCP)
페이지에서 첫 번째 콘텐츠가 렌더링되는 시점을 의미한다.
1. 마찬가지로 렌더링 차단 요소를 제거한다.
2. html 크기를 최소화한다.
- 무분별하게 div div 태그로 감싸다보면 매우 중첩된 트리가 생성되고 파일 크기도 커진다.
3. 외부 웹 폰트가 문제일 수 있다. font-display: swap; 를 적극 활용하여 기본 폰트를 먼저 렌더링한다.
First Input Delay(FID)
사용자가 페이지에서 첫 번째 입력(클릭, 스크롤 등)을 했을 때 브라우저가 이를 처리하는 데 걸리는 시간을 의미한다. 간혹 버튼을 눌러도 먹통인 경우가 있는데, 실은 먹통이 아니라 API 응답을 기다리고 있을 수도 있다.
1. 비동기를 잘 활용해서 자바스크립트 메인 스레드가 무거운 작업에 묶이지 않도록 개발한다.
2. 메인스레드와 별개로 웹 워커를 사용하여 또 다른 작업을 진행할 수 있다.
* 웹 워커에 대해서는 추후 또 또 다른 글을 쓰겠다.
글자 수 제한으로 마치겠다.