긴 애니메이션 프레임 API

Long Animation Frames API (LoAF - Lo-Af로 발음됨)는 느린 사용자 인터페이스 (UI) 업데이트를 더 잘 파악할 수 있도록 Long Tasks API의 업데이트입니다. 이는 응답성을 측정하는 INP (다음 페인트에 대한 상호작용) Core Web Vitals 측정항목에 영향을 미칠 가능성이 높은 느린 애니메이션 프레임을 식별하거나 부드러움에 영향을 미치는 다른 UI 버벅거림을 식별하는 데 유용할 수 있습니다.

API 상태

브라우저 지원

  • 123
  • x
  • x
  • x

Chrome 116에서 Chrome 122로의 오리진 트라이얼에 따라 LoAF API가 Chrome 123에서 출시되었습니다.

Long Tasks API

브라우저 지원

  • 58
  • 79
  • x
  • x

소스

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

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

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

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

응답성을 개선하려면 장기 작업을 중단하는 것이 좋습니다. 긴 작업 각각을 일련의 작은 작업으로 분할하면 여러 작업 사이에서 더 중요한 작업을 실행하여 상호작용에 응답할 때 상당한 지연을 방지할 수 있습니다.

따라서 응답성을 개선하려고 할 때 가장 먼저 하는 일은 성능 트레이스를 실행하고 장기 작업을 확인하는 것입니다. Lighthouse와 같은 실험실 기반 감사 도구 (긴 기본 스레드 작업 피하기 감사가 있음)를 사용하거나 Chrome DevTools에서 장기 작업을 확인하면 됩니다.

실험실 기반 테스트는 응답성 문제를 식별하기 위한 출발점이 되는 경우가 많습니다. 이러한 도구에는 상호작용이 포함되지 않을 수 있습니다. 상호작용은 상호작용의 일부분에 불과합니다. 현장에서 상호작용이 느린 원인을 측정하는 것이 이상적입니다.

Long Tasks API의 단점

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

Real User Monitoring (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

브라우저 지원

  • 123
  • x
  • x
  • x

LoAF (Long Animation Frames API)는 Long Tasks API의 몇 가지 단점을 해결하여 개발자가 더 활용 가능한 분석 정보를 얻어 응답성 문제를 해결하고 INP를 개선하는 데 도움이 되는 새로운 API입니다.

우수한 응답성은 페이지가 페이지와의 상호작용에 빠르게 반응한다는 것을 의미합니다. 여기에는 사용자가 필요로 하는 모든 업데이트를 제때에 그릴 수 있고 이러한 업데이트가 차단되는 것을 방지할 수 있어야 합니다. INP의 경우 200밀리초 이내에 응답하는 것이 좋지만 다른 업데이트 (예: 애니메이션)의 경우에는 너무 길더라도 응답합니다.

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

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

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

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

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

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

그러나 성능 항목의 경우 maxBufferSize이 있고 그 이후에는 최신 항목이 삭제되므로 PerformanceObserver 접근 방식을 사용하는 것이 좋습니다. long-animation-frame 버퍼 크기는 long-tasks와 마찬가지로 200으로 설정됩니다.

작업 대신 프레임을 살펴볼 때의 이점

작업 관점이 아닌 프레임 관점에서 이를 살펴보는 주요 이점은 긴 애니메이션은 누적적으로 긴 애니메이션 프레임을 생성하는 여러 작업으로 구성할 수 있다는 것입니다. 이는 애니메이션 프레임 전에 렌더링을 차단하는 작은 여러 소규모 작업의 합계가 Long Tasks API에 의해 표시되지 않을 수 있는 앞서 언급한 마지막 지점을 해결합니다.

장기 작업에서 이 대체 뷰의 또 다른 이점은 전체 프레임의 타이밍 분석을 제공하는 기능입니다. LoAF는 Long Tasks API와 같이 startTimeduration만 포함하는 대신 다음과 같이 프레임 지속 시간의 다양한 부분에 관한 훨씬 자세한 분석을 포함합니다.

  • startTime: 탐색 시작 시간을 기준으로 긴 애니메이션 프레임의 시작 시간입니다.
  • duration: 긴 애니메이션 프레임의 길이입니다 (프레젠테이션 시간 제외).
  • renderStart: 렌더링 주기의 시작 시간으로, requestAnimationFrame 콜백, 스타일 및 레이아웃 계산, 크기 조절 관찰자, Intersection Observer 콜백이 포함됩니다.
  • styleAndLayoutStart: 스타일 및 레이아웃 계산에 소요된 기간의 시작입니다.
  • firstUIEventTimestamp: 이 프레임이 진행되는 동안 처리될 첫 번째 UI 이벤트 (마우스/키보드 등)의 시간입니다.
  • blockingDuration: 애니메이션 프레임이 차단되는 시간(밀리초)입니다.

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

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

이러한 개별 타이밍에 관한 자세한 내용은 설명서를 참고하세요. 긴 애니메이션 프레임에 기여하는 활동이 무엇인지 자세히 알아볼 수 있습니다.

기여 분석 개선

long-animation-frame 항목 유형에는 긴 애니메이션 프레임에 영향을 준 각 스크립트의 더 나은 기여 분석 데이터가 포함됩니다.

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 사용 설정

Long Animation Frames API는 Chrome 123부터 기본적으로 사용 설정됩니다.

필드에서 Long Animation Frames API 사용

Lighthouse와 같은 도구는 문제를 발견하고 재현하는 데 유용하지만, 필드 데이터만이 제공할 수 있는 사용자 환경의 중요한 측면을 놓칠 수 있는 실험실 도구입니다. Long Animation Frames API는 필드에서 Long Tasks API로 할 수 없는 사용자 상호작용의 중요한 문맥 데이터를 수집하는 데 사용할 수 있습니다. 이를 통해 이전에는 발견하지 못했을 상호작용 문제를 파악하고 재현할 수 있습니다.

몇 가지 추천 전략이 그 아래에 나열되어 있지만, Chrome팀은 이 API에 대한 의견과 API가 제공하는 정보를 사용하여 개발자와 RUM 제공업체가 어떻게 자신을 보고 있는지 듣고 싶습니다.

Long Animation Frames API 지원 감지 기능

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

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

긴 애니메이션 프레임은 아직 기본적으로 지원되지 않고 이 전환 상태에 있을 때 다음 대안을 사용할 수 있습니다.

if ('PerformanceLongAnimationFrameTiming' in window) {
  // Monitor LoAFs
}

긴 애니메이션 데이터를 애널리틱스 엔드포인트에 다시 보고

표시된 대로 LoAF 성능 항목에는 중요한 정보가 포함되어 있습니다. 한 가지 전략으로는 모든 LoAF를 모니터링하고 특정 기준점을 초과하는 LoAF를 분석 엔드포인트로 비콘을 표시하여 추후 분석을 실시할 수 있습니다.

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
  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 });

긴 애니메이션 프레임 항목이 상당히 클 수 있으므로 개발자는 항목의 어떤 데이터를 애널리틱스로 전송해야 하는지 결정해야 합니다. 예를 들어 항목의 요약 시간 및 스크립트 이름 또는 필요하다고 간주될 수 있는 기타 최소 문맥 데이터 세트가 있습니다.

최악의 긴 애니메이션 프레임 관찰

비커닝해야 하는 데이터의 양을 줄이기 위해 사이트에서는 가장 긴 애니메이션 프레임에서 데이터를 수집할 수 있습니다. 따라서 페이지에서 경험하는 긴 애니메이션 프레임 수에 상관없이, 최악의 애니메이션 프레임 중 최악의 1개, 5개 또는 아무리 많은 긴 애니메이션 프레임에 대한 데이터만 비커닝됩니다.

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 });

적절한 시간 (visibilitychange 이벤트 발생 시 권장)에 애널리틱스로 다시 비콘을 보냅니다. 로컬 테스트의 경우 console.table를 주기적으로 사용할 수 있습니다.

console.table(longestBlockingLoAFs);

가장 긴 INP 상호작용으로 연결

최악의 LoAF를 관찰하는 확장 프로그램으로 INP 항목에 해당하는 LoAF 프레임을 기여 분석 데이터로 사용하여 INP 개선 방법에 관한 추가 세부정보를 제공할 수 있습니다.

현재는 INP 항목을 관련 LoAF 항목과 연결하는 직접적인 API는 없지만, 코드에서는 각각의 시작 시간과 종료 시간을 비교하여 INP 항목을 연결할 수 있습니다 (예시 스크립트 참고).

상호작용이 있는 긴 애니메이션 프레임 보고

더 적은 코드를 필요로 하는 다른 접근 방식은 프레임 중에 상호작용이 발생한 경우 (firstUIEventTimestamp 값의 존재로 감지할 수 있음) 항상 가장 큰 (또는 상위 X개의 가장 큰) LoAF 항목을 전송하는 것입니다. 대부분의 경우 여기에는 특정 방문의 INP 상호작용이 포함되며, 드물게는 다른 사용자의 INP 상호작용일 수 있으므로 해결해야 할 중요한 긴 상호작용이 표시되는 드문 경우도 있습니다.

다음 코드는 프레임 중에 상호작용이 발생한 150밀리초를 초과하는 모든 LoAF 항목을 기록합니다. 여기서는 200밀리초의 '양호' INP 임계값보다 약간 작기 때문에 150이 선택되었습니다. 필요에 따라 더 높거나 더 낮은 값을 선택할 수 있습니다.

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
      if (entry.duration > 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 });

긴 애니메이션 프레임에서 공통 패턴 식별

또 다른 전략은 긴 애니메이션 프레임 항목에서 가장 많이 나타나는 일반적인 스크립트를 살펴보는 것입니다. 스크립트 또는 문자 위치 수준에서 데이터를 다시 보고하여 반복적으로 정책을 위반하는 행위를 할 수 있었습니다.

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

긴 애니메이션 프레임에서 일반적인 스크립트(또는 서드 파티 출처)의 실행 시간을 합산하고 다시 보고하여 한 사이트 또는 사이트 모음에서 긴 애니메이션 프레임에 공통된 기여자를 식별할 수 있습니다. 예를 들어 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 사용자 시간 트랙에 표시되어 성능 개선을 위해 어떤 부분에 중점을 두어야 하는지 보여줍니다.

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
    });
  }
});

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

이 API가 장기적으로 유용하다고 확인되는 경우 DevTools 자체에 통합될 가능성이 높습니다. 하지만 그 전까지는 이전 코드 스니펫을 사용하여 이 API를 표시할 수 있습니다.

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

웹 바이탈 확장 프로그램에서 로깅 요약 디버그 정보에 값을 표시하여 성능 문제를 진단합니다. 이제 API가 출시됨에 따라 이러한 도구를 통해 데이터를 더 쉽게 표시할 수 있어 개발자가 어디에 노력을 집중해야 하는지 파악할 수 있습니다. 또한 버전 4에서는 이 기능이 웹 vitals 자바스크립트 라이브러리에 추가될 예정입니다.

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

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

FAQ

다음은 이 API에 대해 자주 묻는 질문(FAQ)입니다.

Long Tasks API에서 단순히 확장하거나 반복하지 않는 이유는 무엇인가요?

이는 잠재적 응답성 문제와 비슷하지만 최종적으로는 다른 측정값을 보고하는 또 다른 방법입니다. 기존 사용 사례에 지장을 주지 않으려면 기존 Long Tasks API를 사용하는 사이트가 계속 작동하도록 하는 것이 중요합니다.

Long Tasks API는 일부 LoAF 기능 (예: 향상된 기여 분석 모델)의 이점을 활용할 수 있지만, 작업이 아닌 프레임에 집중하는 것이 기존의 Long Tasks API와 근본적으로 다른 API라는 많은 이점을 제공한다고 생각합니다.

이 기능이 Long Tasks API를 대체하나요?

Long Animation Frames API가 장기 작업 측정에 더 적합하고 완전한 API라고 생각하지만 현재로서는 Long Tasks API를 지원 중단할 계획은 없습니다.

의견 요청

의견은 GitHub 문제 목록에서 제공할 수 있으며 Chrome의 API 구현에 관한 버그는 Chrome Issue Tracker에 제출할 수 있습니다.

결론

Long Animation Frames API는 이전의 Long Tasks API에 비해 많은 잠재적 장점이 있는 흥미롭고 새로운 API입니다.

이는 INP에서 측정한 반응성 문제를 해결하기 위한 핵심 도구임이 입증되었습니다. INP는 최적화하기 어려운 측정항목이며, 이 API는 Chrome팀에서 개발자가 더 쉽게 문제를 식별하고 해결할 수 있도록 지원하기 위해 노력하고 있습니다.

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

감사의 말

Unsplash에 있는 헨리 비의 썸네일 이미지