INP 처리 시간: 원인, 최적화 및 코드 예제

처리 시간으로 인해 발생하는 INP 문제를 찾고 개선하는 방법을 알아보세요

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

처리 시간으로 인해 발생하는 Interaction to Next Paint (INP) 문제

이 페이지는 Interaction to Next Paint (INP) 시리즈의 일부입니다. INP는 사용자 상호 작용부터 다음 시각적 업데이트까지의 총 시간을 측정합니다. 처리 시간은 INP를 구성하는 세 단계 중 두 번째 단계이며, 입력 지연 이후 및 표시 지연 이전에 발생합니다. INP가 처음이라면 먼저 INP 문제를 식별하고 수정하는 방법에 대한 가이드를 읽어보세요.

요약: Interaction to Next Paint (INP)는 사용자가 페이지와 상호 작용한 후 시각적 변화를 확인하는 데 걸리는 시간을 측정합니다. 이 INP는 "입력 지연", "처리 시간", "표시 지연"의 3가지 구성 요소로 나눌 수 있습니다.

처리 시간은 평균적으로 지연의 약 40%를 차지하며 전체 INP의 주요 원인입니다. 이벤트 핸들러를 최적화하는 것은 처리 시간을 줄이고 INP 점수를 향상시키는 가장 직접적인 방법입니다.

INP 팁: 레이아웃 업데이트보다 앞서는 중요한 코드를 즉시 실행하고 다른 모든 코드는 그 이후에 실행되도록 예약함으로써 처리 시간을 최적화할 수 있습니다.

처리 시간이란 무엇인가요?

Interaction to Next Paint (INP)는 "입력 지연", "처리 시간" 및 "표시 지연"의 3가지 하위 부분으로 나눌 수 있습니다.

처리 시간은 사용자가 웹 페이지와 상호 작용한 후(예: 버튼 클릭 또는 키 누름) 브라우저가 관련된 모든 이벤트 콜백을 실행하는 데 걸리는 시간을 나타냅니다. 처리 시간은 항상 존재하지만, 이벤트 콜백이 너무 많은 처리 시간을 차지할 때 INP 문제가 발생합니다.

간단히 말해, 처리 시간은 상호 작용에 대한 응답으로 발생하는 "작업"의 지속 시간입니다. 검색 버튼을 클릭할 때 처리 시간은 쿼리 유효성 검사, API 요청 준비, 로컬 상태 업데이트, 분석 이벤트 트리거 및 해당 클릭 이벤트에 연결된 기타 모든 코드를 포함합니다. 이러한 이벤트 핸들러의 모든 JavaScript 코드 라인이 처리 시간에 영향을 미칩니다.

처리 시간과 INP

Interaction to Next Paint 최적화를 생각할 때 가장 먼저 떠오르는 것이 처리 시간일 수 있습니다. 이는 브라우저가 레이아웃을 업데이트하기 전에 "수행해야 할 작업"입니다.

많은 개발자가 콜백 함수를 최적화(처리 시간 최적화)하는 관점에서 INP 개선을 생각하며, 이는 맞는 말입니다. 하지만 중요도 측면에서 볼 때, 처리 시간은 개선해야 할 가장 중요한 단일 요소는 아닙니다. 그럼에도 평균적으로 전체 INP 시간의 약 40%를 차지합니다.

CoreDash에서는 매시간 수백만 개의 Core Web Vitals 데이터 포인트를 수집합니다. 해당 데이터를 기반으로 처리 시간은 Interaction to Next Paint의 40%를 차지합니다. 40%는 큰 비중이지만 처리 시간만 최적화한다고 해서 INP 문제가 해결되는 것은 아닙니다. 입력 지연(18%)과 표시 지연(42%)도 살펴보아야 합니다.

처리 시간 예시: 사용자가 양식을 제출하기 위해 버튼을 클릭할 때 양식 데이터의 유효성을 검사하고, 이를 서버로 보내고, 응답을 처리하는 코드는 모두 처리 시간에 영향을 줍니다. 이러한 작업에 소요되는 시간이 길어질수록 처리 시간이 길어지고, 잠재적으로 INP 점수가 악화될 수 있습니다.

높은 처리 시간의 원인은 무엇인가요?

높은 처리 시간의 일반적인 4가지 원인으로는 불필요한 코드, 최적화되지 않은 코드, 클러스터링된 콜백, 레이아웃 스래싱이 있습니다.

  1. 불필요한 코드. 오래되고 사용되지 않는 코드나 사용자 상호 작용과 직접적인 관련이 없는 코드는 콜백 실행 시간을 연장할 수 있습니다. 여기에는 다음 페인트 이전에 완료할 필요가 없는 분석 호출, 로깅, 상태 동기화가 포함됩니다.
  2. 최적화되지 않은 코드. 비효율적인 코드(주로 루프 또는 비효율적인 DOM 조회)는 이벤트 콜백을 필요 이상으로 느리게 실행되게 할 수 있습니다. 루프 전에 결과를 캐싱하는 대신 루프 내부에서 document.querySelectorAll()을 사용하여 DOM을 쿼리하는 것이 일반적인 예시입니다.
  3. 클러스터링된 콜백. 여러 이벤트 콜백이 촘촘하게 예약되면 대기열이 생성됩니다. 사용자 상호 작용으로 인해 트리거된 콜백이 이 대기열에 갇히면 응답이 지연된 것처럼 보입니다. 예를 들어, 단일 클릭에 대해 pointerdown 핸들러, mousedown 핸들러, click 핸들러가 순차적으로 실행될 수 있습니다.
  4. 레이아웃 스래싱. 레이아웃 재계산을 유발하는 빈번한 DOM 조작은 브라우저에 부담을 주어 성능 저하를 초래할 수 있습니다. 이 현상은 코드의 루프 내에서 레이아웃 속성을 읽고 쓰는 작업이 번갈아 발생하여 브라우저가 여러 번 레이아웃을 다시 계산하도록 강제할 때 일어납니다.

처리 시간 최소화

긴 처리 시간으로 인한 INP를 개선하려면, 후속 레이아웃 업데이트를 담당하는 코드가 최대한 빨리 실행되도록 만들어야 합니다.

전략은 두 가지입니다. 기존 코드를 최적화(불필요한 코드 제거 및 현재 코드 최적화)하고, 레이아웃 업데이트 전후에 실행할 코드를 구분하는 것입니다. 레이아웃 업데이트에 필수적인 코드를 먼저 실행하고 다른 모든 코드는 레이아웃 업데이트 이후에 실행하도록 할 수 있습니다.

  1. 사용하지 않는 코드 제거. 사용하지 않는 코드를 제거하는 것은 쉬운 결정처럼 보일 수 있지만, 대부분의 사이트에는 페이지나 UX에 아무런 기여도 하지 않으면서 실행만 되는 오래되고 사용되지 않는 코드가 최소한 일부 존재합니다. 가장 먼저 해야 할 일은 불필요하게 실행되는 코드가 없는지 확인하는 것입니다. 이는 트리 쉐이킹, 코드 스플리팅, Chrome의 코드 커버리지 검사, 사용하지 않는 코드를 알려주는 훌륭한 IDE 사용을 통해 수행할 수 있습니다. (팁: Tag Manager가 로드하는 리소스도 비판적으로 살펴보세요.) 더 많은 전략을 확인하려면 JavaScript를 지연시키는 14가지 방법 가이드를 참조하세요.
  2. 콜백 실행 시간 최소화. JavaScript 프로파일러를 사용하여 코드의 병목 현상을 파악하고 해당 영역을 최적화의 대상으로 삼으세요. 중복 계산을 피하기 위해 메모이제이션, 사전 계산, 캐싱과 같은 기술을 고려해보세요. (팁: Chrome 성능 패널을 사용하여 실행 시간이 긴 스크립트를 찾을 수 있습니다.)
  3. 중요한 코드를 우선시하고 다른 코드를 예약하기. 콜백 코드가 최적화되면 즉시 실행해야 할 코드와 연기할 수 있는 코드로 분류하세요. 다음 실제 예시를 살펴보세요.

    이 예시에서는 레이아웃 업데이트 선행 코드(React)보다 Google Tag Manager 및 Facebook 이벤트 콜백이 먼저 실행됩니다. 해결책은 브라우저가 유휴 상태일 때 GTM 및 Facebook 콜백이 실행되도록 예약하는 것입니다.
  4. 레이아웃 스래싱 또는 리플로우 피하기. 레이아웃 스래싱은 루프 내에서 스타일 업데이트와 스타일 읽기가 섞여 브라우저가 레이아웃을 수없이 재계산하게 될 때 발생합니다. 레이아웃 스래싱을 피하려면 스타일 값 요청("get")을 수행하기 전에 모든 스타일 변경("set")을 먼저 수행하세요. 이 접근 방식은 레이아웃 업데이트 빈도를 최소화하여 페이지를 더 빠르게 만듭니다. 예를 들어 각 단락의 너비를 특정 요소의 너비와 일치하도록 설정하는 루프의 경우, 루프 이전에 해당 요소의 너비를 한 번 읽고 그 값을 사용하여 루프 내에서 단락의 너비를 업데이트하세요.

setTimeout(0)으로 이벤트 핸들러 분할하기

이벤트 핸들러에서 코드를 제거하거나 연기할 수 없는 경우 다음으로 좋은 옵션은 핸들러를 더 작은 덩어리로 분할하는 것입니다. setTimeout(callback, 0) 패턴을 사용하면 작업을 여러 태스크로 분할하여 브라우저가 그 사이사이 레이아웃 업데이트를 처리할 기회를 제공할 수 있습니다. 실용적인 예시는 다음과 같습니다.

// 변경 전: 길게 이어진 단일 이벤트 핸들러가 다음 페인트를 차단합니다.
button.addEventListener('click', () => {
  updateUI();           // 중요: 페인트 이전에 실행되어야 함
  validateForm();       // 중요하지만 대기 가능
  sendAnalytics();      // 중요하지 않음
  syncLocalStorage();   // 중요하지 않음
});

// 변경 후: 중요 작업과 연기된 작업으로 분할
button.addEventListener('click', () => {
  updateUI();  // 중요: 페인트 직전에 즉시 실행

  setTimeout(() => {
    validateForm();
  }, 0);

  setTimeout(() => {
    sendAnalytics();
    syncLocalStorage();
  }, 0);
});

setTimeout(0)의 단점은 계속될 작업을 태스크 대기열의 맨 끝에 배치한다는 것입니다. 다른 작업이 이미 대기열에 있는 경우 연기된 코드가 즉시 실행되지 않을 수 있습니다. 더 예측 가능한 동작을 위해서는 scheduler.yield()를 대신 사용하세요(아래 섹션 참조). 특히 JavaScript 스크롤 처리를 타겟으로 하는 패턴은 전용 가이드를 참조하세요.

시각적 업데이트에 requestAnimationFrame 사용하기

이벤트 핸들러가 시각적 업데이트를 트리거해야 하는 경우 requestAnimationFrame()은 코드가 브라우저가 다음 리페인트를 수행하기 직전이라는 최적의 시간에 실행되도록 보장합니다. 이는 레이아웃 스래싱을 피하기 위해 DOM 읽기 및 쓰기를 일괄 처리해야 할 때 특히 유용합니다.

// requestAnimationFrame을 사용하여 시각적 업데이트 일괄 처리
button.addEventListener('click', () => {
  // 레이아웃 속성을 먼저 읽습니다 (rAF 외부)
  const containerWidth = container.offsetWidth;

  requestAnimationFrame(() => {
    // rAF 내부에서 레이아웃 속성을 씁니다
    items.forEach(item => {
      item.style.width = containerWidth + 'px';
    });
  });

  // 유휴 시간에 대한 비시각적 작업을 예약합니다
  requestIdleCallback(() => {
    trackButtonClick();
    updateSessionState();
  });
});

이 패턴은 DOM 읽기를 DOM 쓰기와 분리하여 강제 동기식 레이아웃을 방지합니다. 시각적 업데이트는 브라우저의 렌더링 파이프라인에서 이상적인 시간에 실행되며 비시각적 작업은 유휴 시간에 실행됩니다.

중요한 코드의 우선순위를 정하는 방법

"중요한 코드를 우선시하고 다른 코드를 예약하기"라는 말이 추상적으로 들릴 수 있으므로 실제로는 어떻게 적용되는지 소개합니다. requestIdleCallback()을 사용하고 메인 스레드에 yield 함으로써 중요한 코드의 우선순위를 정할 수 있습니다.

즉시 실행할 필요가 없는 덜 중요한 작업에는 requestIdleCallback을 사용합니다. 다음은 GTM 이벤트를 예약하기 전후의 예시입니다.

/* 변경 전: 코드를 즉시 실행 */
gtag('event', '<event_name>', {
   'event_category': '<event_category>',
 });

/* 변경 후: 브라우저 유휴 시간 동안 동일한 코드를 실행 */
requestIdleCallback(() => {
  gtag('event', '<event_name>', {
    'event_category': '<event_category>',
  });
}, { timeout: 1000 });

requestIdleCallback의 단점은 코드가 원하는 만큼 빨리 실행되지 않을 수 있다는 것입니다. 이 경우, 가장 중요한 코드가 실행된 후 브라우저에 레이아웃 업데이트를 수행할 수 있는 시간을 잠시 부여하여 "메인 스레드에 yield"할 수 있습니다. 다음은 메인 스레드에 yielding 하여 작업을 분할하는 방법의 예시입니다.

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

async function handleClick() {
  // 이곳에서 가장 중요한 레이아웃 업데이트를 수행합니다
  await yieldToMain();
  // 레이아웃 업데이트 후 가능한 한 빨리 실행해야 하는 다른 작업을 수행합니다
}

scheduler.postTask()를 사용한 세밀한 스케줄링

scheduler.postTask() 함수는 우선순위를 설정하여 작업을 더 세밀하게 예약할 수 있도록 하며, 이는 브라우저가 작업의 우선순위를 지정하여 우선순위가 낮은 작업이 메인 스레드에 yield 하도록 돕습니다. 프로덕션 환경에서 이 API를 사용하기 전에 브라우저 지원 테이블을 확인하세요.

postTask() 함수는 3가지 우선순위 설정을 허용합니다. 우선순위가 가장 낮은 작업에는 "background", 중간 우선순위 작업에는 "user-visible", 높은 우선순위가 요구되는 중요한 작업에는 "user-blocking"을 사용합니다.

이벤트 핸들러 내의 각 작업에 올바른 우선순위를 할당하면 브라우저가 필요한 모든 작업을 완료하는 동시에 사용자 상호 작용을 반응성 있게 처리하도록 할 수 있습니다.

// 중요한 UI 작업을 높은 우선순위로 예약
scheduler.postTask(() => {
  updateCartBadge();
  showConfirmation();
}, { priority: 'user-blocking' });

// 데이터 동기화를 중간 우선순위로 예약
scheduler.postTask(() => {
  syncCartWithServer();
}, { priority: 'user-visible' });

// 분석을 낮은 우선순위로 예약
scheduler.postTask(() => {
  gtag('event', 'add_to_cart', { item: productId });
  fbq('track', 'AddToCart');
}, { priority: 'background' });

실제 적용 방법

WordPress 및 React/Next.js에서 처리 시간 최적화에 접근하는 방법은 다음과 같습니다.

WordPress

WordPress는 서드파티 스크립트와 관련하여 제한적인 제어를 제공합니다. 많은 스크립트가 플러그인을 통해 추가됩니다. 대부분의 경우 이러한 스크립트는 Interaction to Next Paint (INP)를 지연시키는 것 외에는 아무것도 수행하지 않는 이벤트 리스너를 페이지에 추가합니다. WordPress 사이트에서 긴 처리 시간으로 인한 INP 문제가 발생하는 경우 다음 단계를 따르세요.

  • 테마 설정을 확인합니다. "부드러운 스크롤"이나 "애니메이션 메뉴"와 같은 불필요한 옵션을 선택 취소합니다. 이러한 설정은 INP 문제를 일으키는 경향이 있습니다.
  • 어떤 스크립트가 긴 처리 시간의 원인인지 확인합니다(팁: Chrome 성능 패널 사용). 이러한 스크립트가 플러그인과 관련되어 있다면, 더 적은 JavaScript로 비슷한 기능을 수행하는 다른 플러그인을 찾는 것을 고려해보세요.
  • 페이지에서 사용자 지정 스크립트가 실행되는 경우가 많습니다. 해당 스크립트를 확인하여 메인 스레드에 자주 yield 하도록 하고 덜 중요한 콜백은 requestIdleCallback 함수로 감싸세요.
  • 페이지 단위로 사용하지 않는 스크립트를 언로드합니다(팁: wp_deregister_script 사용). 일부 플러그인은 기능이 필요하지 않은 경우에도 모든 페이지에 스크립트를 주입하는 경향이 있습니다.
  • Tag Manager를 확인하고 사용하지 않거나 불필요한 태그를 제거합니다.
  • 가볍고 깔끔한 테마를 사용하세요. "모든 것을 할 수 있는" 다목적 테마는 스크립트가 더 많고 무거운 이벤트 핸들러를 갖는 경향이 있습니다.
  • 페이지 빌더는 최종 사용자에게 페이지를 표시할 때 JavaScript에 크게 의존하는 경우가 많으므로 피하세요.

React / Next.js

React의 훅과 동시성 기능을 사용하면 INP 처리 시간을 크게 줄일 수 있습니다. 핵심적인 기술은 다음과 같습니다.

React 동시성 기능으로 사용자 상호 작용을 우선시하기:

React 18에는 특히 입력 중에 보다 부드러운 UX를 위해 렌더링을 최적화하는 동시성 기능이 도입되었습니다.

  • useTransitionstartTransition: 나중에 렌더링할 중요하지 않은 업데이트를 표시합니다. 이렇게 하면 대규모 업데이트가 사용자 상호 작용을 차단하는 것을 방지할 수 있습니다. 예를 들어, 검색 창의 입력이 반응성을 유지하도록 검색 결과 업데이트를 startTransition으로 감쌉니다.
  • useDeferredValue: UI를 필수적인 부분과 덜 중요한 부분으로 나눕니다. React는 더 반응적인 경험을 위해 덜 중요한 부분의 렌더링을 중단할 수 있습니다. 이는 필터링된 목록이나 검색 결과를 렌더링하는 데 이상적입니다.
  • useOptimistic (React 19+): 네트워크 요청과 같은 비동기 작업이 진행되는 동안 임시적인 낙관적 상태를 보여줍니다. 이를 통해 데이터를 가져오는 중에도 UI가 반응성을 유지합니다.

데이터 페칭을 위한 Suspense (React 18+)

React 18의 Suspense는 브라우저가 사용자 상호 작용의 우선순위를 지정하고 렌더링을 최적화할 수 있도록 하여 INP를 개선하는 데 사용될 수 있습니다. React 16에서는 코드 스플리팅을 위해 Suspense가 도입되었지만, React 18에서는 데이터 페칭을 포함하도록 이 기능이 확장되었습니다.

  • 데이터가 로드되는 동안 로딩 표시기와 같은 fallback 컴포넌트가 표시됩니다.
  • 데이터가 도착하면 React는 일시 중단된 컴포넌트의 렌더링을 재개합니다.
  • Suspense는 동시성 React의 중단 가능한 렌더링과 결합되어 사용자 상호 작용을 우선시합니다. 사용자가 일시 중단된 컴포넌트와 상호 작용하는 경우 React는 해당 컴포넌트의 렌더링 우선순위를 높여 반응성을 유지합니다.

이러한 기능은 React가 백그라운드 렌더링 작업보다 사용자 상호 작용의 우선순위를 높이는 데 도움을 줍니다.

다른 INP 단계 살펴보기

처리 시간은 Interaction to Next Paint의 한 부분일 뿐입니다. INP 점수를 완전히 최적화하려면 다음 두 가지 단계도 해결해야 합니다.

  • 입력 지연: 이벤트 핸들러가 실행되기 전의 대기 시간을 최소화합니다. 입력 지연은 총 INP 시간의 약 18%를 차지합니다.
  • 표시 지연: 총 INP 시간의 약 42%를 차지하는 렌더링 및 페인팅 작업을 줄입니다.

전체 진단 워크플로우를 보려면 INP 문제를 찾고 수정하는 방법에 대한 가이드를 참조하고, 전체 개요를 보려면 INP 허브 페이지로 돌아가세요.

관리 안 하는 순간 퍼포먼스는 무너집니다.

모니터링, 퍼포먼스 버짓, 프로세스까지 세팅합니다. 일회성 수정과 진짜 해결의 차이가 바로 거기서 갈립니다.

한번 얘기해봐요
INP 처리 시간: 원인, 최적화 및 코드 예제Core Web Vitals INP 처리 시간: 원인, 최적화 및 코드 예제