필드에서 느린 상호작용 찾기

'다음 페인트에 대한 상호작용'을 개선할 기회를 찾을 수 있도록 웹사이트의 필드 데이터에서 느린 상호작용을 찾는 방법을 알아봅니다.

필드 데이터는 실제 사용자가 웹사이트를 어떻게 경험하는지를 보여주는 데이터입니다. 실험실 데이터에서만 찾을 수 없는 문제를 알려줍니다. 다음 페인트에 대한 상호작용 (INP)의 경우 필드 데이터는 느린 상호작용을 식별하는 데 필수적이며 이를 해결하는 데 도움이 되는 중요한 단서를 제공합니다.

이 가이드에서는 Chrome 사용자 환경 보고서 (CrUX)의 필드 데이터를 사용하여 웹사이트의 INP를 빠르게 평가하여 웹사이트에 INP 문제가 있는지 확인하는 방법을 알아봅니다. 이후에는 web-vitals JavaScript 라이브러리의 기여 분석 빌드와 Long Animation Frames API (LoAF)에서 제공하는 새로운 통계를 사용하여 웹사이트에서 느린 상호작용에 대한 필드 데이터를 수집하고 해석하는 방법을 알아봅니다.

CrUX로 시작하여 웹사이트의 INP 평가

웹사이트 사용자로부터 필드 데이터를 수집하지 않는 경우 CrUX부터 시작하는 것이 좋습니다. CrUX는 텔레메트리 데이터 전송을 선택한 실제 Chrome 사용자로부터 필드 데이터를 수집합니다.

CrUX 데이터는 다양한 영역에 표시되며 찾고 있는 정보의 범위에 따라 다릅니다. CrUX는 다음에 관한 INP 및 기타 코어 웹 바이탈에 관한 데이터를 제공할 수 있습니다.

  • PageSpeed Insights를 사용하여 개별 페이지 및 전체 출처
  • 페이지 유형. 예를 들어 많은 전자상거래 웹사이트에는 제품 세부정보 페이지 유형과 제품 목록 페이지 유형이 있습니다. Search Console에서 고유 페이지 유형의 CrUX 데이터를 확인할 수 있습니다.

먼저 PageSpeed Insights에 웹사이트 URL을 입력해 보세요. URL을 입력하면 INP를 비롯한 여러 측정항목에 대한 필드 데이터(사용 가능한 경우)가 표시됩니다. 전환 버튼을 사용하여 모바일 및 데스크톱 측정기준의 INP 값을 확인할 수도 있습니다.

PageSpeed Insights의 CrUX에 표시된 필드 데이터로, 3가지 Core Web Vitals의 LCP, INP, CLS와 진단 측정항목으로 TTFB, FCP, 지원 중단된 Core Web Vitals 측정항목으로 FID가 표시됩니다.
PageSpeed 통계에 표시된 CrUX 데이터의 판독입니다. 이 예에서는 웹페이지의 INP를 개선해야 합니다.

이 데이터는 문제가 있는지 알려주므로 유용합니다. 하지만 CrUX가 할 수 없는 일은 문제를 일으키는 원인을 알려주는 것입니다. 웹사이트 사용자로부터 자체 필드 데이터를 수집하는 데 도움이 되는 여러 가지 실제 사용자 모니터링 (RUM) 솔루션이 있습니다. 한 가지 방법은 web-vitals JavaScript 라이브러리를 사용하여 필드 데이터를 직접 수집하는 것입니다.

web-vitals JavaScript 라이브러리로 필드 데이터 수집

web-vitals 자바스크립트 라이브러리는 웹사이트 사용자의 필드 데이터를 수집하기 위해 웹사이트에서 로드할 수 있는 스크립트입니다. 이를 통해 지원되는 브라우저의 INP를 비롯한 여러 측정항목을 기록할 수 있습니다.

브라우저 지원

  • 96
  • 96
  • x
  • x

소스

web-vitals 라이브러리의 표준 빌드를 사용하여 현장 사용자로부터 기본 INP 데이터를 얻을 수 있습니다.

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

사용자의 필드 데이터를 분석하려면 이 데이터를 다음 위치로 전송하는 것이 좋습니다.

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

하지만 이 데이터만으로는 CrUX보다 더 많은 정보를 알 수 없습니다. 여기에서 web-vitals 라이브러리의 기여 분석 빌드가 사용됩니다.

web-vitals 라이브러리의 기여 분석 빌드로 더 나아가기

web-vitals 라이브러리의 기여 분석 빌드는 현장 사용자로부터 얻을 수 있는 추가 데이터를 표시하므로 웹사이트의 INP에 영향을 미치는 문제가 있는 상호작용 문제를 더 효과적으로 해결하는 데 도움이 됩니다. 이 데이터는 라이브러리의 onINP() 메서드에 표시된 attribution 객체를 통해 액세스할 수 있습니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 512
  console.log(rating);       // 'poor'
  console.dir(attribution);  // Attribution data
});
web-vitals 라이브러리의 콘솔 로그가 표시되는 방식 이 예의 콘솔에는 측정항목 이름(INP), INP 값(56)이 표시되며 이 값은 INP 임곗값(좋음) 내에 위치하며 저작자 표시 객체에 표시된 다양한 정보 비트(Long Animation Frame API의 항목 포함)를 포함합니다.
web-vitals 라이브러리의 데이터가 콘솔에 표시되는 방식

기여 분석 빌드는 페이지의 INP 자체 외에도 상호작용에서 집중해야 하는 부분을 포함하여 상호작용이 느린 이유를 파악하는 데 사용할 수 있는 많은 데이터를 제공합니다. 이를 통해 다음과 같은 중요한 질문에 대한 답을 찾을 수 있습니다.

  • "페이지가 로드되는 동안 사용자가 페이지와 상호작용했나요?"
  • "상호작용의 이벤트 핸들러가 장시간 실행되었나요?"
  • "상호작용 이벤트 핸들러 코드가 시작부터 지연되었나요? 그렇다면 그 당시 기본 스레드에서는 어떤 일이 있었나요?'
  • "상호작용으로 인해 많은 렌더링 작업이 발생하여 다음 프레임이 페인트되지 않을 수 있었나요?"

다음 표는 웹사이트에서 얻을 수 있는 기본 기여 분석 데이터의 일부를 보여주며, 웹사이트에서 발생하는 상호작용이 느린 원인을 파악하는 데 도움이 됩니다.

attribution 객체 키 데이터
interactionTarget 페이지의 INP 값을 생성한 요소를 가리키는 CSS 선택자(예: button#save)
interactionType 클릭, 탭 또는 키보드 입력으로 인한 상호작용 유형입니다.
inputDelay* 상호작용의 입력 지연입니다.
processingDuration* 사용자 상호작용에 대한 응답으로 첫 번째 이벤트 리스너가 실행을 시작한 시점부터 모든 이벤트 리스너 처리가 완료된 시점까지의 시간입니다.
presentationDelay* 이벤트 핸들러가 완료된 시점부터 다음 프레임이 그려질 때까지 발생하는 상호작용의 프레젠테이션 지연입니다.
longAnimationFrameEntries* 상호작용과 연결된 LoAF의 항목입니다. 추가 정보는 다음을 참고하세요.
*버전 4의 새로운 기능

web-vitals 라이브러리 버전 4부터 INP 단계 분류 (입력 지연, 처리 시간, 프레젠테이션 지연) 및 Long Animation Frame API (LoAF)를 통해 제공되는 데이터를 통해 문제가 있는 상호작용에 관한 더욱 심층적이고 유용한 정보를 얻을 수 있습니다.

Long Animation Frame API (LoAF)

브라우저 지원

  • 123
  • 123
  • x
  • x

소스

필드 데이터를 사용하여 상호작용을 디버깅하는 것은 까다로운 작업입니다. 하지만 이제 LoAF의 데이터를 통해 느린 상호작용의 원인에 대해 더 잘 파악할 수 있게 되었습니다. LoAF를 통해 정확한 원인을 정확히 찾아내는 데 사용할 수 있는 상세한 타이밍과 기타 데이터를 확인할 수 있기 때문입니다. 더 중요한 것은 문제의 원인이 웹사이트 코드에 있는 것입니다.

web-vitals 라이브러리의 기여 분석 빌드는 attribution 객체의 longAnimationFrameEntries 키 아래에 LoAF 항목의 배열을 노출합니다. 다음 표에는 각 LoAF 항목에서 찾을 수 있는 몇 가지 주요 데이터가 나열되어 있습니다.

LoAF 항목 객체 키 데이터
duration 레이아웃이 완료된 후 페인팅과 합성을 제외한 긴 애니메이션 프레임의 재생 시간입니다.
blockingDuration 장기 작업으로 인해 브라우저가 빠르게 응답할 수 없었던 총 시간입니다. 이 차단 시간에는 JavaScript를 실행하는 장기 작업과 프레임의 후속 긴 렌더링 작업이 포함될 수 있습니다.
firstUIEventTimestamp 프레임 중에 이벤트가 대기열에 추가된 시점의 타임스탬프입니다. 상호작용의 입력 지연이 시작되는 시점을 파악하는 데 유용합니다.
startTime 프레임의 시작 타임스탬프입니다.
renderStart 프레임의 렌더링 작업이 시작된 시간입니다. 여기에는 모든 requestAnimationFrame 콜백 (및 해당하는 경우 ResizeObserver 콜백)이 포함되지만 스타일/레이아웃 작업이 시작되기 전에 표시될 수 있습니다.
styleAndLayoutStart 프레임에서 스타일/레이아웃 작업이 발생할 때 사용 가능한 다른 타임스탬프를 찾을 때 스타일/레이아웃 작업의 길이를 파악하는 데 유용할 수 있습니다.
scripts 페이지의 INP에 기여하는 스크립트 속성 정보가 포함된 항목의 배열입니다.
LoAF 모델에 따른 긴 애니메이션 프레임의 시각화입니다.
LoAF API에 따른 긴 애니메이션 프레임의 타이밍 다이어그램 (blockingDuration 빼기).

이 모든 정보를 통해 상호작용이 느려지는 이유를 자세히 알 수 있지만, LoAF 항목이 표시되는 scripts 배열은 특히 중요한 사항입니다.

스크립트 기여 분석 객체 키 데이터
invoker 호출자. 다음 행에 설명된 호출자 유형에 따라 달라질 수 있습니다. 호출자의 예로는 'IMG#id.onload', 'Window.requestAnimationFrame', 'Response.json.then'와 같은 값이 있습니다.
invokerType 호출자 유형입니다. 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script', 또는 'module-script'일 수 있습니다.
sourceURL 긴 애니메이션 프레임이 시작된 스크립트의 URL입니다.
sourceCharPosition 스크립트에서 sourceURL로 식별된 문자 위치입니다.
sourceFunctionName 식별된 스크립트의 함수 이름입니다.

이 배열의 각 항목에는 이 표에 표시된 데이터가 포함되어 있습니다. 이 데이터를 통해 느린 상호작용을 유발하는 스크립트에 대한 정보와 이 원인이 된 작업을 확인할 수 있습니다.

느린 상호작용의 일반적인 원인 측정 및 파악

이 정보를 사용하는 방법을 설명하기 위해 이제 이 가이드에서는 web-vitals 라이브러리에 표시된 LoAF 데이터를 사용하여 느린 상호작용의 몇 가지 원인을 파악하는 방법을 살펴봅니다.

긴 처리 시간

상호작용 처리 기간은 상호작용의 등록된 이벤트 핸들러 콜백이 완료까지 실행되는 데 걸리는 시간과 그 사이에 발생할 수 있는 기타 모든 작업입니다. 높은 처리 시간은 web-vitals 라이브러리에 표시됩니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

느린 상호작용의 주된 원인은 이벤트 핸들러 코드를 실행하는 데 너무 오래 걸리는 것이 당연하다고 생각하는 것이 당연하지만 항상 그런 것은 아닙니다. 이것이 문제인 것으로 확인되면 다음과 같이 LoAF 데이터를 자세히 살펴볼 수 있습니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

위의 코드 스니펫에서 볼 수 있듯이 LoAF 데이터로 작업하면 다음과 같이 처리 시간이 긴 상호작용의 정확한 원인을 추적할 수 있습니다.

  • 요소 및 등록된 이벤트 리스너입니다.
  • 장기 실행 이벤트 핸들러 코드가 포함된 스크립트 파일과 그 내의 문자 위치
  • 함수 이름입니다.

이러한 유형의 데이터는 매우 중요합니다. 더 이상 높은 처리 시간 값을 담당하는 상호작용(또는 이벤트 핸들러)을 정확하게 알아내기 위해 일일이 작업을 수행하지 않아도 됩니다. 또한 서드 파티 스크립트는 자체 이벤트 핸들러를 등록할 수 있는 경우가 많으므로 개발자 자신의 코드가 원인인지 확인할 수 있습니다. 제어할 수 있는 코드의 경우 장기 작업 최적화를 살펴보는 것이 좋습니다.

긴 입력 지연

장기 실행 이벤트 핸들러가 일반적이지만 상호작용에서 고려해야 할 다른 부분이 있습니다. 한 부분은 입력 지연이라고 하는 처리 기간 전에 발생합니다. 이 시간은 사용자가 상호작용을 시작한 시점부터 이벤트 핸들러 콜백이 실행되기 시작하고 기본 스레드에서 이미 다른 작업을 처리하고 있을 때 발생하는 시점까지의 시간입니다. web-vitals 라이브러리의 기여 분석 빌드에서 상호작용 입력 지연의 길이를 알 수 있습니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

일부 상호작용의 입력 지연이 길면 입력 지연이 긴 상호작용 시점에 페이지에서 어떤 일이 일어났는지 파악해야 합니다. 이는 대개 상호작용이 페이지 로드 도중에 발생했는지 아니면 로드 후에 발생했는지로 귀결되는 경우가 많습니다.

페이지 로드 중에 문제가 발생했나요?

페이지가 로드될 때 기본 스레드가 가장 바쁜 경우가 많습니다. 이 시간 동안 모든 종류의 작업이 큐에 추가되고 처리되며, 이 모든 작업이 실행되는 동안 사용자가 페이지와 상호작용하려고 하면 상호작용이 지연될 수 있습니다. 많은 자바스크립트를 로드하는 페이지에서 스크립트를 컴파일 및 평가하는 것은 물론, 페이지에서 사용자 상호작용에 필요한 기능을 실행하는 작업도 시작할 수 있습니다. 이 작업은 이러한 활동이 발생할 때 사용자가 우연히 상호작용하는 경우 방해가 될 수 있으며 웹사이트 사용자에게도 이러한 상황이 발생하는지 확인할 수 있습니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

필드에 이 데이터를 기록하고 입력 지연이 길고 'classic-script' 또는 'module-script' 호출자 유형이 있는 경우 사이트의 스크립트가 평가하는 데 시간이 오래 걸리고 상호작용을 지연시킬 만큼 충분히 오랫동안 기본 스레드를 차단하고 있다고 할 수 있습니다. 스크립트를 더 작은 번들로 분해하여 이러한 차단 시간을 줄이고, 초기에 사용하지 않는 코드가 나중에 로드되도록 연기하고, 사이트에서 아예 삭제할 수 있는 미사용 코드가 있는지 감사할 수 있습니다.

페이지 로드 후 발생했나요?

입력 지연은 페이지가 로드되는 동안 종종 발생하지만, 페이지가 로드된 에도 완전히 다른 원인으로 인해 발생할 수 있습니다. 페이지 로드 후 입력 지연이 발생하는 일반적인 원인은 이전의 setInterval 호출로 인해 주기적으로 실행되는 코드 또는 더 일찍 실행되도록 대기열에 추가되어 아직 처리 중인 이벤트 콜백일 수 있습니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

높은 처리 시간 값의 문제 해결의 경우와 마찬가지로 앞서 언급한 원인으로 인해 입력 지연이 길어지면 자세한 스크립트 기여 분석 데이터가 제공됩니다. 하지만 상호작용을 지연시킨 작업의 특성에 따라 호출자 유형이 변경된다는 점이 다릅니다.

  • 'user-callback'는 차단 작업이 setInterval, setTimeout 또는 requestAnimationFrame에서 발생했음을 나타냅니다.
  • 'event-listener'는 차단 작업이 큐에 추가되었다가 아직 처리 중인 이전 입력에서 발생했음을 나타냅니다.
  • 'resolve-promise''reject-promise'는 차단 작업이 이전에 시작되었고 사용자가 페이지와 상호작용하려고 할 때 해결되거나 거부되어 상호작용이 지연되는 일부 비동기 작업에서 발생했음을 의미합니다.

어떤 경우든 스크립트 속성 데이터를 보면 어디서부터 시작해야 할지, 입력 지연이 개발자의 코드 때문인지 아니면 타사 스크립트의 지연으로 인한 것인지를 알 수 있습니다.

프레젠테이션이 오래 지연됨

프레젠테이션 지연은 상호작용의 라스트 마일이며 상호작용의 이벤트 핸들러가 완료될 때 시작되어 다음 프레임이 그려질 때까지입니다. 상호작용으로 인해 이벤트 핸들러의 작업이 사용자 인터페이스의 시각적 상태를 변경할 때 발생합니다. 처리 시간 및 입력 지연과 마찬가지로 web-vitals 라이브러리는 상호작용의 프레젠테이션 지연 시간을 알려줍니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

이 데이터를 기록하고 웹사이트의 INP에 영향을 미치는 상호작용에 대한 프레젠테이션 지연이 많이 발생하는 경우, 원인은 다양할 수 있지만 주의해야 할 몇 가지 원인은 다음과 같습니다.

고비용 스타일 및 레이아웃 작업

긴 프레젠테이션 지연은 복잡한 CSS 선택기 및 큰 DOM 크기를 비롯한 다양한 원인으로 인해 발생하는 값비싼 스타일 재계산레이아웃 작업일 수 있습니다. web-vitals 라이브러리에 표시된 LoAF 타이밍으로 이 작업이 작동하는 시간을 측정할 수 있습니다.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF는 프레임별 스타일 및 레이아웃 작업의 기간은 알려주지 않지만 작업이 시작된 시점을 알려줍니다. 이 시작 타임스탬프를 사용하면 프레임의 종료 시간을 결정하고 이 시간에서 스타일 및 레이아웃 작업의 시작 타임스탬프를 빼서 LoAF의 다른 데이터를 사용하여 작업의 정확한 기간을 계산할 수 있습니다.

장기 실행 requestAnimationFrame 콜백

프레젠테이션이 오래 지연되는 이유 중 하나는 requestAnimationFrame 콜백에서 과도한 작업이 실행되었기 때문일 수 있습니다. 이 콜백의 내용은 이벤트 핸들러가 실행을 완료한 후 스타일 재계산 및 레이아웃 작업 직전에 실행됩니다.

콜백 내에서 수행되는 작업이 복잡한 경우, 콜백을 완료하는 데 상당한 시간이 걸릴 수 있습니다. requestAnimationFrame를 사용한 작업으로 인해 프레젠테이션 지연 값이 높게 나타난다고 생각되는 경우 web-vitals 라이브러리에 표시된 LoAF 데이터를 사용하여 다음과 같은 시나리오를 확인할 수 있습니다.

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

프레젠테이션 지연 시간의 상당 부분이 requestAnimationFrame 콜백에 사용된 것으로 확인되면 이러한 콜백에서 실행하는 작업이 사용자 인터페이스의 실제 업데이트로 이어지는 작업 실행에만 한정되도록 합니다. DOM 또는 스타일 업데이트와 관련이 없는 다른 모든 작업은 다음 프레임이 페인트되는 것을 불필요하게 지연시키므로 주의하세요.

결론

입력란 데이터는 현장의 실제 사용자에게 문제가 되는 상호작용을 파악하는 데 도움이 되는 가장 좋은 정보 출처입니다. web-vitals JavaScript 라이브러리 (또는 RUM 제공업체)와 같은 필드 데이터 수집 도구를 사용하면 어떤 상호작용이 가장 문제가 되는지 보다 확실하게 파악하고 실험실에서 문제가 있는 상호작용을 재현한 후 이를 수정할 수 있습니다.

Federico RespiniUnsplash의 히어로 이미지입니다.