낮은 INP의 원인 파악

상호작용에 대한 반응성이 좋지 않으면 페이지가 느리거나 완전히 손상되었다는 인상을 줄 수 있습니다. 다음 페인트에 대한 상호작용 (INP)은 Event Timing API를 기반으로 하는 측정항목입니다. 페이지의 응답성을 측정하기 위해 최악의 상호작용 (일부 이상치 제외)을 측정합니다.

응답하지 않는 웹페이지의 근본 원인을 파악하는 것은 특히 사용자 상호작용과 기기 기능 및 네트워크 상태와 같은 환경 조건에 따라 달라지므로 까다로울 수 있습니다. 이로 인해 페이지 로드와 같이 반복 가능하고 예측 가능한 시나리오에 비해 진단이 더 어려워집니다. 실험실 데이터는 실제 사용자 시나리오의 작은 하위 집합만 복제하므로 현장에서 느린 INP의 원인을 측정하는 것이 필수적입니다.

Event Timing API를 사용하면 INP 기간을 입력 지연 (상호작용이 발생할 때 이미 발생하는 처리), 처리 기간 (상호작용의 직접적인 결과로 인한 지연), 표시 지연 (상호작용 후 다음 프레임을 렌더링하는 데 따른 지연)의 세 가지 하위 부분으로 분할할 수 있습니다. 이렇게 하면 상호작용 코드로 바로 이동하는 대신 문제가 다른 코드, 상호작용 JavaScript 코드 또는 브라우저 처리 렌더링 업데이트에 있는지 각각 식별할 수 있습니다.

상호작용을 지연시키는 JavaScript, 스타일 재계산, 레이아웃 작업에 관한 추가 정보를 수집할 수 있습니다. JS Self-Profiling API를 사용하는 전체 성능 trace는 성능 문제를 일으킬 수 있는 무거운 솔루션입니다. Long Animation Frames API는 느리게 실행되는 JavaScript를 식별하고 INP 상호작용 중에 스타일 재계산과 레이아웃 비용을 구분하기 위해 현장에서 사용할 수 있는 가벼운 API입니다.

  • 스크립트 수준 강제 스타일 및 레이아웃 (forcedStyleDuration 및 forcedLayoutDuration): 입력 지연 또는 처리 시간 중에 실행 중인 스크립트 내에서 동기식 스타일 재계산 (예: DOM 변형 후 getComputedStyle() 호출) 및 강제 레이아웃/리플로우 (예: 변형 후 getBoundingClientRect() 또는 offsetWidth 읽기)를 보고합니다.
  • 프레임 수준 렌더링 스타일 및 레이아웃 (styleDuration 및 layoutDuration): 프레임의 렌더링 업데이트 중에 스타일을 다시 계산하는 데 소요된 시간과 레이아웃을 업데이트하는 데 소요된 시간을 보고하여 높은 프레젠테이션 지연을 설명하는 데 도움이 됩니다.

스타일 재계산과 레이아웃을 구분하는 것은 매우 중요합니다. 이 두 가지는 근본 원인과 해결 방법이 다르기 때문입니다. 스타일 지속 시간이 길면 비용이 많이 드는 CSS 선택자 일치 또는 광범위한 스타일 무효화가 원인일 수 있습니다 (선택자를 단순화하거나 규칙 범위를 줄여 해결). 반면 레이아웃 지속 시간이 길면 비용이 많이 드는 기하학 계산이나 동기식 레이아웃 스래싱이 원인일 수 있습니다 (쓰기 전에 DOM 읽기를 일괄 처리하거나, DOM 구조를 단순화하거나, CSS 포함을 사용하여 해결).

구현 방법

원시 Event Timing API에서 INP를 계산하는 것은 복잡하며 몇 가지 미묘한 차이가 있습니다. RUM 도구 또는 라이브러리를 사용하여 이 데이터를 수집하는 것이 좋습니다.

web-vitals는 INP를 포함한 Core Web Vitals를 계산하는 Google의 오픈소스 라이브러리이며, INP 상호작용을 위한 하위 부분과 긴 애니메이션 프레임 데이터도 포함합니다.

web-vitals 라이브러리를 사용하여 INP 상호작용의 기여 분석 가져오기

web-vitals 라이브러리는 Core Web Vitals 및 기타 실적 측정항목을 측정하는 데 사용되는 작은 라이브러리입니다. onINP() 함수를 사용하면 가장 느린 상호작용을 식별할 수 있으며, 여기에는 INP 하위 부분, 상호작용 중에 실행된 스크립트, Long Animation Frames API를 사용한 스타일 및 레이아웃 지속 시간에 관한 정보가 포함됩니다.

// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const { attribution } = metric;
  const longestScript = attribution.longestScript;

  // Sum rendering-phase style and layout durations across all LoAF entries
  // intersecting the INP interaction (primarily explains presentationDelay).
  let renderStyleDuration = 0;
  let renderLayoutDuration = 0;
  for (const entry of attribution.longAnimationFrameEntries) {
    renderStyleDuration += entry.styleDuration ?? 0;
    renderLayoutDuration += entry.layoutDuration ?? 0;
  }

  // Beacon script, style, and layout attribution for the INP interaction,
  // so you can identify the root cause in production.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: 'INP',
      value: metric.value,
      // These fields give the INP subparts:
      inputDelay: attribution.inputDelay,
      presentationDelay: attribution.presentationDelay,
      processingDuration: attribution.processingDuration,
      interactionTarget: attribution.interactionTarget,
      // These fields identify which script function was responsible
      // for the longest processing during the INP interaction.
      invokerType: longestScript?.entry?.invokerType,
      sourceURL: longestScript?.entry?.sourceURL,
      sourceFunctionName: longestScript?.entry?.sourceFunctionName,
      sourceCharPosition: longestScript?.entry?.sourceCharPosition,
      // subpart indicates which phase (input delay, processing, or
      // presentation delay) the longest script overlapped with most.
      subpart: longestScript?.subpart,
      intersectingDuration: longestScript?.intersectingDuration,
      // Synchronous style and layout work forced inside the longest script
      // (with forcedStyleAndLayoutDuration as a combined fallback):
      forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
      forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
      forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
      // Rendering-phase style and layout work across the interaction's frames
      // (with totalStyleAndLayoutDuration as a combined fallback):
      renderStyleDuration,
      renderLayoutDuration,
      totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
    })
  );
});

권장사항

  • 권장사항: 처음에는 INP 하위 부분을 사용하여 지연이 이미 실행 중인 JavaScript (입력 지연), 상호작용을 위한 이벤트 핸들러 JavaScript (처리 시간) 또는 후속 렌더링 (표시 지연)인지 확인합니다.
  • 권장사항: 특히 입력 지연 및 처리 시간과 관련하여 가장 큰 차단 JavaScript를 추가 세부정보로 식별하려고 시도합니다.
  • 권장사항: 처리 시간이 길거나 표시 지연이 발생하는 경우 스타일 재계산 (styleDuration, forcedStyleDuration)과 레이아웃 (layoutDuration, forcedLayoutDuration)을 구분하세요. 스타일 시간이 길면 비용이 많이 드는 CSS 선택자 또는 광범위한 무효화를 나타내고, 레이아웃 시간이 길면 DOM 도형 병목 현상 또는 동기식 레이아웃 스래싱을 나타냅니다.
  • 권장사항: 런타임 오버헤드가 더 높은 JS Self-Profiling API와 같은 대안보다 Long Animation Frames API를 사용하여 이 세부정보를 제공하는 것이 좋습니다.
  • 다른 RUM 솔루션이 없는 경우 web-vitals 라이브러리를 사용합니다. INP 상호작용을 식별할 수 있으며 상호작용 중에 실행된 하위 부분 및 스크립트에 관한 정보가 포함됩니다 (긴 애니메이션 프레임 API 사용).
  • 필요한 정보를 로컬에 로깅하는 대신 분석 서비스로 비콘백해야 합니다.

브라우저 지원 및 대체 전략

Event Timing API는 대부분의 최신 브라우저에서 사용할 수 있으며 INP 및 INP 하위 부분을 계산하는 데 필요합니다. 이 API를 지원하지 않는 브라우저에서는 INP를 측정할 수 없습니다.

긴 애니메이션 프레임 API는 선택적인 추가 세부정보를 제공합니다. 대체 없이 안전하게 사용할 수 있습니다. 대부분의 경우 Lighthouse에서 식별하는 성능 개선 기회는 다른 브라우저에도 적용됩니다.

기준 타겟이 긴 애니메이션 프레임 API (long-animation-frames)를 지원하지만 별도의 스타일 및 레이아웃 시간 속성 (PerformanceLongAnimationFrameTiming의 styleDuration 및 layoutDuration 또는 PerformanceScriptTiming의 forcedStyleDuration 및 forcedLayoutDuration)을 지원하지 않는 경우 결합된 스타일 및 레이아웃 타이밍으로 대체합니다.

  • 스크립트 내에서 동기식 강제 스타일 및 레이아웃 작업을 하려면 PerformanceScriptTiming에서 script.forcedStyleAndLayoutDuration를 사용하세요.
  • 스타일과 레이아웃을 결합하여 작업하려면 web-vitals/attribution에서 metric.attribution.totalStyleAndLayoutDuration을 사용하거나 각 PerformanceLongAnimationFrameTiming 항목에서 (entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart를 계산합니다.