INP 개선을 위해 메인 스레드에 yield 하는 방법

scheduler.yield()를 사용하여 긴 작업을 나누고 페이지를 반응형으로 유지하세요

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-03-04

메인 스레드에 yield 하기

로맨틱한 영화를 상상해 보세요. 배경은 작은 마을 중심부에 있는 프랑스의 작은 시장입니다. 거리는 커피를 마시고, 크루아상을 먹으며, 꽃을 사는 커플들로 가득합니다. 이제 다른 모든 사람들은 차례를 기다려야 하는 반면, 동시에 오직 한 명의 구매자만이 판매자로부터 물건을 살 수 있다고 상상해 보세요. 제빵사는 쏟아지는 요청에 파묻히고, 꽃집에서는 말다툼이 일어나며, 로맨틱한 산책은 좌절감을 주는 기다림으로 변합니다.

글쎄요... 웹사이트가 너무 바빠질 때 일어나는 일이 바로 이런 것입니다.

최종 검토: Arjen Karel (2026년 3월)

INP에 yield가 중요한 이유

브라우저의 메인 스레드는 HTML 및 CSS 파싱, JavaScript 실행, 클릭 및 스크롤과 같은 입력 이벤트 처리, 시각적 렌더링 등 모든 중요한 프로세스를 처리합니다. 이는 단일 스레드 모델로 실행되므로 한 번에 하나의 작업만 수행할 수 있습니다. 작업이 실행되기 시작하면 브라우저는 작업을 완료할 때까지 실행하며 완료될 때까지 멈추지 않습니다. 현재 작업이 끝날 때까지 다른 작업은 예약되지 않습니다. 이를 메인 스레드 차단(blocking the main thread)이라고 합니다.

메인 스레드 차단은 저조한 Interaction to Next Paint (INP) 점수의 주요 원인입니다. 방문자가 버튼을 클릭하고 JavaScript가 메인 스레드를 200ms 동안 차단하면 스크립트가 완료될 때까지 브라우저는 응답을 페인트할 수 없습니다. INP의 처리 시간 (processing time) 단계는 정확히 이 지연을 측정합니다. 2025 Web Almanac에 따르면, 모바일의 중간 총 차단 시간(Total Blocking Time)은 1,916ms로 전년 대비 58% 증가했습니다. 이는 브라우저가 사용자 입력에 응답할 수 없는 시간이 거의 2초에 달함을 의미합니다.

이를 해결하는 한 가지 방법은 메인 스레드에 yield 하는 것입니다. yielding은 긴 작업을 여러 개의 작은 작업으로 나누어 브라우저가 그 사이에 더 중요한 작업(사용자 입력에 응답하는 등)을 처리할 수 있도록 하는 기법입니다.

긴 작업(Long tasks)과 차단 기간(blocking period): 작업이 50 밀리초보다 오래 걸리면 긴 작업으로 분류되며, 그 50 밀리초 임계값을 초과하는 시간은 해당 작업의 "차단 기간"으로 알려져 있습니다. 이러한 긴 작업을 더 작은 청크로 나누면 연산 집약적인 작업을 처리할 때에도 브라우저가 반응형을 유지할 수 있습니다.

기존의 yielding 전략

Prioritized Task Scheduling API가 나오기 전에는 메인 스레드에 yield 하는 방법이 4가지 있었습니다. 모두 한계가 있습니다.

  • setTimeout(): 가장 일반적인 전략입니다. 지연이 0인 setTimeout()은 작업을 큐의 끝에 추가하여 다른 작업이 먼저 실행될 수 있도록 합니다. 문제점: 작업이 큐의 으로만 푸시될 수 있으므로, 코드가 다시 시작되기 전에 다른 스크립트가 새치기를 할 수 있습니다. 중첩된 setTimeout() 호출은 5회 반복 후 최소 5ms의 지연을 강제하기도 합니다.
  • requestAnimationFrame(): 브라우저의 다음 리페인트 전에 실행할 함수를 큐에 넣습니다. 다음 레이아웃 업데이트 이후에 콜백이 예약되도록 보장하기 위해 주로 setTimeout()과 결합됩니다. yielding을 위해 설계되지 않았으며, 애니메이션 작업을 위해 설계되었습니다.
  • requestIdleCallback(): 브라우저의 유휴 시간 동안 실행될 수 있는 중요하지 않은 저우선순위 작업에 가장 적합합니다. 한계: 바쁜 메인 스레드에서는 예약된 작업이 즉시(또는 아예) 실행된다는 보장이 없습니다.
  • isInputPending(): 보류 중인 사용자 입력을 확인하고 입력이 감지된 경우에만 yield 합니다. Google은 더 이상 이 접근 방식을 권장하지 않습니다. 거짓 음성(false negatives)을 반환할 수 있으며, 애니메이션 및 렌더링 업데이트와 같은 성능에 중요한 다른 작업을 고려하지 않습니다.

scheduler.yield()

이러한 방법들의 한계는 Chrome 팀의 관심사였으며, 특히 많은 사이트가 INP에 실패하는 상황에서 더욱 그랬습니다. 이 문제를 해결하기 위해 그들은 개발자가 작업 순서를 재배열하거나 복잡성을 추가하지 않고도 즉시 메인 스레드에 yield 할 수 있게 해주는 새로운 API인 scheduler.yield()를 구축했습니다.

scheduler.yield()Chrome 129에서 안정화되어 배포되었으며 (2024년 9월) 이제 Safari를 제외한 모든 주요 브라우저에서 지원됩니다.

코드 예제

Safari는 아직 scheduler.yield()를 지원하지 않으므로, setTimeout() fallback을 사용하세요:

function yieldToMain() {
  if ('scheduler' in window && 'yield' in window.scheduler) {
    return window.scheduler.yield();
  }
  return new Promise((resolve) => {
    setTimeout(resolve, 0);
  });
}

yieldToMain() 함수는 window.scheduler.yield가 존재하는지 확인합니다. 존재한다면 네이티브 API를 사용합니다. 그렇지 않다면 코드는 setTimeout()으로 fallback 합니다. 이는 Google에서 권장하는 패턴과 일치합니다.

전체 Prioritized Task Scheduling API(scheduler.postTask()TaskController 포함)가 필요한 프로젝트의 경우, Google Chrome Labs에서 공식 polyfill을 유지 관리합니다.

실제 사례: yieldToMain()으로 검색 개선하기

다음은 yieldToMain()이 사용자의 검색 경험을 어떻게 개선할 수 있는지 보여주는 예입니다.

handleSearch() 함수는 먼저 버튼 콘텐츠를 업데이트하여 검색이 진행 중이라는 즉각적인 피드백을 제공한 다음, 브라우저가 해당 업데이트를 페인트할 수 있도록 yield 합니다. 그런 다음 fetchData()가 검색 데이터를 가져오고 updateHTML(data)가 결과를 표시합니다. 또 다른 yieldToMain()은 빠른 레이아웃 업데이트를 보장합니다. 마지막으로 덜 중요한 작업은 브라우저 유휴 시간 동안 예약됩니다. requestIdleCallback()은 어차피 메인 스레드가 유휴 상태일 때만 실행되므로 그 전에는 yield 하지 않았음에 유의하세요.

async function handleSearch() {
  /* 제출 후 버튼 콘텐츠를 빠르게 업데이트 */
  updateButtonToPending();

  /* 메인 스레드에 Yield */
  await yieldToMain();

  /* 데이터를 가져오고 html 업데이트 */
  const data = await fetchData();
  updateHTML(data);

  /* 메인 스레드에 다시 Yield */
  await yieldToMain();

  /* 일부 함수는 브라우저 유휴 시간 동안에만 실행되어야 함 */
  requestIdleCallback(sendDataToAnalytics);
}

또 다른 실용적인 예제는 분석 스크립트가 상호 작용을 차단하는 것을 방지하기 위해 dataLayer 푸시에 동일한 yield 패턴을 사용하는 방법을 참조하세요.

scheduler.yield()가 더 나은 이유

지연된 작업을 작업 큐의 에 추가하는 setTimeout()과 달리, scheduler.yield()는 실행을 일시 중지하고 연속된 작업을 큐의 에 배치합니다. (입력 콜백 처리와 같은) 더 높은 우선순위의 작업이 완료되자마자 코드가 다시 시작됩니다. 이것이 핵심적인 차이점입니다: 여러분의 코드가 다시 시작되기 전에 다른 스크립트가 새치기를 할 위험 없이 메인 스레드에 yield 할 수 있습니다.

yielding timeline

scheduler.yield()는 또한 Prioritized Task Scheduling API와 함께 작동하도록 설계되었습니다. scheduler.postTask() 콜백 내부에서 호출될 때, scheduler.yield()는 작업의 우선순위 수준을 상속합니다. 이 조합은 여러분의 JavaScript가 어떻게 우선순위가 지정되고 언제 yield 되는지에 대한 세밀한 제어를 제공합니다.

브라우저 지원

scheduler.yield()전 세계 브라우저의 71.5%에서 지원됩니다:

  • Chrome 129+Edge 129+: 2024년 9월부터 안정화됨
  • Firefox 142+: 2025년 8월부터 안정화됨
  • Safari: 지원되지 않음, 발표된 일정 없음

위의 yieldToMain() 함수에 있는 setTimeout() fallback은 여러분의 코드가 모든 브라우저에서 작동하도록 보장합니다. Safari 사용자는 연속된 작업이 큐의 맨 뒤로 가는 이전 동작을 얻게 되지만, 페이지는 여전히 yield 됩니다. 브라우저 지원이 증가함에 따라 더 많은 사용자가 자동으로 더 빠른 재시작을 얻게 될 것입니다.

문제가 서드파티 스크립트가 메인 스레드를 완전히 차단하는 것이라면, yield 하는 것보다 해당 스크립트를 지연(defer)시키는 것이 더 나은 첫 단계입니다. Yielding은 여러분 자신의 코드가 많은 작업을 수행해야 하지만 청크 사이에 브라우저가 반응형을 유지하기를 원할 때 도움이 됩니다.

필드에서 yielding이 INP를 개선하고 있는지 확인하려면 Real User Monitoring으로 페이지를 모니터링하세요. Lighthouse와 같은 랩 도구는 총 차단 시간(Total Blocking Time)을 측정하지만, 오직 필드 데이터만이 방문자가 경험하는 실제 INP를 보여줍니다.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

CoreDash는 제가 직접 쓰려고 만들었습니다.

1KB 미만, EU 호스팅, 쿠키 동의 배너 없음. 이제 MCP까지 지원합니다.

CoreDash 무료 체험
INP 개선을 위해 메인 스레드에 yield 하는 방법Core Web Vitals INP 개선을 위해 메인 스레드에 yield 하는 방법