JavaScript를 지연시키거나 예약하는 16가지 방법

JavaScript를 지연시키고 예약하는 방법 알아보기

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-02-24

왜 JavaScript를 지연시키거나 예약해야 할까요?

JavaScript는 여러 가지 방식으로 웹사이트 속도를 저하시킬 수 있습니다(그리고 그렇게 될 것입니다). 이는 Core Web Vitals에 모든 종류의 부정적인 영향을 미칠 수 있습니다. JavaScript는 네트워크 리소스를 놓고 경쟁할 수 있고, CPU 리소스를 놓고 경쟁할 수 있으며(메인 스레드 차단), 심지어 웹페이지의 파싱을 차단할 수도 있습니다. 스크립트를 지연시키고 예약하면 Core Web Vitals를 획기적으로 향상시킬 수 있습니다.

최종 검토자: Arjen Karel, 2026년 2월

JavaScript가 Core Web Vitals에 미칠 수 있는 악영향을 최소화하려면 스크립트가 다운로드를 위해 대기열에 추가되는 시점을 지정하고 CPU 시간을 차지하고 메인 스레드를 차단할 수 있는 시점을 예약 하는 것이 좋습니다.

JavaScript 타이밍이 Core Web Vitals에 어떤 영향을 미칠 수 있나요?

JavaScript 타이밍이 Core Web Vitals에 어떤 영향을 미칠 수 있을까요? 다음의 실제 사례를 살펴보겠습니다. 첫 번째 페이지는 '렌더링 차단' JavaScript와 함께 로드됩니다.  Total Blocking Time뿐만 아니라 페인트 지표도 상당히 나쁩니다. 두 번째 예시는 JavaScript가 지연되었다는 점을 제외하면 완전히 동일한 페이지입니다. LCP 이미지가 여전히 큰 타격을 입은 것을 볼 수 있습니다. 세 번째 예시는 페이지 'load 이벤트' 이후에 동일한 스크립트가 실행되고 함수 호출이 더 작은 조각으로 분할되어 있습니다. 이 마지막 예시는 여유 있게 Core Web Vitals를 통과합니다.

null
null
null


기본적으로 페이지 헤드에 있는 외부 JavaScript는 렌더링 트리 생성을 차단합니다. 더 구체적으로 설명하자면, 브라우저가 문서에서 스크립트를 만나면 DOM 구성을 일시 중지하고 JavaScript 런타임에 제어권을 넘겨 스크립트가 실행되도록 한 후 DOM 구성을 계속 진행해야 합니다. 이는 페인트 지표(Largest Contentful Paint 및 First Contentful Paint)에 영향을 미칩니다.

지연된(Deferred) 또는 비동기(async) JavaScript는 여전히 페인트 지표에 영향을 미칠 수 있습니다. 특히 Largest Contentful Paint에 영향을 미칠 수 있는데, DOM이 생성된 후(그리고 이미지와 같은 일반적인 LCP 요소가 다운로드되지 않았을 수 있음) 실행되어 메인 스레드를 차단하기 때문입니다.

외부 JavaScript 파일도 네트워크 리소스를 놓고 경쟁합니다. 외부 JavaScript 파일은 일반적으로 이미지보다 먼저 다운로드됩니다. 너무 많은 스크립트를 다운로드하는 경우 이미지 다운로드가 지연됩니다.

마지막으로 중요한 점은 JavaScript가 사용자 상호 작용을 차단하거나 지연시킬 수 있다는 것입니다. 스크립트가 CPU 리소스를 사용 중일 때(메인 스레드 차단) 브라우저는 해당 스크립트가 완료될 때까지 입력(클릭, 스크롤 등)에 응답하지 않습니다. 이는 Interaction to Next Paint (INP) 점수에 직접적인 영향을 미칩니다.

그 영향력은 측정 가능합니다. 2025 Web Almanac에 따르면 모바일 페이지의 15%만이 render-blocking resources 감사를 통과합니다. 모바일 Total Blocking Time의 중앙값은 1,916밀리초입니다. 이는 브라우저가 사용자 입력에 응답할 수 없는 시간이 거의 2초에 달함을 의미합니다. 각 스크립트에 적합한 지연 방법을 선택하는 것이 이 수치를 낮추는 방법입니다.

JavaScript를 예약하거나 지연시키는 것이 어떻게 Core Web Vitals를 해결할까요?

JavaScript를 예약하거나 지연시키는 것 자체가 Core Web Vitals를 고치는 것은 아닙니다. 상황에 맞는 적절한 도구를 사용하는 것이 핵심입니다. 원칙적으로 스크립트 지연을 최소화하되 다운로드를 위해 대기열에 추가하고 적절한 시기에 실행해야 합니다.

적합한 지연 방법을 선택하는 방법은 무엇일까요?

모든 스크립트가 동일한 것은 아니며 모든 스크립트에는 고유한 기능이 있습니다. 어떤 스크립트는 렌더링 프로세스 초기에 필요하지만, 어떤 스크립트는 그렇지 않습니다.

null

저는 중요도에 따라 JavaScript를 4가지 그룹으로 분류하는 것을 선호합니다.

1. Render critical(렌더링에 중요). 웹페이지의 모양을 변경하는 스크립트입니다. 로드되지 않으면 페이지가 완성된 것처럼 보이지 않습니다. 이러한 스크립트는 무슨 수를 써서라도 피해야 합니다. 어떤 이유로든 피할 수 없다면 지연시켜서는 안 됩니다. 예를 들어 상단 슬라이더나 A/B 테스트 스크립트가 있습니다.
2. Critical(중요). 이러한 스크립트는 웹페이지의 모양을 (너무 많이) 변경하지는 않지만 스크립트가 없으면 페이지가 제대로 작동하지 않습니다. 이러한 스크립트는 지연(defer)되거나 비동기(async) 처리되어야 합니다. 예를 들어 메뉴 스크립트가 있습니다.
3. Important(중요함). 귀하 또는 방문자에게 가치가 있기 때문에 로드하려는 스크립트입니다. 저는 이러한 스크립트를 load 이벤트가 발생한 후에 로드하는 경향이 있습니다. 예를 들어 분석 또는 '맨 위로 가기' 버튼이 있습니다.
4. Nice to have(있으면 좋음). 절대적으로 필요한 경우 없어도 살 수 있는 스크립트입니다. 저는 이러한 스크립트를 가장 낮은 우선순위로 로드하고 브라우저가 유휴 상태일 때만 실행합니다. 예를 들어 채팅 위젯이나 Facebook 버튼이 있습니다.

방법 1: defer 속성 사용

defer 속성이 있는 스크립트는 병렬로 다운로드되며 지연(defer) JavaScript 대기열에 추가됩니다. 브라우저가 DOMContentLoaded 이벤트를 실행하기 직전에 해당 대기열의 모든 스크립트가 문서에 나타나는 순서대로 실행됩니다.

<script defer src='javascript.js'></script>

'defer 트릭'은 보통 많은 문제, 특히 페인트 지표를 해결합니다. 안타깝게도 보장할 수는 없으며 스크립트의 품질에 따라 다릅니다. 지연된 스크립트는 모든 스크립트가 로드되고 HTML이 파싱되면(DOMContentLoaded) 실행됩니다. 그때까지 LCP 요소(보통 큰 이미지)가 로드되지 않았을 수 있으며 지연된 스크립트는 여전히 LCP 지연을 유발합니다.

사용 시기:

가능한 한 빨리 필요한 Critical(중요) 스크립트에 지연(deferred) 스크립트를 사용하세요.

장점:

  1. 지연된 스크립트는 병렬로 다운로드됩니다
  2. 실행 시점에 DOM을 사용할 수 있습니다

단점:

  1. 지연된 스크립트는 LCP 지표를 지연시킬 수 있습니다 
  2. 지연된 스크립트는 일단 실행되면 메인 스레드를 차단합니다
  3. 인라인 또는 비동기(async) 스크립트가 지연된 스크립트에 의존하는 경우 스크립트를 지연시키는 것이 안전하지 않을 수 있습니다

방법 2: async 속성 사용

async 속성이 있는 스크립트는 병렬로 다운로드되며 다운로드가 완료되는 즉시 실행됩니다.

<script async src='javascript.js'></script>

비동기(Async) 스크립트는 페이지 속도 문제를 해결하는 데 거의 도움이 되지 않습니다. 병렬로 다운로드된다는 점은 훌륭하지만 그게 전부입니다. 다운로드가 완료되면 실행되면서 메인 스레드를 차단합니다.

사용 시기:

가능한 한 빨리 필요하고 독립적인(다른 스크립트에 의존하지 않는) Critical(중요) 스크립트에 비동기 스크립트를 사용하세요.

장점:

  1. 비동기 스크립트는 병렬로 다운로드됩니다.
  2. 비동기 스크립트는 가능한 한 빨리 실행됩니다.

단점:

  1. DOMContentLoaded는 비동기 전후에 모두 발생할 수 있습니다.
  2. 스크립트의 실행 순서는 사전에 알 수 없습니다.
  3. 다른 비동기 또는 지연된 스크립트에 의존하는 비동기 스크립트를 사용할 수 없습니다

이 두 가지 접근 방식에 대한 자세한 비교는 defer vs async 및 이것이 Core Web Vitals에 미치는 영향을 참조하세요.

방법 3: 모듈 사용

모듈식 스크립트는 async 속성이 없는 한 기본적으로 지연됩니다. async 속성이 있는 경우 비동기 스크립트처럼 취급됩니다.

<script module src='javascript.js'></script>

모듈은 JavaScript에 대해 생각하는 새로운 방식이며 몇 가지 설계상의 결함을 수정합니다. 그 외에 스크립트 모듈을 사용한다고 해서 웹사이트 속도가 빨라지는 것은 아닙니다. 

사용 시기:

애플리케이션이 모듈식으로 구축된 경우 JavaScript 모듈도 사용하는 것이 합리적입니다.

장점:

  1. 모듈은 기본적으로 지연됩니다
  2. 모듈은 유지 관리가 더 쉽고 모듈식 웹 디자인과 잘 작동합니다
  3. 모듈을 사용하면 동적 가져오기를 통해 특정 시간에 필요한 모듈만 가져오는 쉬운 코드 분할이 가능합니다.

단점:

  1. 모듈 자체는 Core Web Vitals를 개선하지 않습니다
  2. 적시(just-in-time) 또는 즉시 모듈을 가져오면 속도가 느려지고 INP가 악화될 수 있습니다 

방법 4: 페이지 하단에 스크립트 배치

푸터 스크립트는 나중에 다운로드되도록 대기열에 추가됩니다. 이렇게 하면 스크립트 태그 위의 문서에 있는 다른 리소스의 우선순위가 높아집니다.

<html>
   <head></head>
   <body>
      [your page contents here]
      <script defer src='javascript.js'></script>
   </body>
</html>

스크립트를 페이지 하단에 배치하는 것은 흥미로운 기법입니다. 이렇게 하면 다른 리소스(예: 이미지)가 스크립트보다 먼저 예약됩니다. 이렇게 하면 JavaScript 파일 다운로드가 완료되고 스크립트 실행으로 인해 메인 스레드가 차단되기 전에 이러한 리소스를 브라우저에서 사용할 수 있게 되어 화면에 그려질 가능성이 높아집니다. 하지만 ... 보장할 수는 없습니다.

사용 시기:

스크립트 성능이 이미 꽤 좋지만 이미지 및 웹폰트와 같은 다른 리소스의 우선순위를 약간 높이고 싶을 때 사용합니다.

장점:

  1. 스크립트를 페이지 하단에 배치하는 데는 많은 지식이 필요하지 않습니다.
  2. 올바르게 수행하면 이로 인해 페이지가 손상될 위험이 없습니다

단점:

  1. 중요한 스크립트가 나중에 다운로드되고 실행될 수 있습니다
  2. 기본적인 JavaScript 문제를 해결하지 못합니다

방법 5: 스크립트 삽입(Inject)

삽입된 스크립트는 비동기(async) 스크립트처럼 취급됩니다. 병렬로 다운로드되며 다운로드 후 즉시 실행됩니다.

<script>
    const loadScript = (scriptSource) => {
        const script = document.createElement('script');
        script.src = scriptSource;
        document.head.appendChild(script);
    }

    // call the loadscript function that injects 'javascript.js'
    loadScript('javascript.js');
</script>

Core Web Vitals 관점에서 볼 때 이 기법은 <script async>를 사용하는 것과 정확히 동일합니다.

사용 시기:

이 방법은 가능한 한 일찍 트리거되는 서드 파티 스크립트에서 자주 사용됩니다. 함수 호출을 사용하면 코드를 쉽게 캡슐화하고 압축할 수 있습니다.

장점:

  1. 비동기 스크립트를 삽입하는 캡슐화된 코드입니다.

단점:

  1. DOMContentLoaded는 스크립트가 로드되기 전후에 모두 발생할 수 있습니다.
  2. 스크립트의 실행 순서는 사전에 알 수 없습니다.
  3. 다른 비동기 또는 지연된 스크립트에 의존하는 스크립트에는 이 방법을 사용할 수 없습니다

방법 6: 나중에 스크립트 삽입

제 생각에 Nice-to-have(있으면 좋음) 스크립트는 지연된 상태로 로드되어서는 절대 안 됩니다. 가장 적절한 순간에 삽입되어야 합니다. 아래 예시에서 스크립트는 브라우저가 'load' 이벤트를 보낸 후에 실행됩니다.

<script>
window.addEventListener('load', function () {
  // see method 5 for the loadscript function
  loadScript('javascript.js');
});
</script>

이것은 Largest Contentful Paint를 안정적으로 향상시킬 수 있는 첫 번째 기법입니다. 이미지를 포함한 모든 중요한 리소스는 브라우저가 'load 이벤트'를 실행할 때 다운로드됩니다. load 이벤트가 호출되는 데 오랜 시간이 걸릴 수 있으므로 이로 인해 온갖 종류의 문제가 발생할 수 있습니다.

사용 시기:

페인트 지표에 영향을 미칠 이유가 없는 nice-to-have 스크립트에 사용합니다.

장점:

  1. 페이지와 해당 리소스가 로드된 후 스크립트를 삽입하므로 중요한 리소스를 놓고 경쟁하지 않습니다

단점:

  1. Core Web Vitals 측면에서 페이지 설계가 부실할 경우 페이지에서 'load' 이벤트를 보내는 데 시간이 오래 걸릴 수 있습니다
  2. 중요한 스크립트(지연 로딩, 메뉴 등)에 이것을 적용하지 않도록 주의해야 합니다

방법 7: 스크립트 유형 변경(그리고 다시 원상 복구)

페이지 어딘가에서 1. type 속성이 있고 2. type 속성이 "text/javascript"가 아닌 스크립트 태그가 발견되면 해당 스크립트는 브라우저에서 다운로드 및 실행되지 않습니다. 많은 JavaScript 로더(CloudFlare의 RocketLoader 등)가 이 원리에 의존합니다. 이 아이디어는 매우 단순하고 우아합니다.

먼저 모든 스크립트를 다음과 같이 다시 작성합니다.

<script type="some-cool-script-type" src="javascript.js"></script>

그런 다음, 로드 과정 중 어느 시점에 이러한 스크립트가 '일반 JavaScript'로 다시 변환됩니다.

사용 시기:

제가 권장하는 방법은 아닙니다. JavaScript의 영향을 해결하려면 단순히 모든 스크립트를 대기열의 조금 더 아래로 이동하는 것보다 훨씬 더 많은 작업이 필요합니다. 반면, 페이지에서 실행되는 스크립트에 대한 제어 권한이 거의 없거나 JavaScript 지식이 부족한 경우 이것이 최선의 방법일 수 있습니다.

장점:

  1. 간단합니다. RocketLoader 또는 기타 플러그인을 활성화하기만 하면 이제 모든 스크립트가 약간 나중에 실행됩니다.
  2. JS 기반 지연 로딩을 사용하지 않았다면 페인트 지표가 개선될 것입니다.
  3. 인라인 및 외부 스크립트에서 작동합니다.

단점:

  1. 스크립트 실행 시점에 대해 세밀하게 제어할 수 없습니다
  2. 잘못 작성된 스크립트는 손상될 수 있습니다
  3. JavaScript를 해결하기 위해 JavaScript를 사용합니다
  4. 실행 시간이 긴 스크립트를 수정하는 데는 아무런 역할을 하지 않습니다

방법 8: Intersection Observer 사용

Intersection Observer를 사용하면 요소가 스크롤되어 표시되는 뷰포트 내로 들어올 때 함수(이 경우에는 외부 JavaScript를 로드함)를 실행할 수 있습니다.

<script>
const handleIntersection = (entries, observer) => {
    if (entries?.[0].isIntersecting) {
        // load your script or execute another
           function like trigger a lazy loaded element
        loadScript('javascript.js');

        // remove the observer
        observer.unobserve(entries?.[0].target);
    }
};
const Observer = new window.IntersectionObserver()
Observer.observe(document.querySelector('footer'));
</script>

이것은 현재 존재하는 JavaScript 지연 방법 중 단연코 가장 효과적인 방법입니다. 필요한 스크립트만, 필요하기 직전에 로드합니다. 안타깝게도 현실은 이렇게 깔끔하지 않으며, 뷰포트로 스크롤되는 요소에 결합할 수 있는 스크립트는 많지 않습니다.

사용 시기:

이 기법을 최대한 활용하세요! 스크립트가 화면 밖 구성 요소(지도, 슬라이더, 양식 등)와만 상호 작용할 때마다 이 스크립트를 삽입하는 가장 좋은 방법입니다.

장점:

  1. Core Web Vitals LCP 및 FCP를 방해하지 않습니다
  2. 사용되지 않는 스크립트를 삽입하지 않습니다. 이로 인해 INP가 개선됩니다

단점:

  1. 표시되는 뷰포트에 있을 수 있는 구성 요소에는 사용해서는 안 됩니다
  2. 기본적인 <script src="...">보다 유지 관리가 어렵습니다
  3. 레이아웃 이동(layout shift)을 유발할 수 있습니다

방법 9: readystatechange 사용

document.readystate는 'DOMContentloaded' 및 'load' 이벤트의 대안으로 사용할 수 있습니다. 'interactive' readystate는 DOM을 변경하거나 이벤트 핸들러 추가해야 하는 중요한 스크립트를 호출하기에 좋은 위치입니다. 
'complete' ready state는 덜 중요한 스크립트를 호출하기에 좋은 위치입니다.

document.addEventListener('readystatechange', (event) => {
  if (event.target.readyState === 'interactive') {
    initLoader();
  } else if (event.target.readyState === 'complete') {
    initApp();
  }
});

방법 10: 시간 초과 없이 setTimeout 사용

setTimeout은 페이지 속도 커뮤니티에서 눈살을 찌푸리게 만들면서도 심하게 과소평가된 방법입니다. setTimeout은 자주 오용되기 때문에 평판이 좋지 않습니다.  많은 개발자들은 setTimeout이 스크립트 실행을 설정된 밀리초만큼 지연시키는 데만 사용할 수 있다고 생각합니다. 이것은 사실이지만 setTimeout은 실제로는 훨씬 더 흥미로운 일을 합니다. 브라우저의 이벤트 루프 끝에 새로운 작업을 생성합니다. 이 동작을 사용하여 작업을 효과적으로 예약할 수 있습니다. 긴 작업을 여러 개의 작은 작업으로 나누는 데 사용할 수도 있습니다.

<script>
   setTimeout(() => {
       // load a script or execute another function
       console.log('- I am called from a 0ms timeOut()')
    }, 0);

   console.log('- I was last in line but executed first')
/* Output: - I was last in line but executed first - I am called from a 0ms timeOut() */ </script>

사용 시기:

setTimeout은 브라우저의 이벤트 루프에 새로운 작업을 생성합니다. 순차적으로 실행되는 많은 함수 호출에 의해 메인 스레드가 차단될 때 이것을 사용하세요.

장점:

  1. 오래 실행되는 코드를 더 작은 조각으로 나눌 수 있습니다.

단점:

  1. setTimeout은 다소 투박한 방법이며 중요한 스크립트에 대한 우선순위를 제공하지 않습니다.
  2. 루프의 끝에 실행될 코드를 추가합니다

방법 11: 시간 초과가 있는 setTimeout 사용

0ms가 넘는 시간 초과로 setTimeout을 호출하면 상황이 훨씬 더 흥미로워집니다.

<script>
   setTimeout(() => {
       // load a script or execute another function
       console.log('- I am called from a 10ms timeOut()')
    }, 10);

   setTimeout(() => {
       // load a script or execute another function
       console.log('- I am called from a 0ms timeOut()')
    }, 0);

   console.log('- I was last in line but executed first')
/* Output: - I was last in line but executed first - I am called from a 0ms timeOut() - I am called from a 10ms timeOut() */ </script>

사용 시기:

하나의 스크립트를 다른 스크립트 다음에 예약하는 쉬운 방법이 필요할 때 약간의 시간 초과 설정이 효과적입니다.

장점:

  1. 모든 브라우저에서 지원됨

단점:

  1. 고급 예약을 제공하지 않음

방법 12: Promise를 사용하여 마이크로태스크 설정

마이크로태스크(micro-task)를 생성하는 것 또한 JavaScript를 예약하는 흥미로운 방법입니다. 마이크로태스크는 현재 실행 루프가 완료된 직후 실행되도록 예약됩니다.

<script>
   const myPromise = new Promise((resolve, reject) => {
      resolve();
   }).then(
      () => {
         console.log('- I was scheduled after a promise')
         }
   );
console.log('- I was last in line but executed first') /* Output: - I was last in line but executed first - I was scheduled after a promise */ </script>

사용 시기:

다른 작업 직후에 작업을 예약해야 할 때. 

장점:

  1. 마이크로태스크는 작업 실행이 완료된 직후에 예약됩니다.
  2. 마이크로태스크를 사용하여 동일한 이벤트 내에서 덜 중요한 JavaScript 코드를 지연시킬 수 있습니다. 

단점:

  1. 메인 스레드를 더 작은 부분으로 분할하지 않습니다. 브라우저가 사용자 입력에 응답할 기회가 주어지지 않습니다.
  2. 자신이 무엇을 하고 있는지 정확히 알지 못하는 이상 Core Web Vitals를 향상시키기 위해 마이크로태스크를 사용할 필요는 없을 것입니다.

방법 13: 마이크로태스크 사용

queueMicrotask()를 사용하여 동일한 결과를 얻을 수 있습니다. Promise보다 queueMicrotask()를 사용하는 것의 장점은 약간 더 빠르고 Promise를 처리할 필요가 없다는 것입니다.

<script>
   queueMicrotask(() => {
      console.log('- I am a microtask')
   })

   console.log('- I was last in line but executed first')

   /*
      Output:
      - I was last in line but executed first
      - I am a microtask
   */
</script>


방법 14: requestIdleCallback 사용

window.requestIdleCallback() 메서는 브라우저의 유휴(idle) 기간 동안 호출될 함수를 대기열에 추가합니다. 이를 통해 개발자는 애니메이션 및 입력 응답과 같이 지연 시간에 민감한 이벤트에 영향을 주지 않고 메인 이벤트 루프에서 백그라운드 및 우선순위가 낮은 작업을 수행할 수 있습니다. 함수는 일반적으로 선입선출(first-in-first-out) 순서로 호출되지만, 시간 초과가 지정된 콜백은 시간 초과가 경과하기 전에 실행하기 위해 필요한 경우 순서를 벗어나 호출될 수 있습니다.

<script>
requestIdleCallback(() => {
    const script = document.createElement('script');
    script.src = 'javascript.js';
    document.head.appendChild(script);
});
</script>

사용 시기:

Nice to have(있으면 좋은) 스크립트 또는 사용자 입력 후 중요하지 않은 작업을 처리할 때 이것을 사용합니다.

장점:

  1. 사용자에게 미치는 영향을 최소화하면서 JavaScript 실행
  2. INP를 개선할 가능성이 높음

단점:

  1. 코드가 실행된다는 보장이 없음 

방법 15: postTask 사용

scheduler.postTask() 메서드를 사용하면 작업이 실행되기 전 최소 지연 시간, 작업의 우선순위, 그리고 작업 우선순위를 수정하거나 작업을 중단하는 데 사용할 수 있는 신호(signal)를 선택적으로 지정할 수 있습니다. 이것은 작업 콜백 함수의 결과와 함께 이행(resolve)되거나 중단 이유 또는 작업에서 발생한 오류와 함께 거부(reject)되는 Promise를 반환합니다.

<script>
scheduler.postTask(() => {
    const script = document.createElement('script');
    script.src = 'javascript.js';
    document.head.appendChild(script);
}, { priority: 'background' });
</script>

사용 시기:

우선순위에 대한 세밀한 제어가 필요할 때 postTask는 스크립트를 예약하기에 적합한 API입니다.

장점:

  1. JavaScript 실행 예약에 대한 완벽한 제어!

단점:

  1. Safari에서는 지원되지 않습니다. Chrome 94+, Edge 94+ 및 Firefox 142+에서 지원됩니다. 전체 적용 범위를 위해서는 기능 감지(feature detection)와 setTimeout fallback을 사용하세요.

방법 16: scheduler.yield() 사용

scheduler.yield()는 긴 작업을 분할하는 가장 새로운 방법입니다. 새 작업에서 해결되는 Promise를 반환하여, 작업 덩어리(chunks) 사이에 브라우저가 사용자 입력에 응답할 수 있는 기회를 제공합니다. setTimeout과 달리, 이어지는 작업은 대기열에 있는 다른 작업보다 우선순위를 갖기 때문에, 코드가 대기열 뒤로 밀리지 않고 중단된 부분부터 다시 시작됩니다.

<script>
async function processItems(items) {
    for (const item of items) {
        doWork(item);
        await scheduler.yield();
    }
}
</script>

이것은 INP를 개선하는 데 있어 단연 최고의 도구입니다. 메인 스레드를 수백 밀리초 동안 차단하는 긴 작업을 더 작은 조각으로 분할할 수 있으며, 각 조각은 yield 포인트로 분리됩니다. 브라우저는 모든 yield 포인트에서 사용자 입력을 처리할 수 있습니다. 이 패턴에 대한 실용적인 연습은 메인 스레드에 양보(yield)하는 방법을 참조하세요.

Safari는 아직 scheduler.yield()를 지원하지 않으므로 항상 fallback을 포함하세요.

<script>
function yieldToMain() {
    if (globalThis.scheduler?.yield) {
        return scheduler.yield();
    }
    return new Promise(resolve => {
        setTimeout(resolve, 0);
    });
}

async function processItems(items) {
    for (const item of items) {
        doWork(item);
        await yieldToMain();
    }
}
</script>

사용 시기:

메인 스레드를 차단하는 장기 실행 JavaScript가 있을 때마다 사용하세요. 상호 작용 핸들러 및 루프에서 데이터를 처리하는 모든 코드에서 INP를 개선하기 위해 권장되는 접근 방식입니다.

장점:

  1. 대기열에서 위치를 잃지 않고 긴 작업을 분할합니다
  2. (맨 뒤로 가는 setTimeout과 달리) 대기 중인 다른 작업보다 먼저 이어지는 작업이 실행됩니다
  3. 브라우저가 사용자 입력에 응답할 기회를 제공함으로써 직접적으로 INP를 향상시킵니다

단점:

  1. Safari에서는 지원되지 않습니다. Chrome 129+, Edge 129+ 및 Firefox 142+에서 지원됩니다.
  2. 전체 브라우저 적용 범위를 위해 fallback이 필요합니다(setTimeout이 폴리필(polyfill)로 작동함)

이러한 기법을 적용한 후 Real User Monitoring을 통해 개선 사항을 확인하세요. Lighthouse 점수는 시작점일 뿐이며, Google은 순위를 매기는 데 실제 사용자의 필드 데이터를 사용합니다. CoreDash는 실제 방문자의 INP 및 모든 Core Web Vitals를 추적하므로 지연 전략이 프로덕션 환경에서 실제로 작동하는지 확인할 수 있습니다.

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 무료 체험
JavaScript를 지연시키거나 예약하는 16가지 방법Core Web Vitals JavaScript를 지연시키거나 예약하는 16가지 방법