현장에서 성능 디버그

분석을 통해 실제 사용자 문제를 파악하고 해결하는 데 도움이 되도록 디버그 정보로 성능 데이터에 기여도를 부여하는 방법을 알아보세요.

Google은 성능을 측정하고 디버그하는 두 가지 도구 카테고리를 제공합니다.

  • 실험실 도구: Lighthouse와 같은 도구로, 다양한 조건 (예: 느린 네트워크, 저가형 휴대기기)을 모방할 수 있는 시뮬레이션된 환경에서 페이지가 로드됩니다.
  • 현장 도구: Chrome의 집계된 실제 사용자 데이터를 기반으로 하는 Chrome 사용자 환경 보고서(CrUX)와 같은 도구입니다. (PageSpeed InsightsSearch Console과 같은 도구에서 보고하는 필드 데이터는 CrUX 데이터에서 가져옵니다.)

현장 도구는 실제 사용자의 환경을 실제로 나타내는 더 정확한 데이터를 제공하지만 실험실 도구는 문제를 식별하고 해결하는 데 더 효과적입니다.

CrUX 데이터는 페이지의 실제 실적을 더 잘 나타내지만 CrUX 점수를 알면 성능을 개선하는 방법을 파악하는 데 도움이 되지 않을 수 있습니다.

반면 Lighthouse는 문제를 식별하고 개선 방법을 구체적으로 제안합니다. 그러나 Lighthouse는 페이지 로드 시 발견한 성능 문제에 대해서만 제안합니다. 페이지에서 스크롤하거나 버튼을 클릭하는 등 사용자 상호작용의 결과로만 표시되는 문제는 감지하지 않습니다.

여기서 중요한 질문이 하나 떠오릅니다. 현장의 실제 사용자로부터 Core Web Vitals 또는 기타 성능 측정항목의 디버그 정보를 캡처하려면 어떻게 해야 하나요?

이 게시물에서는 현재 Core Web Vitals 측정항목별로 추가 디버깅 정보를 수집하는 데 사용할 수 있는 API를 자세히 설명하고 기존 분석 도구에서 이 데이터를 캡처하는 방법을 제안합니다.

기여 분석 및 디버깅을 위한 API

레이아웃 변경 횟수(CLS)

모든 Core Web Vitals 측정항목 중 CLS는 필드에서 디버그 정보를 수집하는 것이 가장 중요한 측정항목일 수 있습니다. CLS는 페이지의 전체 수명 주기 동안 측정되므로 사용자가 페이지와 상호작용하는 방식(스크롤 거리, 클릭한 항목 등)에 따라 레이아웃이 변경되는지 여부와 변경되는 요소가 달라질 수 있습니다.

PageSpeed Insights의 다음 보고서를 살펴보겠습니다.

CLS 값이 다른 PageSpeed Insights 보고서
PageSpeed Insights에는 가능한 경우 필드 데이터와 실험실 데이터가 모두 표시되며, 이 두 데이터는 서로 다를 수 있습니다.

실험실 (Lighthouse)의 CLS에 보고된 값과 현장 (CrUX 데이터)의 CLS에 보고된 값은 상당히 다릅니다. 이는 Lighthouse에서 테스트할 때 사용되지 않는 양방향 콘텐츠가 페이지에 많이 있을 수 있다는 점을 고려하면 당연한 결과입니다.

하지만 사용자 상호작용이 필드 데이터에 영향을 미친다는 점을 이해하더라도 75번째 백분위수에서 점수가 0.28이 되도록 페이지의 어떤 요소가 이동하는지 알아야 합니다. 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});

발생하는 모든 레이아웃 전환에 대해 데이터를 측정하고 애널리틱스 도구로 전송하는 것은 실용적이지 않을 수 있습니다. 하지만 모든 전환을 모니터링하면 가장 심각한 전환을 추적하고 이에 관한 정보를 보고할 수 있습니다.

목표는 모든 사용자에게 발생하는 모든 레이아웃 변경을 파악하고 수정하는 것이 아닙니다. 가장 많은 수의 사용자에게 영향을 미치고 75번째 백분위수에서 페이지의 CLS에 가장 큰 영향을 미치는 변경사항을 파악하는 것이 목표입니다.

또한 변경이 있을 때마다 가장 큰 소스 요소를 계산할 필요는 없으며 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인 Largest 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 하위 부분 시간을 측정하는 것이 유용할 수 있습니다. 이는 사이트와 관련된 구체적인 최적화 단계를 결정하는 데 유용할 수 있습니다.

다음 페인트에 대한 상호작용 (INP)

INP의 현장에서 캡처해야 하는 가장 중요한 정보는 다음과 같습니다.

  1. 상호작용한 요소
  2. 상호작용 유형의 이유
  3. 상호작용이 발생한 시점

상호작용이 느려지는 주요 원인은 차단된 기본 스레드이며 이는 JavaScript가 로드되는 동안 자주 발생할 수 있습니다. 페이지 로드 중에 대부분의 느린 상호작용이 발생하는지 여부를 알면 문제를 해결하기 위해 취해야 할 조치를 결정하는 데 도움이 됩니다.

INP 측정항목은 등록된 이벤트 리스너를 실행하는 데 걸리는 시간과 모든 이벤트 리스너가 실행된 후 다음 프레임을 페인트하는 데 걸리는 시간을 비롯한 상호작용의 전체 지연 시간을 고려합니다. 즉, INP의 경우 상호작용이 느려지는 경향이 있는 타겟 요소와 이러한 상호작용의 유형을 아는 것이 매우 유용합니다.

다음 코드는 INP 항목의 타겟 요소와 시간을 로깅합니다.

function logINPDebugInfo(inpEntry) {
  console
.log('INP target element:', inpEntry.target);
  console
.log('INP interaction type:', inpEntry.name);
  console
.log('INP time:', inpEntry.startTime);
}

이 코드는 어떤 event 항목이 INP 항목인지 확인하는 방법을 보여주지 않습니다. 이 로직은 더 복잡하기 때문입니다. 하지만 다음 섹션에서는 web-vitals JavaScript 라이브러리를 사용하여 이 정보를 가져오는 방법을 설명합니다.

web-vitals JavaScript 라이브러리 사용

이전 섹션에서는 분석 도구에 전송하는 데이터에 포함할 디버그 정보를 캡처하는 몇 가지 일반적인 제안과 코드 예시를 제공합니다.

버전 3부터 web-vitals JavaScript 라이브러리에는 이 모든 정보를 표시하는 기여 분석 빌드와 몇 가지 추가 신호가 포함되어 있습니다.

다음 코드 예는 성능 문제의 근본 원인을 파악하는 데 유용한 디버그 문자열이 포함된 추가 이벤트 매개변수 (또는 맞춤 측정기준)를 설정하는 방법을 보여줍니다.

import {onCLS, 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 'INP':
      eventParams
.debug_target = attribution.interactionTarget;
     
break;
 
}

 
// Assumes the global `gtag()` function exists, see:
 
// https://developers.google.com/analytics/devguides/collection/ga4
  gtag
('event', name, eventParams);
}

onCLS
(sendToGoogleAnalytics);
onLCP
(sendToGoogleAnalytics);
onINP
(sendToGoogleAnalytics);

이 코드는 Google 애널리틱스에서만 사용할 수 있지만 일반적인 개념은 다른 분석 도구에도 적용할 수 있습니다.

이 코드는 단일 디버그 신호를 보고하는 방법만 보여줍니다. 하지만 측정항목당 여러 신호를 수집하고 보고할 수 있으면 유용합니다.

예를 들어 INP를 디버그하려면 상호작용하는 요소, 상호작용 유형, 시간, loadState, 상호작용 단계 등을 수집해야 할 수 있습니다 (예: 긴 애니메이션 프레임 데이터).

web-vitals 기여 분석 빌드는 다음 INP 예와 같이 추가 기여 분석 정보를 노출합니다.

import {onCLS, 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.interactionTarget;
      eventParams
.debug_type = attribution.interactionType;
      eventParams
.debug_time = attribution.interactionTime;
      eventParams
.debug_load_state = attribution.loadState;
      eventParams
.debug_interaction_delay = Math.round(attribution.inputDelay);
      eventParams
.debug_processing_duration = Math.round(attribution.processingDuration);
      eventParams
.debug_presentation_delay =  Math.round(attribution.presentationDelay);
     
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);
onINP
(sendToGoogleAnalytics);

노출되는 디버그 신호의 전체 목록은 웹-비탈스 기여 분석 문서를 참고하세요.

데이터 보고 및 시각화

측정항목 값과 함께 디버그 정보를 수집하기 시작했다면 다음 단계는 모든 사용자의 데이터를 집계하여 패턴과 동향을 찾는 것입니다.

앞서 언급했듯이 사용자에게 발생하는 모든 문제를 해결할 필요는 없습니다. 특히 처음에는 가장 많은 사용자에게 영향을 미치는 문제를 해결해야 합니다. 이는 Core Web Vitals 점수에 가장 부정적인 영향을 미치는 문제이기도 합니다.

GA4의 경우 BigQuery를 사용하여 데이터를 쿼리하고 시각화하는 방법에 관한 전용 도움말을 참고하세요.

요약

이 게시물이 기존 성능 API와 web-vitals 라이브러리를 사용하여 디버그 정보를 가져와 실제 사용자 방문을 기반으로 성능을 진단하는 데 도움이 되었기를 바랍니다. 이 가이드에서는 Core Web Vitals에 중점을 두고 있지만, 이 개념은 JavaScript에서 측정할 수 있는 모든 성능 측정항목의 디버깅에도 적용됩니다.

성능 측정을 처음 시작하는 사용자이고 이미 Google 애널리틱스 사용자인 경우 웹 바이탈 보고서 도구가 시작하기에 좋은 도구일 수 있습니다. 이 도구는 이미 Core Web Vitals 측정항목의 디버그 정보 보고를 지원하기 때문입니다.

애널리틱스 공급업체로서 제품을 개선하고 사용자에게 더 많은 디버깅 정보를 제공하려는 경우 여기에 설명된 기법을 고려하되 여기에 제시된 아이디어에 만이 국한되지 마세요. 이 게시물은 모든 분석 도구에 일반적으로 적용될 수 있도록 작성되었지만 개별 분석 도구는 더 많은 디버그 정보를 캡처하고 보고할 수 있으며 그렇게 해야 합니다.

마지막으로 API 자체에 기능이나 정보가 누락되어 이러한 측정항목을 디버그하는 데 문제가 있다고 생각되면 web-vitals-feedback@googlegroups.com으로 의견을 보내주세요.