콘텐츠 추천 제공업체인 Taboola가 LoAF를 사용해 게시자 파트너 웹사이트의 INP를 최대 36% 개선한 방법

Long Animation Frames API(LoAF)를 활용하고 스마트한 수익 창출 전략을 채택하여 Taboola가 광고 실적을 저하시키지 않으면서 게시자의 웹사이트 응답성을 개선한 방법

David Belford
David Belford

다음 페인트에 대한 상호작용(INP)은 웹사이트의 사용자 입력에 대한 반응성을 평가하는 측정항목입니다. INP는 사용자가 상호작용을 시작할 때(예: 클릭, 탭, 입력)부터 결과로 나타나는 시각적 피드백까지의 시간을 측정합니다. INP는 2024년 3월에 Core Web Vitals로서 최초 입력 지연 시간(FID)을 대체할 예정입니다.

Taboola는 공개 웹에서 초당 50만 개의 맞춤 콘텐츠를 제공하는 세계 최고의 콘텐츠 추천 플랫폼입니다. 이러한 추천을 통해 Taboola의 9,000개 독점 게시자 파트너가 수익을 창출하고 잠재고객의 참여를 유도할 수 있습니다. 게시자가 자바스크립트를 사용하여 페이지에 대한 추천을 렌더링합니다.

서드 파티 JavaScript는 페이지가 사용자 입력에 빠르게 반응하는 기능에 영향을 미칠 수 있으므로 Taboola는 JavaScript 파일 크기와 실행 시간을 줄이는 데 많은 투자를 해 왔습니다. Taboola는 전체 렌더링 엔진을 재설계하고 추상화 없이 브라우저 API를 직접 사용하여 INP에 미치는 영향을 최소화하고 있습니다.

이 사례 연구에서는 새로운 Long Animation Frames(LoAF) API를 사용하여 현장에서 페이지 응답성에 미치는 영향을 측정하고 사용자 환경을 개선하기 위해 특정 최적화를 적용하기 위한 후속 조치를 통해 INP를 개선하기 위한 Taboola의 여정을 다룹니다.

INP의 프록시로서 TBT

총 차단 시간(TBT)은 페이지 응답성에 영향을 줄 만큼 충분히 오래 기본 스레드가 차단된 위치를 식별하는 실험실 기반 측정항목입니다. INP와 같이 응답성을 측정하는 필드 측정항목은 TBT가 높으면 영향을 받을 수 있습니다. 애니 설리번모바일 기기의 TBT와 INP 간의 상관관계를 조사한 결과, 기본 스레드 차단 시간이 최소화되면 사이트의 INP 점수가 더 높을 가능성이 높습니다.

이러한 상관관계와 높은 TBT에 대한 Taboola 게시자의 우려로 인해 Taboola는 이 측정항목에 대한 기여도를 최소화하는 데 주력했습니다.

차단된 기본 스레드 시간에 관한 Lighthouse 감사의 스크린샷 기본 스레드는 여러 스크립트에 의해 2,630밀리초 동안 총 2,630밀리초 동안 차단되었으며, 서드 파티 자바스크립트가 이 시간 동안 712밀리초를 기여했습니다. Taboola의 RELEASE.js 스크립트가 서드 파티 차단 시간의 대부분(691밀리초)을 차지합니다.
Taboola의 이전 엔진에서는 RELEASE.js와 같은 스크립트가 691밀리초 동안 기본 스레드를 차단합니다.

Taboola는 TBT를 INP의 대리 측정항목으로 사용하여 JavaScript 실행 시간을 모니터링하고 최적화하여 Core Web Vitals에 미칠 수 있는 영향을 제한하기 시작했습니다. 먼저 다음 작업을 수행했습니다.

  • Long Tasks API를 사용하여 현장에서 문제가 있는 스크립트를 식별하고 최적화합니다.
  • PageSpeed Insights API를 사용하여 매일 10,000~15,000개의 URL을 평가하여 TBT 기여도를 추정합니다.

하지만 Taboola는 이러한 도구로 TBT를 분석하는 데 몇 가지 제한사항이 있음을 발견했습니다.

  • Long Tasks API는 작업을 출처 도메인이나 특정 스크립트에 기여할 수 없으므로 장기 작업의 소스를 식별하기가 더 어려워집니다.
  • Long Tasks API는 렌더링 지연을 일으킬 수 있는 태스크와 레이아웃 변경의 조합이 아니라 장기 작업만 식별합니다.

이러한 문제를 해결하기 위해 Taboola는 Long Animation Frames(LoAF) API 오리진 체험판에 참여하여 사용자 입력 응답성에 미치는 실제 영향을 더 잘 이해하고자 했습니다. 오리진 트라이얼을 통해 새로운 기능 또는 실험용 기능을 이용할 수 있으므로 개발자는 사용자가 한시적으로 사용해 볼 수 있는 신규 기능을 테스트할 수 있습니다.

이 과제에서 가장 어려운 점은 광고 KPI(핵심성과지표)를 손상시키거나 게시자의 리소스 지연을 일으키지 않고 INP를 성공적으로 개선하는 것이었습니다.

LoAF를 사용하여 INP 영향 평가하기

긴 애니메이션 프레임은 렌더링 업데이트가 50밀리초 이상 지연될 때 발생합니다. Taboola는 긴 작업만이 아니라 느린 사용자 인터페이스 업데이트의 원인을 파악하여 현장에서 페이지 응답성에 미치는 영향을 분석할 수 있었습니다. LoAF를 관찰한 Taboola는 다음을 할 수 있었습니다.

  1. 특정 Taboola 작업에 항목 기여도를 부여합니다.
  2. 특정 기능이 프로덕션에 배포되기 전에 성능 문제를 관찰합니다.
  3. 집계된 데이터를 수집하여 A/B 테스트에서 여러 코드 버전을 비교하고 주요 성공 측정항목을 보고합니다.

다음 JavaScript는 프로덕션에서 LoAF를 수집하여 Taboola의 영향을 분리하는 데 사용되는 단순화된 버전입니다.

function loafEntryAnalysis (entry) {
  if (entry.blockingDuration === 0) {
    return;
  }

  let taboolaIsMajor = false;
  const hasInteraction = entry.firstUIEventTimestamp > 0;
  let taboolaDuration = 0;
  const nonTaboolaLoafReport = {};
  const taboolaLoafReport = {};

  entry.scripts.forEach((script) => {
    const taboolaScriptBlockingDuration = handleLongAnimationFrameScript(script, taboolaLoafReport, nonTaboolaLoafReport);
    taboolaDuration += taboolaScriptBlockingDuration;

    if (taboolaScriptBlockingDuration > 0 || taboolaDuration > entry.duration / 2) {
      taboolaIsMajor = true;
    }
  });

  generateToboolaLoafReport(taboolaLoafReport, nonTaboolaLoafReport, hasInteraction, taboolaIsMajor);

  if (hasInteraction) {
    const global = _longAnimationFramesReport.global;
    global.inpBlockingDuration = Math.max(global.inpBlockingDuration, entry.blockingDuration);

    if (taboolaIsMajor) {
      global.taboolaInpBlockingDuration = Math.max(global.taboolaInpBlockingDuration, entry.blockingDuration);
    }
  }
}

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    loafEntryAnalysis(entry);
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
  • loafEntryAnalysis 함수를 사용하면 Taboola가 주요 기여자가 되는 항목을 식별할 수 있었습니다.
  • 총 스크립트 길이의 절반 이상이 Taboola로 인해 발생하거나 Taboola 스크립트 실행에 50밀리초 이상 걸리는 경우 Taboola가 주요 요인으로 간주됩니다.
  • 긴 애니메이션 프레임으로 인해 사용자 상호작용이 지연되면 firstUIEventTimeStamp이 생성됩니다. 가장 긴 차단 기간이 전체 INP 점수로 간주됩니다. Taboola에서 firstUIEventTimeStamp를 트리거하여 Taboola INP 점수를 계산한 시점도 확인할 수 있습니다.

LoAF로 수집된 데이터를 바탕으로 Taboola는 수익 창출 기회를 적용할 수 있는 영역을 파악하는 다음과 같은 기여 분석 표를 만들었습니다.

스크립트 기간 (밀리초)
vpaid/units/33_6_8/infra/cmTagINLINE_INSTREAM.js:106517 997
vpaid/units/33_6_8/infra/cmTagFEED_MANAGER.js:496662 561
vpaid/vPlayer/player/v15.8.6/OvaMediaPlayer.js:44631 336
libtrc/impl.20231212-23-RELEASE.js:821090 857
publisher_name/pmk-20220605.5.js:7728 336
libtrc/card-interference-detector.20231219-7-RELEASE.es6.js:183 239
Taboola RUM에서 캡처한 LoAF 스크립트 항목

TRECS 엔진: 새로운 수익 창출 전략

Taboola는 LoAF를 사용하여 스크립트 최적화 기회를 더 잘 이해할 뿐만 아니라 JavaScript 실행 및 차단 시간을 상당히 최소화하기 위해 전체 렌더링 엔진을 다시 설계했습니다.

TRECS (Taboola Recommendations Extensible Client Service)는 클라이언트 측 렌더링과 게시자의 현재 JS 코드를 유지하면서 Taboola의 추천을 로드하는 데 필요한 필수 파일의 수와 크기를 줄입니다.

LoAF를 사용하여 렌더링 차단 태스크가 식별되면 '성능 페더'는 scheduler.postTask()를 사용하여 기본 스레드에 양보하기 전에 이러한 태스크를 분할할 수 있습니다. 이 설계는 렌더링 업데이트와 같은 중요한 사용자 대상 작업을 기본 스레드를 차지하고 있을 수 있는 기존 작업과 관계없이 최대한 빨리 실행할 수 있도록 합니다.

다음은 'Performance Fader' 태스크 러너의 JS 스니펫입니다.

/**
* Send a task to run using the Fader. The task will run using the browser Scheduler, by the configuration settings, or immediately.
* @param task
* @param isBlocker
*/
function sendTaskToFader (task, isBlocker = true) {
  const publisherFaderChoice = fillOptimizationGlobals(); // Loading publisher choice
  const applyYielding = publisherFaderChoice === OptimizationFaderType.Responsiveness;

  if (applyYielding) {
    return runAsPostTask(task, isBlocker);
  }

  return runImmediately(task);
}

/**
* Yielding method using scheduler.postTask and falling back to setTimeout when it's not availabe based on the publisher choice
*/
function runAsPostTask (task, isBlocker = true) {
  if ('scheduler' in window && 'postTask' in scheduler) {
    const priority = isBlocker ? 'user-blocking': 'background';

    return window?.scheduler?.postTask(task, { priority });
  }

  const publisherChoiceEnableFallback = fillPublisherChoices();

  if (publisherChoiceEnableFallback) {
    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve(task());
      }, 0);
    });
  }

  return runImmediately(task);
}

sendTaskToFader 함수는 다음을 수행합니다.

  • 내부적으로 scheduler.postTask()를 사용하거나 (API를 사용할 수 있는 경우) setTimeout로 대체하는 runAsPostTask를 사용합니다.
  • 이 함수는 긴 애니메이션 프레임과 INP를 유발하는 코드 섹션에서 함수 호출을 래핑합니다. 이러한 코드 섹션을 더 짧은 작업으로 분할하여 INP를 줄입니다.

비즈니스 측정항목

LoAF 덕분에 Taboola는 INP에 미치는 영향을 더 잘 이해할 수 있었습니다. 또한 이 도구는 새 TRECS 엔진의 일부로 사용할 수 있는 스크립트 최적화 기회를 강조 표시했습니다.

TRECS 및 실적 페이드의 영향을 확인하기 위해 Taboola는 게시자 파트너 패널에서 스크립트가 실행되지 않는 기존 엔진을 대상으로 INP를 측정하는 A/B 테스트를 실시했습니다.

다음 표는 Taboola 네트워크의 익명의 게시자 4명의 75번째 백분위수에서 INP 결과를 밀리초로 나타냅니다.

게시자 TRECS + 실적 페더가 포함된 INP 기존 엔진이 있는 INP INP 감소 (%)
게시자 A 48 75 36%
게시자 B 153 163 6%
게시자 C 92 135 33%
게시자 D 37 52 29%

다행히 테스트 패널에서 TRECS 및 실적 페더를 사용 설정했을 때 광고 클릭률 및 1,000회 노출당수익 (RPM)과 같은 비즈니스 측정항목은 부정적인 영향을 받지 않았습니다. 광고 KPI에서 예상대로 부정적인 결과 없이 INP가 긍정적으로 개선됨에 따라 Taboola는 제품에 대한 게시자의 인식을 점진적으로 개선할 것입니다.

앞서 강조 표시된 동일한 고객에서 실행된 또 다른 Lighthouse 결과는 새 엔진을 사용할 때 Taboola의 기본 스레드 차단 시간이 크게 개선되었음을 보여줍니다.

기본 스레드 차단 시간을 개선하기 위해 새 TRECS 및 Performance Fader 엔진을 적용한 후 차단된 기본 스레드 시간에 대한 Lighthouse 감사의 스크린샷 감사가 최적화 전 712밀리초에서 206밀리초로 줄었습니다.
Taboola의 새로운 엔진을 사용하면 RELEASE.js와 같은 스크립트로 TBT를 485밀리초 (-70%) 줄일 수 있었습니다.

이는 LoAF를 사용하여 INP의 원인을 파악하고 실적 페더를 사용하여 후속 양보 기법을 배포하면 Taboola 파트너가 광고 및 페이지 실적에서 최대의 성공을 거둘 수 있음을 보여줍니다.

결론

INP 최적화는 특히 파트너 웹사이트에서 서드 파티 스크립트를 사용하는 경우 복잡한 프로세스입니다. 최적화를 시작하기 전에 특정 스크립트에 INP를 기여 분석하면 추측과 다른 사이트 성능 측정항목에 대한 잠재적 손상을 방지할 수 있습니다. LoAF API는 특히 삽입된 서드 파티의 경우 INP 문제를 식별하고 해결하는 데 유용한 도구로 입증되었습니다. 이를 통해 서드 파티는 페이지에 있는 다른 기술의 간섭을 제거하면서 특정 SDK 개선 기회를 정확하게 파악할 수 있습니다.

LoAF는 scheduler.postTask() 사용과 같은 적절한 반환 전략과 함께 사용하면 페이지 응답성 저하의 원인을 관찰하고 이해하는 데 도움이 되며, 이를 통해 웹사이트의 INP를 개선하는 데 필요한 정보를 얻을 수 있습니다.

이 작업에 기여해 주신 Google의 Gilberto Cocchi, Noam Rosenthal, Rick Viscomi, Taboola의 엔지니어링 및 제품팀의 Dedi Hakak, Anat Dagan, Omri Ariav님께 감사드립니다.