긴 애니메이션 프레임 API

Long Animation Frames API (LoAF, 발음: Lo-Af)는 느린 사용자 인터페이스 (UI) 업데이트를 더 잘 이해할 수 있도록 Long Tasks API를 업데이트한 것입니다. 이는 응답성을 측정하는 다음 페인트에 대한 상호작용 (INP) 코어 웹 바이탈 측정항목에 영향을 줄 수 있는 느린 애니메이션 프레임을 식별하거나 부드러움에 영향을 미치는 다른 UI 버벅거림을 식별하는 데 유용할 수 있습니다.

API 상태

브라우저 지원

  • Chrome: 123
  • Edge: 123.
  • Firefox: 지원되지 않음
  • Safari: 지원되지 않음

소스

Chrome 116~Chrome 122의 오리진 트라이얼에 이어 LoAF API는 Chrome 123부터 출시되었습니다.

백그라운드: Long Tasks API

브라우저 지원

  • Chrome: 58
  • Edge: 79
  • Firefox: 지원되지 않음
  • Safari: 지원되지 않음

소스

Long Animation Frames API는 Chrome 58부터 Chrome에서 사용할 수 있었던 Long Tasks API의 대안입니다. 이름에서 알 수 있듯이 Long Task API를 사용하면 기본 스레드를 50밀리초 이상 차지하는 작업인 긴 작업을 모니터링할 수 있습니다. 긴 작업은 PeformanceObserver를 사용하여 PerformanceLongTaskTiming 인터페이스를 통해 모니터링할 수 있습니다.

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

긴 태스크는 응답성 문제를 일으킬 수 있습니다. 사용자가 페이지와 상호작용하려고 해도(예: 버튼을 클릭하거나 메뉴를 여는 경우) 기본 스레드에서 이미 긴 작업을 처리하고 있다면 해당 작업이 완료될 때까지 사용자의 상호작용이 지연됩니다.

응답성을 개선하려면 긴 작업을 분할하는 것이 좋습니다. 대신 각 긴 작업을 여러 개의 작은 작업으로 나누면 상호작용에 대한 응답이 크게 지연되지 않도록 그 사이에서 더 중요한 작업을 실행할 수 있습니다.

따라서 응답성을 개선하려고 할 때는 먼저 성능 트레이스를 실행하고 긴 작업을 살펴보는 것이 좋습니다. Lighthouse (긴 기본 스레드 작업 방지 감사 포함)와 같은 실험실 기반 감사 도구를 사용하거나 Chrome DevTools에서 긴 작업을 살펴보는 방법이 있습니다.

실험실 기반 테스트는 상호작용이 포함되지 않을 수 있으므로 응답성 문제를 식별하기에는 적절하지 않은 시작점일 수 있습니다. 상호작용이 포함되는 경우에도 발생할 수 있는 상호작용의 일부만 포함됩니다. 현장에서 느린 상호작용의 원인을 측정하는 것이 좋습니다.

Long Tasks API의 단점

Performance Observer를 사용하여 필드에서 긴 작업을 측정하는 것은 다소 유용합니다. 실제로는 긴 작업이 발생했다는 사실과 걸린 시간을 제외하고는 그다지 많은 정보를 제공하지 않습니다.

실시간 사용자 모니터링 (RUM) 도구는 종종 이를 사용하여 긴 작업의 수 또는 기간을 추세 분석하거나 긴 작업이 발생한 페이지를 식별합니다. 하지만 긴 작업의 원인에 관한 기본 세부정보가 없으면 이 정보를 제한적으로만 사용할 수 있습니다. Long Tasks API에는 기본 기여 분석 모델만 있습니다. 이 모델은 긴 작업이 발생한 컨테이너(최상위 문서 또는 <iframe>)만 알려줄 뿐, 이를 호출한 스크립트나 함수는 알려주지 않습니다(일반적인 항목 참고).

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

Long Tasks API는 일부 중요한 작업을 제외할 수도 있으므로 불완전한 뷰이기도 합니다. 렌더링과 같은 일부 업데이트는 별도의 작업에서 발생하며, 이러한 업데이트는 해당 상호작용의 '총 작업량'을 정확하게 측정하기 위해 업데이트를 일으킨 이전 실행과 함께 포함하는 것이 이상적입니다. 태스크를 사용하는 데 따르는 제한사항에 관한 자세한 내용은 설명서의 '긴 태스크의 단점' 섹션을 참고하세요.

마지막 문제는 긴 작업을 측정할 때 50밀리초 제한보다 오래 걸리는 개별 작업에 대해서만 보고된다는 점입니다. 애니메이션 프레임은 이 50밀리초 제한보다 작은 여러 작업으로 구성될 수 있지만 여전히 브라우저의 렌더링 기능을 차단할 수 있습니다.

Long Animation Frames API

브라우저 지원

  • Chrome: 123
  • Edge: 123.
  • Firefox: 지원되지 않음
  • Safari: 지원되지 않음

소스

Long Animation Frames API (LoAF)는 Long Tasks API의 일부 단점을 해결하여 개발자가 반응성 문제를 해결하고 INP를 개선하는 데 도움이 되는 더 많은 실행 가능한 통계를 얻고, 부드러움 문제에 대한 통계를 얻을 수 있도록 지원하는 새로운 API입니다.

응답성이 좋다는 것은 페이지가 상호작용에 빠르게 반응한다는 것을 의미합니다. 이를 위해서는 사용자에게 필요한 업데이트를 적시에 페인트하고 이러한 업데이트가 차단되지 않도록 해야 합니다. INP의 경우 200밀리초 이내에 응답하는 것이 좋습니다. 하지만 다른 업데이트 (예: 애니메이션)의 경우 200밀리초도 너무 길 수 있습니다.

Long Animation Frames API는 차단 작업을 측정하는 대체 방법입니다. Long Animation Frames API는 개별 작업을 측정하는 대신 이름에서 알 수 있듯이 긴 애니메이션 프레임을 측정합니다. 긴 애니메이션 프레임은 렌더링 업데이트가 50밀리초 (Long Tasks API의 임곗값과 동일) 이상 지연되는 경우를 말합니다.

긴 애니메이션 프레임은 렌더링이 필요한 작업의 시작부터 측정됩니다. 긴 애니메이션 프레임의 첫 번째 작업에 렌더링이 필요하지 않은 경우 렌더링이 필요 없는 작업이 완료되면 긴 애니메이션 프레임이 종료되고 다음 작업으로 새로운 긴 애니메이션 프레임이 시작됩니다. 이러한 렌더링되지 않는 긴 애니메이션 프레임은 잠재적으로 차단될 수 있는 작업을 측정할 수 있도록 50밀리초 (renderStart 시간 0)를 초과하는 경우에도 Long Animation Frames API에 포함됩니다.

긴 애니메이션 프레임은 PerformanceObserver가 있는 긴 작업과 비슷한 방식으로 관찰할 수 있지만 대신 long-animation-frame 유형을 살펴봅니다.

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

이전의 긴 애니메이션 프레임은 다음과 같이 성능 타임라인에서 쿼리할 수도 있습니다.

const loafs = performance.getEntriesByType('long-animation-frame');

그러나 성능 항목의 maxBufferSize가 있으므로 이후에는 더 새로운 항목이 삭제되므로 PerformanceObserver 접근 방식이 권장됩니다. long-animation-frame 버퍼 크기는 long-tasks와 마찬가지로 200으로 설정됩니다.

작업 대신 프레임을 보는 이점

태스크 관점이 아닌 프레임 관점에서 이를 살펴보는 주요 이점은 긴 애니메이션이 누적적으로 긴 애니메이션 프레임을 초래한 임의 수의 태스크로 구성될 수 있다는 것입니다. 이렇게 하면 이전에 언급한 마지막 사항, 즉 애니메이션 프레임 앞에 있는 많은 소규모 렌더링 차단 태스크의 합계가 Long Tasks API에 표시되지 않을 수 있는 문제가 해결됩니다.

긴 작업에 대한 이 대체 보기의 또 다른 이점은 전체 프레임의 타이밍 분석을 제공할 수 있다는 것입니다. Long Tasks API와 같이 startTimeduration만 포함하는 대신 LoAF는 프레임 시간의 다양한 부분을 훨씬 더 자세히 분류합니다.

프레임 타임스탬프 및 기간

  • startTime: 탐색 시작 시간을 기준으로 긴 애니메이션 프레임의 시작 시간입니다.
  • duration: 긴 애니메이션 프레임의 지속 시간입니다 (표시 시간을 제외함).
  • renderStart: requestAnimationFrame 콜백, 스타일 및 레이아웃 계산, 크기 조절 관찰자, 교차 관찰자 콜백을 포함하는 렌더링 주기의 시작 시간입니다.
  • styleAndLayoutStart: 스타일 및 레이아웃 계산에 소비된 기간의 시작입니다.
  • firstUIEventTimestamp: 이 프레임 중에 처리되는 첫 번째 UI 이벤트 (마우스/키보드 등)의 시간입니다.
  • blockingDuration: 애니메이션 프레임이 입력 또는 기타 높은 우선순위 태스크의 처리를 차단하는 총 시간(밀리초)입니다.

blockingDuration에 관한 설명

긴 애니메이션 프레임은 여러 작업으로 구성될 수 있습니다. blockingDuration는 50밀리초보다 긴 태스크 기간 (가장 긴 태스크 내 최종 렌더링 기간 포함)의 합계입니다.

예를 들어 긴 애니메이션 프레임이 55밀리초와 65밀리초의 두 태스크와 20밀리초의 렌더링으로 구성된 경우 duration는 약 140밀리초이고 blockingDuration는 (55 - 50) + (65 + 20 - 50) = 40밀리초입니다. 이 140밀리초 길이의 애니메이션 프레임 중 40밀리초 동안 프레임은 입력 처리가 차단된 것으로 간주되었습니다.

duration 또는 blockingDuration을 볼지 여부

일반적인 60Hz 디스플레이의 경우 브라우저는 원활한 업데이트를 위해 최소 16.66밀리초마다 또는 입력 처리와 같은 우선순위가 높은 작업 후에(대응성 업데이트를 위해) 프레임을 예약하려고 시도합니다. 그러나 입력이나 다른 우선순위가 높은 작업이 없고 다른 작업의 대기열이 있는 경우 브라우저는 일반적으로 작업이 얼마나 잘 분할되어 있는지와 관계없이 16.66밀리초를 훨씬 지나도 현재 프레임을 계속 진행합니다. 즉, 브라우저는 항상 입력에 우선순위를 두려고 하지만 렌더링 업데이트보다 작업 대기열을 처리할 수도 있습니다. 이는 렌더링이 비용이 많이 드는 프로세스이기 때문에 여러 작업의 결합된 렌더링 작업을 처리하면 일반적으로 전체 작업이 줄어들기 때문입니다.

따라서 blockingDuration가 낮거나 0인 긴 애니메이션 프레임도 입력에 반응하는 것처럼 느껴야 합니다. 따라서 긴 작업을 분할하여 blockingDuration를 줄이거나 제거하는 것이 INP로 측정한 반응성을 개선하는 데 중요합니다.

그러나 blockingDuration와 관계없이 긴 애니메이션 프레임이 많으면 UI 업데이트가 지연되어 INP로 측정한 반응성에는 문제가 덜 되더라도 여전히 부드러움에 영향을 미치고 스크롤이나 애니메이션에 지연된 사용자 인터페이스 느낌을 줄 수 있습니다. 이 영역의 문제를 이해하려면 duration를 살펴보세요. 하지만 작업을 분할하여 해결할 수 없고 대신 작업을 줄여야 하므로 최적화하기가 더 까다로울 수 있습니다.

프레임 타이밍

앞서 언급한 타임스탬프를 사용하면 긴 애니메이션 프레임을 타이밍으로 나눌 수 있습니다.

시간 계산
시작 시간 startTime
종료 시간 startTime + duration
작업 기간 renderStart ? renderStart - startTime : duration
렌더링 소요 시간 renderStart ? (startTime + duration) - renderStart: 0
렌더링: 레이아웃 전 시간 styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
렌더링: 스타일 및 레이아웃 시간 styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

향상된 스크립트 기여 분석

long-animation-frame 항목 유형에는 긴 애니메이션 프레임에 기여한 각 스크립트의 더 나은 기여 분석 데이터가 포함됩니다 (5밀리초가 넘는 스크립트의 경우).

Long Tasks API와 마찬가지로 이는 각 항목에 다음을 자세히 설명하는 기여 분석 항목 배열로 제공됩니다.

  • nameEntryType는 모두 script을 반환합니다.
  • 스크립트가 호출된 방식을 나타내는 의미 있는 invoker (예: 'IMG#id.onload', 'Window.requestAnimationFrame', 'Response.json.then')입니다.
  • 스크립트 진입점의 invokerType:
    • user-callback: 웹 플랫폼 API에서 등록된 알려진 콜백 (예: setTimeout, requestAnimationFrame)
    • event-listener: 플랫폼 이벤트 리스너입니다 (예: click, load, keyup).
    • resolve-promise: 플랫폼 약속의 핸들러입니다 (예: fetch()). 약속의 경우 동일한 약속의 모든 핸들러가 하나의 '스크립트'로 함께 혼합됩니다..
    • reject-promise: resolve-promise와 같지만 거부된 경우입니다.
    • classic-script: 스크립트 평가 (예: <script> 또는 import())
    • module-script: classic-script와 동일하지만 모듈 스크립트에 사용됩니다.
  • 해당 스크립트의 별도 타이밍 데이터:
    • startTime: 진입 함수가 호출된 시간입니다.
    • duration: startTime과 후속 마이크로태스크 대기열의 처리가 완료된 시점 사이의 시간입니다.
    • executionStart: 컴파일 후 시간입니다.
    • forcedStyleAndLayoutDuration: 이 함수 내에서 강제 레이아웃 및 스타일을 처리하는 데 걸린 총 시간입니다 (트래싱 참고).
    • pauseDuration: 동기 작업 (알림, 동기식 XHR) '일시중지'에 소비된 총 시간입니다.
  • 스크립트 소스 세부정보:
    • sourceURL: 사용 가능한 경우 스크립트 리소스 이름 (또는 찾을 수 없는 경우 비어 있음)입니다.
    • sourceFunctionName: 사용 가능한 경우 스크립트 함수 이름 (또는 찾을 수 없는 경우 비어 있음)입니다.
    • sourceCharPosition: 가능한 경우 스크립트 문자 위치 (또는 찾을 수 없는 경우 -1)입니다.
  • windowAttribution: 긴 애니메이션 프레임이 발생한 컨테이너 (최상위 문서 또는 <iframe>)입니다.
  • window: 동일 출처 창에 대한 참조입니다.

소스 항목이 제공되는 경우 개발자는 호출 스크립트의 문자 위치에 이르기까지 긴 애니메이션 프레임의 각 스크립트가 어떻게 호출되었는지 정확하게 알 수 있습니다. 이렇게 하면 긴 애니메이션 프레임이 발생한 JavaScript 리소스의 정확한 위치를 알 수 있습니다.

long-animation-frame 성능 항목의 예

단일 스크립트를 포함하는 전체 long-animation-frame 실적 항목 예는 다음과 같습니다.

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

이를 통해 웹사이트는 렌더링 업데이트 지연의 원인을 파악할 수 있는 전례 없는 양의 데이터를 얻을 수 있습니다.

현장에서 Long Animation Frames API 사용

Chrome DevTools 및 Lighthouse와 같은 도구는 문제를 발견하고 재현하는 데 유용하지만 현장 데이터만 제공할 수 있는 사용자 환경의 중요한 측면을 놓칠 수 있는 실험실 도구입니다.

Long Animation Frames API는 Long Tasks API가 할 수 없는 사용자 상호작용에 관한 중요한 문맥 데이터를 수집하기 위해 현장에서 사용하도록 설계되었습니다. 이렇게 하면 다른 방법으로는 발견하지 못할 수 있는 상호작용 문제를 식별하고 재현하는 데 도움이 됩니다.

긴 애니메이션 프레임 API 지원 기능 감지

다음 코드를 사용하여 API가 지원되는지 테스트할 수 있습니다.

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

Long Animation Frames API의 가장 명확한 사용 사례는 다음 페인트와의 상호작용 (INP) 문제를 진단하고 해결하는 데 도움이 되는 것입니다. 이것이 Chrome팀이 이 API를 개발한 주요 이유 중 하나였습니다. 좋은 INP는 상호작용부터 프레임이 페인트될 때까지 200밀리초 이내에 모든 상호작용에 응답하는 INP입니다. Long Animation Frames API는 50밀리초 이상 걸리는 모든 프레임을 측정하므로 문제가 있는 대부분의 INP에는 이러한 상호작용을 진단하는 데 도움이 되는 LoAF 데이터가 포함되어야 합니다.

'INP LoAF'는 다음 다이어그램과 같이 INP 상호작용이 포함된 LoAF입니다.

페이지에 있는 긴 애니메이션 프레임의 예로 INP LoAF가 강조표시되어 있습니다.
페이지에는 여러 LoAF가 있을 수 있으며, 그중 하나는 INP 상호작용과 관련이 있습니다.

경우에 따라 INP 이벤트가 두 개의 LoAF에 걸쳐 있을 수 있습니다. 일반적으로 프레임이 이전 프레임의 렌더링 부분을 시작한 후에 상호작용이 발생하여 이벤트 핸들러가 다음 프레임에서 처리되는 경우입니다.

페이지에 있는 긴 애니메이션 프레임의 예로 INP LoAF가 강조표시되어 있습니다.
페이지에는 여러 LoAF가 있을 수 있으며, 그중 하나는 INP 상호작용과 관련이 있습니다.

드물지만 두 개 이상의 LoAF에 걸쳐 있을 수도 있습니다.

INP 상호작용과 연결된 LoAF 데이터를 기록하면 INP 상호작용에 관한 더 많은 정보를 얻을 수 있어 문제를 진단하는 데 도움이 됩니다. 이는 입력 지연을 이해하는 데 특히 유용합니다. 해당 프레임에서 실행 중인 다른 스크립트를 확인할 수 있기 때문입니다.

이벤트 핸들러가 이러한 값을 재현하지 않는 경우 설명할 수 없는 처리 시간표시 지연을 파악하는 것도 유용할 수 있습니다. 사용자에게 다른 스크립트가 실행 중일 수 있으며 이 스크립트는 자체 테스트에 포함되지 않을 수 있기 때문입니다.

INP 항목을 관련 LoAF 항목과 연결하는 직접적인 API는 없지만 각 항목의 시작 시간과 종료 시간을 비교하여 코드에서 연결할 수는 있습니다 (WhyNp 예시 스크립트 참고). web-vitals 라이브러리에는 v4의 INP 기여 분석 인터페이스의 longAnimationFramesEntries 속성에 있는 모든 교차 LoAF가 포함됩니다.

LoAF 항목을 연결한 후에는 INP 저작자 표시와 함께 정보를 포함할 수 있습니다. scripts 객체는 이러한 프레임에서 실행 중인 다른 항목을 표시할 수 있으므로 가장 중요한 정보가 포함되어 있습니다. 따라서 이 데이터를 애널리틱스 서비스로 다시 비콘하면 상호작용이 느린 이유를 더 잘 이해할 수 있습니다.

INP 상호작용에 대한 LoAF를 보고하면 페이지에서 가장 시급한 상호작용 문제를 파악하는 데 도움이 됩니다. 각 사용자는 페이지와 다르게 상호작용할 수 있으며, INP 기여 분석 데이터가 충분히 확보되면 INP 기여 분석 데이터에 여러 잠재적 문제가 포함됩니다. 이렇게 하면 볼륨별로 스크립트를 정렬하여 느린 INP와 관련된 스크립트를 확인할 수 있습니다.

더 긴 애니메이션 데이터를 분석 엔드포인트로 다시 보고

INP LoAF만 살펴보는 단점은 향후 INP 문제를 일으킬 수 있는 다른 잠재적인 개선 영역을 놓칠 수 있다는 점입니다. 이로 인해 INP 문제를 해결하면 큰 개선이 있을 것으로 기대했지만 다음으로 느린 상호작용이 그보다 약간 나은 정도여서 INP가 크게 개선되지 않는 것처럼 느껴질 수 있습니다.

따라서 INP LoAF만 보는 대신 페이지 전체 기간의 모든 LoAF를 고려하는 것이 좋습니다.

LoAF가 많은 페이지로, 그중 일부는 INP 상호작용이 아니더라도 상호작용 중에 발생합니다.
모든 LoAF를 살펴보면 향후 INP 문제를 파악하는 데 도움이 될 수 있습니다.

하지만 각 LoAF 항목에는 상당한 양의 데이터가 포함되어 있으므로 분석을 일부 LoAF로만 제한하는 것이 좋습니다. 또한 긴 애니메이션 프레임 항목은 상당히 클 수 있으므로 개발자는 항목의 어떤 데이터를 분석으로 전송할지 결정해야 합니다. 예를 들어 항목의 요약 시간, 스크립트 이름 또는 필요할 수 있는 기타 문맥 데이터의 최소 집합 등이 여기에 해당합니다.

긴 애니메이션 프레임 데이터의 양을 줄이기 위해 권장되는 패턴은 다음과 같습니다.

이러한 패턴 중 가장 적합한 패턴은 최적화 여정의 진행 정도와 긴 애니메이션 프레임의 일반적인 정도에 따라 다릅니다. 이전에 반응성 최적화를 수행한 적이 없는 사이트의 경우 LoAF가 많을 수 있으므로 상호작용이 있는 LoAF로만 제한하거나 높은 기준점을 설정하거나 가장 나쁜 LoAF만 살펴보는 것이 좋습니다.

일반적인 응답성 문제를 해결할 때 상호작용 또는 높은 차단 시간으로만 제한하지 않거나 기준점을 낮추어 범위를 넓힐 수 있습니다.

상호작용이 있는 긴 애니메이션 프레임 관찰

INP 긴 애니메이션 프레임 외에도 유용한 정보를 얻으려면 blockingDuration가 높은 상호작용 (firstUIEventTimestamp 값의 존재로 감지할 수 있음)이 있는 모든 LoAF를 살펴볼 수 있습니다.

또한 이 방법은 두 가지를 상관시키는 것보다 더 간단하게 INP LoAF를 모니터링할 수 있는 방법이 될 수 있습니다. 상관시키는 방법은 더 복잡할 수 있습니다. 대부분의 경우 여기에는 특정 방문의 INP LoAF가 포함되며, 드물지만 포함되지 않는 경우에도 수정해야 하는 긴 상호작용이 표시됩니다. 다른 사용자의 INP 상호작용일 수 있기 때문입니다.

다음 코드는 프레임 중에 상호작용이 발생한 blockingDuration가 100밀리초를 초과하는 모든 LoAF 항목을 로깅합니다. 여기서는 100이 선택됩니다. 200밀리초의 '좋음' INP 기준점보다 작기 때문입니다. 필요에 따라 더 높거나 낮은 값을 선택할 수 있습니다.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > REPORTING_THRESHOLD_MS &&
      entry.firstUIEventTimestamp > 0
    ) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

차단 시간이 긴 긴 애니메이션 프레임 관찰

상호작용이 있는 긴 애니메이션 프레임을 모두 확인하는 대신 차단 시간이 긴 긴 애니메이션 프레임을 모두 확인하는 것이 좋습니다. 이는 긴 애니메이션 프레임 중에 사용자가 상호작용하는 경우 발생할 수 있는 잠재적인 INP 문제를 나타냅니다.

다음 코드는 프레임 중에 상호작용이 발생한 경우 차단 시간이 100밀리초를 초과하는 모든 LoAF 항목을 로깅합니다. 여기서 100은 잠재적인 문제 프레임을 식별하는 데 도움이 되는 200밀리초의 '양호' INP 기준점보다 낮고, 보고되는 긴 애니메이션 프레임의 양을 최소로 유지하기 때문에 선택되었습니다. 필요에 따라 더 높거나 낮은 값을 선택할 수 있습니다.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

원활성을 개선하기 위해 중요한 UI 업데이트 중에 긴 애니메이션 프레임 관찰

앞서 언급한 바와 같이 차단 시간이 긴 애니메이션 프레임을 살펴보면 입력 응답성을 해결하는 데 도움이 됩니다. 하지만 원활한 재생을 위해 긴 duration을 사용하여 긴 애니메이션 프레임을 모두 살펴봐야 합니다.

노이즈가 상당히 많을 수 있으므로 다음과 같은 패턴으로 측정값을 주요 지점으로 제한하는 것이 좋습니다.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  if (measureImportantUIupdate) {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

async function doUIUpdatesWithMeasurements() {
  measureImportantUIupdate = true;
  await doUIUpdates();
  measureImportantUIupdate = false;
}

가장 긴 애니메이션 프레임 확인

사이트는 설정된 임곗값을 사용하는 대신 가장 긴 애니메이션 프레임에서 데이터를 수집하여 비콘으로 전송해야 하는 데이터의 양을 줄일 수 있습니다. 따라서 페이지에서 긴 애니메이션 프레임이 얼마나 발생하든 가장 나쁜 5개, 10개 또는 절대적으로 필요한 긴 애니메이션 프레임의 데이터만 다시 비콘으로 전송됩니다.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

이러한 전략을 결합할 수도 있습니다. 상호작용 시간이 100밀리초를 초과하는 LoAF 중 가장 나쁜 10개만 살펴봅니다.

적절한 시점 (visibilitychange 이벤트가 가장 좋음)에 애널리틱스로 다시 비콘을 전송합니다. 로컬 테스트의 경우 주기적으로 console.table를 사용할 수 있습니다.

console.table(longestBlockingLoAFs);

긴 애니메이션 프레임에서 공통 패턴 파악

대신 긴 애니메이션 프레임 항목에 가장 많이 표시되는 일반적인 스크립트를 살펴볼 수 있습니다. 스크립트 및 문자 위치 수준에서 데이터를 다시 보고하여 반복 위반자를 식별할 수 있습니다.

이는 성능 문제를 일으키는 테마나 플러그인을 여러 사이트에서 식별할 수 있는 맞춤설정 가능한 플랫폼에 특히 적합합니다.

긴 애니메이션 프레임에서 일반적인 스크립트 또는 서드 파티 출처의 실행 시간을 합산하여 다시 보고하여 사이트 또는 사이트 모음 전반에서 긴 애니메이션 프레임에 공통적으로 기여하는 요소를 식별할 수 있습니다. 예를 들어 URL을 확인하려면 다음을 실행합니다.

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

출력 예는 다음과 같습니다.

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

도구에서 Long Animation Frames API 사용

또한 이 API를 사용하면 로컬 디버깅을 위한 추가 개발자 도구를 사용할 수 있습니다. Lighthouse 및 Chrome DevTools와 같은 일부 도구는 하위 수준의 추적 세부정보를 사용하여 이러한 데이터의 대부분을 수집할 수 있었지만, 이 상위 수준 API를 사용하면 다른 도구가 이 데이터에 액세스할 수 있습니다.

DevTools에 긴 애니메이션 프레임 데이터 표시

performance.measure() API를 사용하여 DevTools에 긴 애니메이션 프레임을 표시할 수 있습니다. 그러면 성능 트레이스의 DevTools 사용자 시간 트랙에 표시되어 성능 개선에 집중해야 할 부분을 보여줍니다. DevTools 확장성 API를 사용하면 자체 트랙에 표시할 수도 있습니다.

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
      detail: {
        devtools: {
          dataType: "track-entry",
          track: "Long animation frames",
          trackGroup: "Performance Timeline",
          color: "tertiary-dark",
          tooltipText: 'LoAF'
        }
      }
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
기본 플레임 차트와 비교할 수 있는 긴 애니메이션 프레임 데이터를 보여주는 맞춤 트랙이 있는 DevTools 성능 패널 트레이스
DevTools에 긴 애니메이션 프레임 데이터가 표시됩니다.

장기적으로는 긴 애니메이션 프레임이 DevTools 자체에 통합될 가능성이 높지만, 그때까지는 이전 코드 스니펫을 사용하여 DevTools에 표시할 수 있습니다.

또한 위 그림의 첫 번째 항목은 브라우저가 여러 태스크를 렌더링하는 대신 동일한 긴 애니메이션 프레임에서 여러 태스크를 함께 처리한 위치를 보여줍니다. 앞서 언급했듯이 우선순위가 높은 입력 작업은 없지만 작업 대기열이 있는 경우 이러한 상황이 발생할 수 있습니다. 첫 번째 긴 작업에는 완료해야 할 렌더링 업데이트가 몇 가지 있지만 (그렇지 않으면 현재 긴 애니메이션 프레임이 재설정되고 다음 작업으로 새 프레임이 시작됨) 브라우저는 이 렌더링을 즉시 실행하는 대신 여러 작업을 추가로 처리한 후에야 긴 렌더링 작업을 실행하고 긴 애니메이션 프레임을 종료했습니다. 이는 지연된 렌더링을 식별하는 데 도움이 되도록 긴 작업뿐만 아니라 DevTools에서 긴 애니메이션 프레임을 보는 것이 얼마나 유용한지 보여줍니다.

다른 개발자 도구에서 긴 애니메이션 프레임 데이터 사용

Web Vitals 확장 프로그램이 로깅 요약 디버그 정보에 값을 표시하여 성능 문제를 진단했습니다.

이제 각 INP 콜백 및 각 상호작용에 대한 긴 애니메이션 프레임 데이터도 표시됩니다.

Core Web Vitals 확장 프로그램 콘솔 로깅
웹 바이탈 확장 프로그램 콘솔 로깅에 LoAF 데이터가 표시됩니다.

자동 테스트 도구에서 긴 애니메이션 프레임 데이터 사용

마찬가지로 CI/CD 파이프라인의 자동화된 테스트 도구는 다양한 테스트 모음을 실행하는 동안 긴 애니메이션 프레임을 측정하여 잠재적인 성능 문제에 관한 세부정보를 표시할 수 있습니다.

FAQ

이 API에 관해 자주 묻는 질문(FAQ)은 다음과 같습니다.

Long Tasks API를 확장하거나 반복하면 안 되나요?

이는 잠재적인 응답성 문제를 측정하는 데 유사하지만 궁극적으로는 다른 측정항목을 보고하는 대안적인 방법입니다. 기존 Long Tasks API를 사용하는 사이트가 계속 작동하도록 하여 기존 사용 사례가 중단되지 않도록 하는 것이 중요합니다.

Long Tasks API는 LoAF의 일부 기능 (예: 더 나은 기여 분석 모델)을 활용할 수 있지만, 태그가 아닌 프레임에 집중하면 기존 Long Tasks API와 근본적으로 다른 API가 될 수 있는 많은 이점을 얻을 수 있습니다.

스크립트 항목이 없는 이유는 무엇인가요?

이는 긴 애니메이션 프레임이 JavaScript가 아닌 대규모 렌더링 작업으로 인해 발생했음을 나타낼 수 있습니다.

긴 애니메이션 프레임이 JavaScript로 인해 발생했지만 앞서 언급한 다양한 개인 정보 보호 이유 (주로 페이지에서 소유하지 않은 JavaScript)로 인해 스크립트 저작자 표시를 제공할 수 없는 경우에도 이러한 문제가 발생할 수 있습니다.

스크립트 항목이 있지만 소스 정보가 없거나 제한적인 이유는 무엇인가요?

이는 참조할 만한 좋은 출처가 없기 때문에 발생할 수 있습니다.

no-cors cross-origin 스크립트의 경우에도 스크립트 정보가 제한되지만 <script> 호출에 crossOrigin = "anonymous"를 추가하여 CORS를 사용하여 이러한 스크립트를 가져오면 해결할 수 있습니다.

예를 들어 페이지에 추가할 기본 Google 태그 관리자 스크립트는 다음과 같습니다.

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->

GTM에 전체 기여 분석 세부정보를 제공할 수 있도록 j.crossOrigin = "anonymous"를 추가하도록 개선할 수 있습니다.

Long Tasks API가 대체되나요?

Long Animation Frames API가 긴 작업을 측정하는 데 더 나은, 더 완전한 API라고 생각하지만 현재로서는 Long Tasks API를 지원 중단할 계획이 없습니다.

의견을 보내주세요

GitHub 문제 목록에서 의견을 제공하거나 Chrome의 API 구현에 있는 버그를 Chrome 문제 추적기에서 신고할 수 있습니다.

결론

Long Animation Frames API는 이전의 Long Tasks API보다 많은 잠재적 이점을 제공하는 흥미로운 새로운 API입니다.

INP로 측정한 응답성 문제를 해결하는 데 중요한 도구로 입증되고 있습니다. INP는 최적화하기 어려운 측정항목이며 이 API는 Chrome팀이 개발자가 문제를 더 쉽게 파악하고 해결할 수 있도록 노력하는 방법 중 하나입니다.

하지만 Long Animation Frames API의 범위는 INP를 넘어서며, 웹사이트 사용자 환경의 전반적인 원활함에 영향을 줄 수 있는 느린 업데이트의 다른 원인을 파악하는 데 도움이 될 수 있습니다.

감사의 말씀

Unsplash헨리 비님 제공 썸네일 이미지