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

Taboola는 Long Animation Frames API (LoAF)를 활용하고 스마트한 수익 창출 전략을 채택해 광고 실적 저하 없이 게시자의 웹사이트 응답성을 개선할 수 있었습니다.

David Belford
David Belford

다음 페인트에 대한 상호작용 (INP)은 사용자 입력에 대한 웹사이트의 응답성을 평가하는 측정항목입니다. INP는 사용자가 상호작용(예: 클릭, 탭, 입력)을 시작한 시점부터 그에 따른 시각적 피드백까지의 시간을 측정합니다. INP는 2024년 3월에 최초 입력 반응 시간 (FID)을 Core Web Vital로 대체하기로 되어 있습니다.

Taboola는 세계 최고의 콘텐츠 탐색 플랫폼으로, 개방형 웹에서 매초 500,000건의 추천을 제공하고 있습니다. Taboola의 독점 게시자인 9,000곳은 이러한 추천을 바탕으로 수익을 창출하고 독자의 참여를 유도할 수 있습니다. 게시자는 자바스크립트를 사용하여 페이지에서 추천을 렌더링합니다.

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

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

TBT를 INP의 프록시로 사용

총 차단 시간 (TBT)은 페이지 응답에 영향을 미칠 정도로 오랫동안 기본 스레드가 차단된 위치를 식별하는 실습 기반 측정항목입니다. 응답성을 측정하는 필드 측정항목(예: INP)은 높은 TBT의 영향을 받을 수 있습니다. 애니 설리반휴대기기에서의 TBT와 INP의 상관관계를 조사한 결과, 기본 스레드 차단 시간을 최소화하면 사이트에서 좋은 INP 점수를 얻을 가능성이 더 높아진 것으로 나타났습니다.

이러한 상관관계와 높은 TBT에 대한 Taboola 게시자의 우려 덕분에 Taboola는 이 측정항목에 대한 기여도를 최소화하는 데 집중하게 되었습니다.

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

Taboola는 TBT를 INP의 프록시 측정항목으로 사용하여 자바스크립트 실행 시간을 모니터링하고 최적화하여 코어 웹 바이탈에 미치는 잠재적 영향을 제한했습니다. 다음과 같은 조치로 시작했습니다.

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

그러나 Taboola는 이러한 도구로 TBT를 분석할 때 몇 가지 한계가 있다는 사실을 발견했습니다.

  • Long Tasks API는 작업의 출처를 원본 도메인 또는 특정 스크립트로 지정할 수 없으므로 장기 작업의 소스를 식별하기가 더 어렵습니다.
  • Long Tasks API는 렌더링 지연을 일으킬 수 있는 작업과 레이아웃 변경의 조합이 아닌 장기 작업만 식별합니다.

이러한 문제를 해결하기 위해 Taboola는 사용자 입력 반응에 미치는 실질적인 영향을 이해하기 위해 LoAF (Long Animation Frames) API 오리진 트라이얼에 참여했습니다. 오리진 트라이얼은 신규 또는 실험적인 기능에 대한 액세스를 제공합니다. 이를 통해 개발자는 사용자가 한시적으로 사용해 볼 수 있는 새로운 기능을 테스트할 수 있습니다.

이 과제에서 가장 어려운 부분은 Google Ads KPI(핵심성과지표)를 저해하거나 게시자에게 리소스 지연을 초래하지 않고 INP를 성공적으로 개선하는 것이라는 점을 강조하고 싶습니다.

LoAF를 사용한 INP 영향 평가

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

  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 });
  • Taboola는 loafEntryAnalysis 함수를 사용해 주요 기여자인 항목을 식별할 수 있었습니다.
  • 전체 스크립트 길이의 절반 이상이 Taboola로 인해 발생하거나 Taboola 스크립트가 실행되는 데 50밀리초 넘게 걸리는 경우 Taboola가 주요 기여자로 간주됩니다.
  • 긴 애니메이션 프레임으로 인해 사용자 상호작용이 지연되면 firstUIEventTimeStamp이 생성됩니다. 가장 긴 차단 기간이 전체 INP 점수로 간주됩니다. 또한 Taboola가 firstUIEventTimeStamp를 트리거한 시점을 파악하여 Taboola INP 점수를 계산할 수 있습니다.

Taboola는 LoAF를 통해 수집된 데이터를 활용해 수익 창출 기회를 적용할 수 있는 영역을 보여주는 기여도 표를 다음과 같이 만들었습니다.

스크립트 시간 (밀리초)
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 Engine: 새로운 수익 창출 전략

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 엔진의 일부로 사용할 수 있는 스크립트 최적화 기회도 강조했습니다.

Taboola는 TRECS와 Performance Fader의 영향을 파악하기 위해 게시자 파트너 패널에서 스크립트를 생성하지 않고 기존 엔진에 대한 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)과 같은 비즈니스 측정항목은 부정적인 영향을 받지 않았습니다. Taboola는 Google Ads KPI에 예상한 부정적인 결과가 전혀 없이 INP가 긍정적으로 개선됨에 따라 제품에 대한 게시자의 인식을 점진적으로 개선할 예정입니다.

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

기본 스레드 차단 시간을 개선하기 위해 새 TRECS 및 Performance Fader 엔진을 적용한 후 차단된 기본 스레드 시간에 관한 Lighthouse 감사 스크린샷입니다. 감사 시간이 206밀리초로 단축되었으며 최적화 전의 712밀리초와 비교됩니다.
Taboola의 새로운 엔진 덕분에 RELEASE.js 등의 스크립트가 TBT를 485ms (-70%) 줄였습니다.

이를 통해 LoAF를 사용하여 INP의 원인을 파악하고 Performance Fader로 수익을 창출하는 기법을 배포함으로써 Taboola의 파트너가 광고 및 페이지 성능 면에서 최대의 성공을 거둘 수 있음을 알 수 있습니다.

결론

INP 최적화는 복잡한 과정이며, 특히 파트너 웹사이트에서 서드 파티 스크립트를 사용하는 경우 더욱 그렇습니다. 최적화가 시작되기 전에 특정 스크립트에 INP를 기여하면 추측과 다른 사이트 성능 측정항목에 대한 잠재적 피해가 사라집니다.LoAF API는 특히 삽입된 서드 파티에서 INP 문제를 식별하고 해결하는 데 유용한 도구임이 입증되었습니다.

LoAF를 scheduler.postTask() 사용과 같은 효과적인 수익 창출 전략과 함께 사용하면 페이지 응답성이 낮은 원인을 관찰하고 파악하여 웹사이트의 INP를 개선하는 데 필요한 정보를 얻을 수 있습니다.

이 작업에 기여해 주신 Taboola 엔지니어링 및 제품팀의 Gilberto Cocchi, Noam Rosenthal, Rick Viscomi, 그리고 Taboola 엔지니어링 및 제품팀의 Dedi Hakak, Anat Dagan, Omri Ariav에게 특별한 감사 인사를 전합니다.