분석을 통해 실제 사용자 문제를 식별하고 해결하는 데 도움이 되도록 성능 데이터에 디버그 정보를 제공하는 방법을 알아보세요.
Google은 성능을 측정하고 디버그할 수 있는 두 가지 카테고리의 도구를 제공합니다.
- 실험실 도구: 다양한 조건을 모방할 수 있는 시뮬레이션된 환경 (예: 느린 네트워크, 저사양 휴대기기)에서 페이지를 로드하는 Lighthouse와 같은 도구입니다.
- 필드 도구: Chrome의 실제 사용자 집계 데이터를 기반으로 하는 Chrome 사용자 환경 보고서(CrUX)와 같은 도구입니다. PageSpeed Insights 및 Search Console과 같은 도구에서 보고되는 필드 데이터는 CrUX 데이터에서 가져옵니다.
현장 도구가 더 정확한 데이터, 즉 실제 사용자가 경험하는 것을 실제로 나타내는 데이터인 반면, 실습 도구가 문제를 식별하고 수정하는 데 더 효과적인 경우가 많습니다.
CrUX 데이터는 페이지의 실제 성능을 더 잘 나타내지만, CrUX 점수를 아는 것은 성능 개선 방법을 파악하는 데 도움이 되지 않습니다.
반면 Lighthouse는 문제를 식별하고 개선 방법을 구체적으로 제안합니다. 하지만 Lighthouse는 페이지 로드 시 발견한 성능 문제에 관해서만 제안합니다. 페이지의 스크롤 또는 버튼 클릭과 같은 사용자 상호작용의 결과로 나타나는 문제만 감지하지 않습니다.
따라서 중요한 질문이 생깁니다. 어떻게 하면 현장의 실제 사용자로부터 코어 웹 바이탈의 디버그 정보 또는 기타 성능 측정항목을 캡처할 수 있을까요?
이 게시물에서는 현재 각 코어 웹 바이탈 측정항목의 추가 디버깅 정보를 수집하는 데 사용할 수 있는 API를 자세히 설명하고 기존 분석 도구에서 이 데이터를 캡처하는 방법에 관한 아이디어를 제공합니다.
기여 분석 및 디버깅용 API
CLS
모든 코어 웹 바이탈 측정항목 중에서 CLS는 아마도 필드에서 디버그 정보를 수집하는 것이 가장 중요한 측정항목일 것입니다. CLS는 페이지의 전체 수명에 걸쳐 측정되므로 사용자가 페이지와 상호작용하는 방식(스크롤하는 거리, 클릭하는 항목 등)은 레이아웃 변경 여부와 이동하는 요소에 상당한 영향을 미칠 수 있습니다.
PageSpeed Insights에서 다음과 같은 보고서를 살펴보세요.
실험실에서 CLS (Lighthouse)에 보고된 값은 현장의 CLS (CrUX 데이터)와 상당히 다릅니다. 이는 Lighthouse에서 테스트할 때 사용되지 않는 대화형 콘텐츠가 페이지에 많을 수 있다고 간주할 때 적합합니다.
하지만 사용자 상호작용이 필드 데이터에 영향을 미친다는 사실을 알더라도 페이지에서 어떤 요소가 변화하여 75번째 백분위 수에서 0.3점이 나오는지 파악해야 합니다.
LayoutShiftAttribution 인터페이스를 사용하면 가능합니다.
레이아웃 변경 기여 분석 가져오기
LayoutShiftAttribution 인터페이스는 Layout Instability API가 내보내는 각 layout-shift
항목에 노출됩니다.
이 두 인터페이스에 관한 자세한 설명은 레이아웃 변경 디버그를 참고하세요. 이 게시물의 목적상 개발자는 페이지에서 발생하는 모든 레이아웃 변경과 이동 중인 요소를 관찰할 수 있어야 합니다.
다음은 각 레이아웃 변경과 이동한 요소를 기록하는 코드의 예입니다.
new PerformanceObserver((list) => {
for (const {value, startTime, sources} of list.getEntries()) {
// Log the shift amount and other entry info.
console.log('Layout shift:', {value, startTime});
if (sources) {
for (const {node, curRect, prevRect} of sources) {
// Log the elements that shifted.
console.log(' Shift source:', node, {curRect, prevRect});
}
}
}
}).observe({type: 'layout-shift', buffered: true});
레이아웃 변경이 발생할 때마다 데이터를 측정하고 분석 도구로 전송하는 것은 실용적이지 않을 수 있습니다. 그러나 모든 이동을 모니터링하면 최악의 변화를 추적하고 이에 관한 정보만 보고할 수 있습니다.
목표는 모든 사용자에게 발생하는 모든 레이아웃 변경을 식별하고 수정하는 것이 아닙니다. 목표는 가장 많은 사용자에게 영향을 주어 페이지의 CLS에 가장 많이 기여하는 변경을 식별하는 것입니다(75번째 백분위수).
또한 시프트가 있을 때마다 가장 큰 소스 요소를 계산할 필요는 없으며, CLS 값을 분석 도구에 전송할 준비가 된 경우에만 계산하면 됩니다.
다음 코드는 CLS에 기여한 layout-shift
항목의 목록을 가져와 가장 큰 이동에서 가장 큰 소스 요소를 반환합니다.
function getCLSDebugTarget(entries) {
const largestEntry = entries.reduce((a, b) => {
return a && a.value > b.value ? a : b;
});
if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
const largestSource = largestEntry.sources.reduce((a, b) => {
return a.node && a.previousRect.width * a.previousRect.height >
b.previousRect.width * b.previousRect.height ? a : b;
});
if (largestSource) {
return largestSource.node;
}
}
}
가장 큰 변화에 기여하는 가장 큰 요소를 식별하면 이를 애널리틱스 도구에 보고할 수 있습니다.
특정 페이지의 CLS에 가장 많이 기여하는 요소는 사용자마다 다를 수 있지만 모든 사용자의 요소를 집계하면 가장 많은 사용자 수에 영향을 미치는 변화하는 요소의 목록을 생성할 수 있습니다.
이러한 요소 이동의 근본 원인을 파악하고 수정하면 애널리틱스 코드에서 작은 변화만 페이지의 '최악' 변동으로 보고하기 시작합니다. 결국 보고된 모든 변경은 페이지가 '양호' 기준인 0.1 내에 들 만큼 작아지게 됩니다.
최대 변화 소스 요소와 함께 캡처하는 것이 유용할 수 있는 다른 메타데이터는 다음과 같습니다.
- 최대 변동 시간
- 단일 페이지 애플리케이션과 같이 URL을 동적으로 업데이트하는 사이트의 경우 최대 이동 시점의 URL 경로입니다.
LCP
필드에서 LCP를 디버그하기 위해 필요한 기본 정보는 특정 페이지 로드에서 가장 큰 요소 (LCP 후보 요소)가 무엇인지입니다.
완전히 동일한 페이지에서도 LCP 후보 요소가 사용자마다 다를 수 있습니다(실제로 매우 일반적).
여러 이유로 이 문제가 발생할 수 있습니다.
- 사용자 기기의 화면 해상도가 달라 페이지 레이아웃이 다르므로 표시 영역 내에 다른 요소가 표시됩니다.
- 사용자가 맨 위로 스크롤한 페이지를 항상 로드하지는 않습니다. 링크에 프래그먼트 식별자 또는 텍스트 프래그먼트가 포함되는 경우가 많습니다. 즉, 페이지의 어떤 스크롤 위치에서든 페이지가 로드되어 표시될 수 있습니다.
- 콘텐츠가 현재 사용자에 맞게 맞춤설정될 수 있으므로 LCP 후보 요소는 사용자마다 크게 다를 수 있습니다.
즉, 특정 페이지에서 어떤 요소 또는 요소 집합이 가장 일반적인 LCP 후보 요소가 될지 가정할 수 없습니다. 실제 사용자 행동을 기준으로 측정해야 합니다.
LCP 후보 요소 식별
JavaScript에서 LCP 후보 요소를 확인하려면 LCP 시간 값을 결정하는 데 사용하는 것과 동일한 API인 최대 Contentful Paint API를 사용하면 됩니다.
largest-contentful-paint
항목을 관찰할 때 마지막 항목의 element
속성을 확인하여 현재 LCP 후보 요소를 확인할 수 있습니다.
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP element:', lastEntry.element);
}).observe({type: 'largest-contentful-paint', buffered: true});
LCP 후보 요소를 알면 측정항목 값과 함께 애널리틱스 도구에 전송할 수 있습니다. 이렇게 하면 CLS와 마찬가지로 먼저 최적화할 가장 중요한 요소를 식별하는 데 도움이 됩니다.
LCP 후보 요소 외에도 LCP 하위 파트 시간을 측정하는 것도 유용할 수 있습니다. 이는 사이트와 관련된 특정 최적화 단계를 결정하는 데 유용할 수 있습니다.
FID
필드에서 FID를 디버그하려면 FID가 첫 번째 입력 이벤트 전체 지연 시간의 지연 부분만 측정한다는 점에 유의해야 합니다. 즉, 사용자가 상호작용한 내용이 상호작용 당시 기본 스레드에서 발생한 다른 작업만큼 중요하지 않습니다.
예를 들어 SSR(서버 측 렌더링)을 지원하는 많은 JavaScript 애플리케이션은 사용자 입력과 상호작용하기 전, 즉 대화형 콘텐츠를 만드는 데 필요한 JavaScript가 로드를 완료하기 전에 화면에 렌더링할 수 있는 정적 HTML을 제공합니다.
이러한 유형의 애플리케이션에서는 첫 번째 입력이 하이드레이션 전 또는 후에 발생했는지 아는 것이 매우 중요할 수 있습니다. 하이드레이션이 완료되기 전에 많은 사용자가 페이지와 상호작용하려고 시도하는 것으로 확인되면 대화형으로 보이는 상태가 아닌 사용 중지 또는 로드 상태로 페이지를 렌더링하는 것이 좋습니다.
애플리케이션 프레임워크가 수분 섭취 타임스탬프를 노출하는 경우 이를 first-input
항목의 타임스탬프와 비교하여 첫 번째 입력이 수분 섭취 전 또는 후에 발생했는지 확인할 수 있습니다. 프레임워크에서 이 타임스탬프를 노출하지 않거나 하이드레이션을 전혀 사용하지 않는 경우 또 다른 유용한 신호는 JavaScript가 로드를 완료하기 전이나 후에 입력이 발생했는지 여부일 수 있습니다.
DOMContentLoaded
이벤트는 페이지의 HTML이 완전히 로드되고 파싱된 후 실행됩니다. 여기에는 동기 스크립트, 지연된 스크립트 또는 모듈 스크립트 (정적으로 가져온 모든 모듈 포함)가 로드되기를 기다리는 과정이 포함됩니다. 따라서 해당 이벤트의 타이밍을 사용하고 FID가 발생한 시점과 비교할 수 있습니다.
다음 코드는 first-input
항목을 관찰하고 DOMContentLoaded
이벤트가 종료되기 전에 첫 번째 입력이 발생했는지 여부를 로깅합니다.
new PerformanceObserver((list) => {
const fidEntry = list.getEntries()[0];
const navEntry = performance.getEntriesByType('navigation')[0];
const wasFIDBeforeDCL =
fidEntry.startTime < navEntry.domContentLoadedEventStart;
console.log('FID occurred before DOMContentLoaded:', wasFIDBeforeDCL);
}).observe({type: 'first-input', buffered: true});
FID 대상 요소 및 이벤트 유형 식별
유용할 수 있는 추가적인 디버그 신호는 상호작용된 요소 및 상호작용 유형 (예: mousedown
, keydown
, pointerdown
)입니다. 요소 자체와의 상호작용은 FID에 영향을 미치지는 않지만 (FID는 총 이벤트 지연 시간의 지연 부분일 뿐임) 사용자가 상호작용하는 요소를 파악하면 FID를 개선하는 최선의 방법을 결정하는 데 유용할 수 있습니다.
예를 들어 사용자의 첫 번째 상호작용 중 대부분이 특정 요소와 관련된 경우 HTML에서 해당 요소에 필요한 자바스크립트 코드를 인라인 처리하고 나머지는 지연 로드를 사용해 보세요.
첫 번째 입력 이벤트와 연결된 상호작용 유형과 요소를 가져오려면 first-input
항목의 target
및 name
속성을 참고하세요.
new PerformanceObserver((list) => {
const fidEntry = list.getEntries()[0];
console.log('FID target element:', fidEntry.target);
console.log('FID interaction type:', fidEntry.name);
}).observe({type: 'first-input', buffered: true});
INP
INP는 필드에서 캡처할 가장 유용한 정보 비트가 다음과 같다는 점에서 FID와 매우 유사합니다.
- 상호작용한 요소
- 상호작용 유형의 이유
- 상호작용이 발생한 시점
FID와 마찬가지로 상호작용 속도가 느려지는 주요 원인은 차단된 기본 스레드로, JavaScript가 로드되는 동안 흔히 발생할 수 있습니다. 페이지 로드 중에 가장 느린 상호작용이 발생하는지 알면 문제를 해결하기 위해 취해야 할 조치를 파악하는 데 도움이 됩니다.
FID와 달리 INP 측정항목은 상호작용의 전체 지연 시간을 고려합니다. 여기에는 등록된 이벤트 리스너를 실행하는 데 걸리는 시간과 모든 이벤트 리스너가 실행된 후 다음 프레임을 페인트하는 데 걸리는 시간이 포함됩니다. 즉, INP의 경우 느린 상호작용을 유발하는 경향이 있는 타겟 요소와 이러한 상호작용의 유형을 아는 것이 훨씬 더 유용합니다.
INP와 FID는 모두 Event Timing API를 기반으로 하므로 JavaScript에서 이 정보를 결정하는 방법은 이전 예와 매우 유사합니다. 다음 코드는 INP 항목의 타겟 요소와 시간 (DOMContentLoaded
기준)을 로깅합니다.
function logINPDebugInfo(inpEntry) {
console.log('INP target element:', inpEntry.target);
console.log('INP interaction type:', inpEntry.name);
const navEntry = performance.getEntriesByType('navigation')[0];
const wasINPBeforeDCL =
inpEntry.startTime < navEntry.domContentLoadedEventStart;
console.log('INP occurred before DCL:', wasINPBeforeDCL);
}
이 코드는 어떤 event
항목이 INP 항목인지 확인하는 방법을 보여주지 않습니다. 이 로직이 더 복잡하기 때문입니다. 다음 섹션에서는 web-vitals JavaScript 라이브러리를 사용하여 이 정보를 가져오는 방법을 설명합니다.
web-vitals JavaScript 라이브러리와 함께 사용
위 섹션에서는 애널리틱스 도구로 보내는 데이터에 포함할 디버그 정보를 캡처하기 위한 몇 가지 일반적인 제안사항과 코드 예를 제공합니다.
버전 3부터 web-vitals JavaScript 라이브러리에는 이 모든 정보를 표시하는 기여 분석 빌드와 몇 가지 추가 신호도 포함되어 있습니다.
다음 코드 예에서는 성능 문제의 근본 원인을 파악하는 데 유용한 디버그 문자열이 포함된 추가 이벤트 매개변수 (또는 맞춤 측정기준)를 설정하는 방법을 보여줍니다.
import {onCLS, onFID, onINP, onLCP} from 'web-vitals/attribution';
function sendToGoogleAnalytics({name, value, id, attribution}) {
const eventParams = {
metric_value: value,
metric_id: id,
}
switch (name) {
case 'CLS':
eventParams.debug_target = attribution.largestShiftTarget;
break;
case 'LCP':
eventParams.debug_target = attribution.element;
break;
case 'FID':
case 'INP':
eventParams.debug_target = attribution.eventTarget;
break;
}
// Assumes the global `gtag()` function exists, see:
// https://developers.google.com/analytics/devguides/collection/ga4
gtag('event', name, eventParams);
}
onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onFID(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);
이 코드는 Google 애널리틱스에만 적용되지만 일반적인 개념은 다른 분석 도구로도 변환되어야 합니다.
이 코드는 단일 디버그 신호에 관해 보고하는 방법만 보여주지만, 측정항목별로 여러 신호를 수집하고 보고하는 것이 유용할 수 있습니다. 예를 들어 INP를 디버그하기 위해 상호작용 유형, 시간, 상호작용 중인 요소도 수집할 수 있습니다. web-vitals
기여 분석 빌드는 다음 예와 같이 이 모든 정보를 노출합니다.
import {onCLS, onFID, onINP, onLCP} from 'web-vitals/attribution';
function sendToGoogleAnalytics({name, value, id, attribution}) {
const eventParams = {
metric_value: value,
metric_id: id,
}
switch (name) {
case 'INP':
eventParams.debug_target = attribution.eventTarget;
eventParams.debug_type = attribution.eventType;
eventParams.debug_time = attribution.eventTime;
eventParams.debug_load_state = attribution.loadState;
break;
// Additional metric logic...
}
// Assumes the global `gtag()` function exists, see:
// https://developers.google.com/analytics/devguides/collection/ga4
gtag('event', name, eventParams);
}
onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onFID(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);
노출된 디버그 신호의 전체 목록은 웹 바이탈 저작자 표시 문서를 참고하세요.
데이터 보고 및 시각화
측정항목 값과 함께 디버그 정보를 수집하기 시작했다면 다음 단계는 모든 사용자의 데이터를 집계하여 패턴과 추세를 찾는 것입니다.
위에서 언급했듯이 사용자에게 발생하는 모든 문제를 해결할 필요는 없으며, 특히 가장 먼저 해결해야 하는 문제는 코어 웹 바이탈 점수에 가장 큰 부정적인 영향을 미치는 문제이기도 합니다.
GA4의 경우 BigQuery를 사용하여 데이터를 쿼리하고 시각화하는 방법에 대한 전용 도움말을 참고하세요.
요약
이 게시물이 기존 Performance API 및 web-vitals
라이브러리를 사용하여 디버그 정보를 얻어 현장의 실제 사용자 방문을 기반으로 성능을 진단하는 데 도움이 되는 구체적인 방법을 설명하는 데 도움이 되었기를 바랍니다. 이 가이드에서는 코어 웹 바이탈을 중점적으로 다루지만 자바스크립트에서 측정할 수 있는 모든 성능 측정항목을 디버깅하는 데도 개념이 적용됩니다.
성능 측정을 막 시작했고 이미 Google 애널리틱스 사용자라면 웹 바이탈 보고서 도구가 코어 웹 바이탈 측정항목의 디버그 정보 보고를 이미 지원하므로 웹 바이탈 보고서 도구를 사용하는 것이 좋습니다.
제품을 개선하고 사용자에게 더 많은 디버깅 정보를 제공하고자 하는 분석 공급업체라면 여기에 설명된 기술 중 일부를 고려하세요. 여기 제시된 아이디어로만 제한되지는 마세요. 이 게시물은 일반적으로 모든 분석 도구에 적용할 수 있도록 작성되었습니다. 그러나 개별 분석 도구는 훨씬 더 많은 디버그 정보를 캡처하고 보고할 수 있습니다.
마지막으로 API 자체에 기능이나 정보가 누락되어 이러한 측정항목을 디버그하는 기능에 차이가 있다고 생각되면 web-vitals-feedback@googlegroups.com으로 의견을 보내주세요.