핵심 Web Vital을 개선한 방법(사례 연구)
게시 됨: 2022-03-10작년에 Google은 핵심 Web Vitals의 중요성과 웹 사이트를 방문할 때 개인의 실제 경험을 반영하는 방법을 강조하기 시작했습니다. 성능은 우리 회사 Instant Domain Search의 핵심 기능입니다. 이름에 있습니다. 우리의 중요한 점수가 많은 사람들에게 좋지 않다는 것을 알았을 때 우리가 얼마나 놀랐을지 상상해 보십시오. 우리의 빠른 컴퓨터와 광섬유 인터넷은 실제 사람들이 우리 사이트에서 경험하는 경험을 가렸습니다. 오래지 않아 Google Search Console의 빨간색 "불량" 및 노란색 "개선 필요" 알림이 우리의 주의를 필요로 했습니다. Entropy가 승리했고 버벅거림을 제거하고 사이트를 더 빠르게 만드는 방법을 찾아야 했습니다.
나는 2005년 Instant Domain Search를 설립했고 Facebook에서 소프트웨어 엔지니어로 일하기 전에 Y Combinator 회사(Snipshot, W06)에서 일하는 동안 부업으로 유지했습니다. 우리는 최근에 주로 캐나다 빅토리아에 기반을 둔 소규모 그룹으로 성장했으며 새로운 기능과 성능 개선에 대한 오랜 백로그를 통해 작업하고 있습니다. 열악한 웹 바이탈스 점수와 다가오는 Google 업데이트로 인해 이러한 문제를 찾고 수정하는 데 중점을 두었습니다.
사이트의 첫 번째 버전이 시작되었을 때 PHP, MySQL 및 XMLHttpRequest로 사이트를 구축했습니다. Internet Explorer 6은 완벽하게 지원되었고 Firefox는 점유율을 얻었고 Chrome은 출시되기 몇 년 전이었습니다. 시간이 지남에 따라 우리는 다양한 정적 사이트 생성기, JavaScript 프레임워크 및 서버 기술을 통해 발전해 왔습니다. 우리의 현재 프론트엔드 스택은 Next.js와 함께 제공되는 React 및 도메인 이름 검색에 응답하기 위해 Rust가 내장된 백엔드 서비스입니다. 우리는 가능한 한 많은 제3자 스크립트를 피하고 비트맵 PNG 대신 간단한 SVG 그래픽을 사용하여 CDN을 통해 최대한 많은 서비스를 제공함으로써 모범 사례를 따르려고 노력합니다. 충분하지 않았습니다.
Next.js를 사용하면 React 및 TypeScript에서 페이지와 구성 요소를 빌드할 수 있습니다. VS Code와 함께 사용하면 개발 경험이 놀랍습니다. Next.js는 일반적으로 React 구성 요소를 정적 HTML 및 CSS로 변환하여 작동합니다. 이렇게 하면 초기 콘텐츠가 CDN에서 제공될 수 있고 Next는 페이지를 "수화"하여 요소를 동적으로 만들 수 있습니다. 페이지가 수화되면 우리 사이트는 사람들이 도메인 이름을 검색하고 생성할 수 있는 단일 페이지 앱으로 바뀝니다. 우리는 많은 서버 측 작업을 수행하기 위해 Next.js에 의존하지 않으며 대부분의 콘텐츠는 CDN에서 제공되는 HTML, CSS 및 JavaScript로 정적으로 내보내집니다.
누군가 도메인 이름을 검색하기 시작하면 페이지 콘텐츠를 검색 결과로 바꿉니다. 검색을 최대한 빠르게 하기 위해 프런트 엔드는 도메인 조회 및 제안에 크게 최적화된 Rust 백엔드를 직접 쿼리합니다. 많은 쿼리에 즉시 응답할 수 있지만 일부 TLD의 경우 해결하는 데 1~2초가 소요될 수 있는 느린 DNS 쿼리를 수행해야 합니다. 이러한 느린 쿼리 중 일부가 해결되면 새로운 정보가 들어오는 대로 UI를 업데이트합니다. 결과 페이지는 사람마다 다르며 각 사람이 사이트를 경험하는 방식을 정확히 예측하기 어려울 수 있습니다.
Chrome DevTools는 훌륭하며 성능 문제를 추적할 때 시작하기에 좋은 곳입니다. 성능 보기는 HTTP 요청이 나가는 시간, 브라우저가 JavaScript를 평가하는 데 시간을 소비하는 위치 등을 정확하게 보여줍니다.
Google이 예정된 검색 알고리즘 업데이트에서 사이트 순위를 지정하는 데 사용할 세 가지 핵심 성능 평가 지표가 있습니다. Google은 실제 사람들이 사이트에서 가지고 있는 LCP, FID 및 CLS 점수를 기반으로 경험을 "양호", "개선 필요" 및 "나쁨"으로 분류합니다.
- LCP 또는 Largest Contentful Paint는 가장 큰 콘텐츠 요소가 표시되는 데 걸리는 시간을 정의합니다.
- FID 또는 첫 번째 입력 지연은 상호 작용에 대한 사이트의 응답성(인터페이스의 탭, 클릭 또는 키 누르기와 페이지의 응답 사이의 시간)과 관련이 있습니다.
- CLS 또는 누적 레이아웃 이동은 키보드 또는 클릭 이벤트와 같은 동작이 없는 페이지에서 요소가 이동하거나 이동하는 방식을 추적합니다.
Chrome은 로그인한 모든 Chrome 사용자에 대해 이러한 측정항목을 추적하도록 설정되어 있으며 사이트에서 고객의 경험을 요약한 익명의 통계를 평가를 위해 Google로 다시 보냅니다. 이 점수는 Chrome 사용자 경험 보고서를 통해 액세스할 수 있으며 PageSpeed Insights 도구로 URL을 검사할 때 표시됩니다. 점수는 지난 28일 동안 해당 URL을 방문한 사람들의 75번째 백분위수 경험을 나타냅니다. 이것은 업데이트에서 사이트 순위를 지정하는 데 사용할 숫자입니다.
75번째 백분위수(p75) 측정항목은 성능 목표에 대한 합리적인 균형을 유지합니다. 예를 들어, 평균을 취하면 사람들이 가지고 있는 많은 나쁜 경험을 숨길 수 있습니다. 중앙값 또는 50번째 백분위수(p50)는 우리 제품을 사용하는 사람들의 절반이 더 나쁜 경험을 하고 있음을 의미합니다. 반면에 95번째 백분위수(p95)는 연결이 불규칙한 오래된 장치에서 너무 많은 극단적인 이상값을 캡처하기 때문에 구축하기 어렵습니다. 우리는 75번째 백분위수를 기준으로 한 점수가 충족하기에 공정한 기준이라고 생각합니다.
점수를 제어하기 위해 먼저 Chrome에 내장되어 있고 web.dev/measure/ 및 PageSpeed Insights에서 호스팅되는 몇 가지 우수한 도구를 위해 Lighthouse로 눈을 돌렸습니다. 이러한 도구는 사이트에서 몇 가지 광범위한 기술 문제를 찾는 데 도움이 되었습니다. Next.js가 CSS를 번들로 묶는 방식과 FID에 영향을 미치는 초기 렌더링 시간을 늦추는 방식을 확인했습니다. 첫 번째 쉬운 승리는 실험적인 Next.js 기능인 optimizeCss 덕분에 일반적인 성능 점수를 크게 향상시키는 데 도움이 되었습니다.
Lighthouse는 또한 일부 정적 자산이 CDN에서 제공되지 않도록 하는 잘못된 캐시 구성을 포착했습니다. 우리는 Google Cloud Platform에서 호스팅되며 Google Cloud CDN에서는 Cache-Control 헤더에 '공개'가 포함되어야 합니다. Next.js에서는 내보내는 모든 헤더를 구성할 수 없으므로 Go에서 구현된 경량 HTTP 프록시 서버인 Caddy 뒤에 Next.js 서버를 배치하여 헤더를 재정의해야 했습니다. 우리는 또한 CDN이 백그라운드에서 비동기적으로 원본(Next.js 서버)에서 콘텐츠를 가져올 수 있도록 하는 최신 브라우저의 비교적 새로운 stale-while-revalidate 지원으로 우리가 할 수 있는 것을 제공하고 있는지 확인할 기회를 얻었습니다.
npm에서 제품에 필요한 거의 모든 것을 추가하는 것은 쉽습니다. 어쩌면 너무 쉬울 수도 있습니다. 번들 크기가 커지는 데 오랜 시간이 걸리지 않습니다. 큰 번들은 느린 네트워크에서 다운로드하는 데 시간이 더 오래 걸리고 75번째 백분위수 휴대 전화는 방금 다운로드한 모든 코드를 이해하려고 시도하는 동안 기본 UI 스레드를 차단하는 데 많은 시간을 할애합니다. 우리는 npm 패키지가 번들에 추가할 종속성과 바이트 수를 보여주는 무료 도구인 BundlePhobia를 좋아했습니다. 이로 인해 우리는 여러 반응 스프링 기반 애니메이션을 제거하거나 더 간단한 CSS 전환으로 대체했습니다.
BundlePhobia 및 Lighthouse를 사용하여 타사 오류 로깅 및 분석 소프트웨어가 번들 크기와 로드 시간에 크게 기여했음을 발견했습니다. 이러한 도구를 제거하고 sendBeacon 및 ping과 같은 최신 브라우저 API를 활용하는 자체 클라이언트 측 로깅으로 교체했습니다. 우리는 기성 도구가 제공할 수 있는 것보다 더 자세히 관심 있는 질문에 답변할 수 있는 자체 Google BigQuery 인프라에 로깅 및 분석을 보냅니다. 이것은 또한 다수의 제3자 쿠키를 제거하고 우리가 클라이언트로부터 로깅 데이터를 보내는 방법과 시기를 훨씬 더 많이 제어할 수 있게 해줍니다.
우리의 CLS 점수는 여전히 개선의 여지가 가장 많았습니다. Google이 CLS를 계산하는 방식은 복잡합니다. 초기 페이지 로드 또는 키보드 또는 클릭 상호작용에서 최대 5초로 제한되는 1초 간격의 최대 "세션 창"이 주어져서 사이트에서 모든 것을 이동하는 것을 완료할 수 있습니다. . 이 주제에 대해 더 자세히 읽고 싶다면 여기 주제에 대한 훌륭한 가이드가 있습니다. 이것은 사이트에 도착한 직후에 나타나는 많은 유형의 오버레이 및 팝업에 불이익을 줍니다. 예를 들어 콘텐츠를 이동하는 광고 또는 콘텐츠에 도달하기 위해 광고를 스크롤하기 시작할 때 나타날 수 있는 상향 판매. 이 기사는 CLS 점수가 계산되는 방법과 그 이유에 대한 훌륭한 설명을 제공합니다.
우리는 이러한 종류의 디지털 혼란에 근본적으로 반대하므로 Google이 우리가 주장하는 개선의 여지가 얼마나 많은지 보고 놀랐습니다. Chrome에는 명령 메뉴를 사용하여 "핵심 Web Vitals 오버레이 표시"에 액세스할 수 있는 Web Vitals 오버레이가 내장되어 있습니다. Chrome이 CLS 계산에서 고려하는 요소를 정확히 확인하기 위해 설정에서 Chrome Web Vitals 확장 프로그램의 "콘솔 로깅" 옵션이 더 유용하다는 것을 알았습니다. 활성화되면 이 플러그인은 현재 페이지에 대한 LCP, FID 및 CLS 점수를 표시합니다. 콘솔에서 페이지의 어떤 요소가 이 점수에 연결되어 있는지 정확히 볼 수 있습니다. 우리의 CLS 점수는 개선의 여지가 가장 많았습니다.
세 가지 메트릭 중 CLS는 페이지와 상호 작용할 때 누적되는 유일한 것입니다. Web Vitals 확장에는 제품과 상호 작용하는 동안 CLS를 유발하는 요소를 정확히 표시하는 로깅 옵션이 있습니다. Smashing Magazine 홈페이지에서 스크롤할 때 CLS 메트릭이 어떻게 추가되는지 확인하세요.
Google은 시간이 지남에 따라 CLS를 계산하는 방법을 계속 조정할 것이므로 Google의 웹 개발 블로그를 팔로우하여 최신 정보를 받는 것이 중요합니다. Chrome Web Vitals 확장 프로그램과 같은 도구를 사용할 때 보다 현실적인 경험을 얻으려면 CPU 및 네트워크 조절을 활성화하는 것이 중요합니다. 모바일 CPU를 시뮬레이션하여 개발자 도구로 이를 수행할 수 있습니다.
한 배포에서 다음 배포까지 진행 상황을 추적하는 가장 좋은 방법은 Google과 동일한 방식으로 페이지 경험을 측정하는 것입니다. Google Analytics가 설정되어 있는 경우 이를 수행하는 쉬운 방법은 Google의 web-vitals 모듈을 설치하고 Google Analytics에 연결하는 것입니다. 이렇게 하면 진행 상황을 대략적으로 측정할 수 있으며 Google Analytics 대시보드에서 볼 수 있습니다.
이것은 우리가 벽에 부딪힌 곳입니다. 우리는 CLS 점수를 볼 수 있었고 크게 향상되었지만 여전히 해야 할 일이 있었습니다. 우리의 CLS 점수는 대략 0.23이었고 이것을 0.1 미만으로, 가급적이면 0으로 낮추어야 했습니다. 하지만 이 시점에서 우리는 페이지의 어떤 구성 요소가 여전히 점수에 영향을 미치는지 정확히 알려주는 것을 찾을 수 없었습니다. 우리는 Chrome이 Core Web Vitals 도구에서 많은 세부사항을 노출했음을 알 수 있었지만, 로깅 수집기는 가장 중요한 부분인 정확히 어떤 페이지 요소가 문제를 일으켰는지 버린 것입니다.
필요한 모든 세부 정보를 캡처하기 위해 브라우저에서 웹 필수 데이터를 캡처하는 서버리스 기능을 구축했습니다. 데이터에 대해 실시간 쿼리를 실행할 필요가 없으므로 저장을 위해 Google BigQuery의 스트리밍 API로 스트리밍합니다. 이 아키텍처는 생성할 수 있는 많은 데이터 포인트를 저렴하게 캡처할 수 있음을 의미합니다.
Web Vitals 및 BigQuery로 작업하면서 몇 가지 교훈을 얻은 후, 우리는 이 기능을 번들로 묶고 이 도구를 vitals.dev에서 오픈 소스로 출시하기로 결정했습니다.
Instant Vitals를 사용하면 BigQuery에서 Web Vitals 점수 추적을 빠르게 시작할 수 있습니다. 다음은 생성한 BigQuery 테이블 스키마의 예입니다.
Instant Vitals와 통합하는 것은 쉽습니다. 백엔드 또는 서버리스 기능으로 데이터를 보내기 위해 클라이언트 라이브러리와 통합하여 시작할 수 있습니다.
import { init } from "@instantdomain/vitals-client"; init({ endpoint: "/api/web-vitals" });
그런 다음 서버에서 서버 라이브러리와 통합하여 회로를 완성할 수 있습니다.
import fs from "fs"; import { init, streamVitals } from "@instantdomain/vitals-server"; // Google libraries require service key as path to file const GOOGLE_SERVICE_KEY = process.env.GOOGLE_SERVICE_KEY; process.env.GOOGLE_APPLICATION_CREDENTIALS = "/tmp/goog_creds"; fs.writeFileSync( process.env.GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_SERVICE_KEY ); const DATASET_; init({ datasetId: DATASET_ID }).then().catch(console.error); // Request handler export default async (req, res) => { const body = JSON.parse(req.body); await streamVitals(body, body.name); res.status(200).end(); };
요청 본문과 측정항목 이름을 사용하여 streamVitals
를 호출하여 측정항목을 BigQuery로 보내기만 하면 됩니다. 라이브러리는 데이터 세트와 테이블 생성을 처리합니다.
하루 분량의 데이터를 수집한 후 이 쿼리를 다음과 같이 실행했습니다.
SELECT `<project_name>.web_vitals.CLS`.Value, Node FROM `<project_name>.web_vitals.CLS` JOIN UNNEST(Entries) AS Entry JOIN UNNEST(Entry.Sources) WHERE Node != "" ORDER BY value LIMIT 10
이 쿼리는 다음과 같은 결과를 생성합니다.
값 | 마디 |
---|---|
4.6045324800736724E-4 | /html/body/div[1]/main/div/div/div[2]/div/div/blockquote |
7.183070668914928E-4 | /html/body/div[1]/header/div/div/header/div |
0.031002668277977697 | /html/body/div[1]/footer |
0.035830703317463526 | /html/body/div[1]/main/div/div/div[2] |
0.035830703317463526 | /html/body/div[1]/footer |
0.035830703317463526 | /html/body/div[1]/main/div/div/div[2] |
0.035830703317463526 | /html/body/div[1]/main/div/div/div[2] |
0.035830703317463526 | /html/body/div[1]/footer |
0.035830703317463526 | /html/body/div[1]/footer |
0.03988482067913317 | /html/body/div[1]/footer |
이것은 어떤 페이지에서 CLS에 가장 큰 영향을 미치는 요소를 보여줍니다. 그것은 우리 팀이 조사하고 고칠 펀치 목록을 만들었습니다. Instant Domain Search에서 모바일 연결이 느리거나 불량하면 일부 검색 결과를 로드하는 데 500ms 이상이 걸리는 것으로 나타났습니다. 이러한 사용자를 위한 CLS의 최악의 기여자 중 하나는 실제로 우리의 바닥글이었습니다.
레이아웃 이동 점수는 이동하는 요소의 크기와 이동 거리의 함수로 계산됩니다. 검색 결과 보기에서 기기가 검색 결과를 수신하고 렌더링하는 데 일정 시간 이상 걸리면 결과 보기가 zero-height
축소되어 바닥글이 표시됩니다. 결과가 나오면 바닥글을 페이지 맨 아래로 다시 밀어 넣습니다. 여기까지 이동한 큰 DOM 요소는 CLS 점수에 많은 것을 추가했습니다. 이를 제대로 수행하려면 검색 결과가 수집되고 렌더링되는 방식을 재구성해야 합니다. 우리는 검색 결과 보기에서 바닥글을 제거하기로 결정했습니다. 느린 연결에서 바닥글이 튀는 것을 막을 수 있는 빠른 해킹입니다.
이제 이 보고서를 정기적으로 검토하여 개선 방법을 추적하고 앞으로 나아가면서 감소하는 결과를 방지하는 데 사용합니다. 우리는 사이트에서 새로 출시된 기능과 제품에 대한 특별한 관심의 가치를 목격했으며 핵심 핵심이 우리 순위에 유리하게 작용하고 있는지 확인하기 위해 일관된 검사를 운영했습니다. Instant Vital을 공유하여 다른 개발자도 Core Web Vital 점수를 처리하는 데 도움이 되기를 바랍니다.
Google은 Chrome에 내장된 우수한 성능 도구를 제공하며 이를 사용하여 여러 성능 문제를 찾고 수정했습니다. Google에서 제공한 현장 데이터가 p75 진행 상황에 대한 좋은 요약을 제공했지만 실행 가능한 세부 정보가 없다는 것을 알게 되었습니다. 어떤 DOM 요소가 레이아웃 이동과 입력 지연을 일으키는지 정확히 알아내야 했습니다. XPath 쿼리를 사용하여 자체 필드 데이터를 수집하기 시작하자 사이트에서 모든 사람의 경험을 향상시킬 수 있는 특정 기회를 식별할 수 있었습니다. 약간의 노력으로 6월의 페이지 경험 업데이트를 준비하기 위해 실제 핵심 성능 평가 필드 점수를 수용 가능한 범위로 낮췄습니다. 우리는 이 숫자가 오른쪽으로 내려가는 것을 보게 되어 기쁩니다!