INP(Interaction to Next Paint)는 2024년 3월 12일 FID를 대체한 핵심 웹 바이탈로, 클릭·탭·키 입력 같은 모든 상호작용이 화면에 반영되기까지 걸리는 시간을 측정합니다. 표준 기준으로 75번째 백분위에서 200밀리초 이하면 ‘좋음’입니다. 메인스레드를 무거운 작업으로 막지 않는 것이 핵심이며, Findable은 긴 작업 쪼개기·디바운스로 클릭이 ‘멈칫’하지 않게 만듭니다.
요약
- INP는 2024.03.12 FID를 대체한 ‘반응성’ 핵심 웹 바이탈이다 — 첫 입력만 보던 FID와 달리 모든 상호작용을 본다.
- 좋은 INP는 75번째 백분위에서 200ms 이하(표준 기준). 즉 사용자 4명 중 3명이 0.2초 안에 반응을 받아야 한다.
- 클릭이 ‘멈칫’하는 주범은 메인스레드를 길게 붙잡는 무거운 자바스크립트다.
- 해법은 긴 작업 쪼개기(yield), 불필요한 계산 미루기, 입력 디바운스다.
예전에 한 고객사 사이트에서 ‘버튼이 가끔 안 눌린다’는 제보가 들어왔습니다. 코드를 봐도 버튼은 멀쩡했죠. 진짜 원인은 버튼이 아니라, 그 옆에서 돌던 무거운 계산이었습니다. 사용자가 누른 순간 브라우저가 그 계산을 처리하느라 화면을 못 그렸던 겁니다. 사용자에겐 ‘안 눌린 것’처럼 느껴졌고요. 그 ‘멈칫’의 정체를 숫자로 보여주는 지표가 바로 INP입니다.
그래서 INP가 뭔가요? FID와 뭐가 다르죠?
둘 다 ‘반응성’을 보는 지표입니다. 차이는 보는 범위입니다. 옛 지표 FID는 ‘첫 입력이 처리되기 시작할 때까지의 지연’만 쟀습니다. 처리에 걸리는 시간도, 화면에 그려지는 시간도 보지 않았죠. 그래서 ‘반응이 빠른 것처럼’ 보이는 착시가 있었습니다. INP는 페이지에서 일어난 모든 상호작용을, 입력 지연 + 처리 시간 + 다음 화면을 그리는 시간까지 통째로 봅니다. 그리고 대표값 하나를 내놓습니다. 더 정직한 지표입니다.
왜 2024년에 바뀐 건가요?
구글은 2024년 3월 12일 INP를 핵심 웹 바이탈로 정식 채택하고 FID를 대체했습니다(FID는 같은 해 9월 프로그램에서 제거). 이유는 단순합니다. 요즘 웹은 첫 클릭보다 ‘쓰는 내내’의 반응성이 더 중요하기 때문입니다. 메뉴 열기, 탭 전환, 필터 적용처럼 매 순간의 클릭이 빨라야 사람이 ‘쾌적하다’고 느낍니다. INP는 바로 그 ‘쓰는 내내’를 봅니다.
말로는 모릅니다 — 직접 눌러보세요
아래 두 버튼은 똑같이 생겼지만 속이 다릅니다. ‘가벼운 버튼’은 메인스레드를 거의 쓰지 않고, ‘무거운 버튼’은 누르는 순간 메인스레드를 잠깐 꽉 막습니다. 번갈아 눌러보고, 반응 시간 숫자를 비교해 보세요.
두 버튼을 눌러 반응 속도를 비교
메인스레드를 막으면 클릭이 '멈칫'합니다. 핵심 지표 INP를 직접 느껴보세요.
차이가 느껴지시나요? ‘무거운 버튼’을 누르면 눌린 모양이 한 박자 늦게 따라옵니다. 이게 INP가 나쁜 상태의 감각입니다. 실제 사이트에서는 이 ‘무거운 작업’이 광고 스크립트, 거대한 목록 렌더링, 잘못 짠 스크롤 핸들러 같은 모습으로 숨어 있습니다.
그럼 ‘좋은 INP’는 얼마인가요?
표준 기준으로는 실사용자 데이터의 75번째 백분위에서 200밀리초 이하면 ‘좋음’입니다. 200~500ms는 ‘개선 필요’, 500ms 초과는 ‘나쁨’입니다. 풀어 말하면, 사용자 4명 중 적어도 3명이 클릭 후 0.2초 안에 화면 반응을 받아야 한다는 뜻입니다. 0.2초는 사람이 ‘즉시’라고 느끼는 경계선에 가깝습니다. 그 선을 넘기면 ‘느린 사이트’라는 인상이 남습니다. (임계값은 표준 기준이며 기기·네트워크 환경에 따라 다르게 측정될 수 있습니다.)
그래서 어떻게 고치나요? — 메인스레드를 양보하기
핵심은 하나입니다. 클릭 직후 메인스레드를 오래 붙잡지 마라. 제가 현장에서 실제로 쓰는 방법은 이렇습니다.
① 긴 작업 쪼개기. 한 번에 수십 밀리초씩 걸리는 작업을 작은 조각으로 나누고, 조각 사이에 메인스레드를 잠깐 양보합니다. 그러면 브라우저가 그 틈에 화면을 그리고 다음 입력을 받습니다. 위의 ‘무거운 버튼’도 작업을 쪼개기만 하면 ‘가벼운 버튼’처럼 즉시 반응하게 만들 수 있습니다.
② 화면에 안 보이는 일은 뒤로. 클릭 직후 정말 필요한 건 ‘눌린 표시’와 ‘다음 화면’뿐입니다. 분석 전송, 무거운 계산 같은 건 화면을 먼저 그린 뒤로 미룹니다. 사용자는 먼저 반응을 받고, 나머지는 뒤에서 조용히 처리됩니다.
③ 입력 디바운스. 검색창 타이핑처럼 입력이 빗발치는 곳에서는 매 글자마다 무거운 작업을 돌리지 않습니다. 입력이 잠깐 멈춘 뒤 한 번만 처리하도록 묶습니다(디바운스). 처리 횟수 자체가 줄어드니 메인스레드가 한가해집니다.
제가 현장에서 실제로 쓰는 두 조각입니다. 하나는 긴 작업을 쪼개 메인스레드를 양보하는 코드, 하나는 INP가 어디서 나빠지는지 직접 재보는 코드입니다.
// 1) 긴 작업을 조각내고 사이사이 메인스레드를 양보(yield)
async function processChunks(items, handle) {
for (let i = 0; i < items.length; i++) {
handle(items[i]);
if (i % 50 === 0) {
// 브라우저가 그 틈에 화면을 그리고 다음 입력을 받게 함
await (scheduler?.yield?.() ?? new Promise(r => setTimeout(r)));
}
}
}
// 2) 느린 상호작용만 골라 INP 원인 추적
new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
if (e.duration > 200) { // 200ms 초과 = '개선 필요'
console.warn("느린 상호작용:", e.name, Math.round(e.duration) + "ms");
}
}
}).observe({ type: "event", durationThreshold: 200, buffered: true });두 번째 조각이 콘솔에 찍어주는 ‘느린 상호작용’ 목록이, 위의 무거운 버튼 같은 범인을 실제 사이트에서 찾아내는 출발점입니다.
| 항목 | 블로킹 코드 | 분할 처리(쪼개기·디바운스) |
|---|---|---|
| 클릭 반응성 | 한 박자 늦게 멈칫 | 즉시 눌린 표시 |
| INP | 200ms 초과 위험 | 200ms 이하 ‘좋음’ 지향 |
| 사용자 이탈 | ‘느린 사이트’로 이탈↑ | 행동을 끝까지 완료↑ |
다른 담당자와의 연결
반응성은 혼자 있는 지표가 아닙니다. 페이지가 그려지는 속도, 자원이 불러와지는 순서와 한 몸으로 움직입니다. 반응성 설계의 전체 그림은 성능 담당자의 노트에서, 성능의 기본 개념은 웹 성능 기본기에서, 그리고 첫 화면이 빠르게 뜨게 만드는 로딩 설계는 로딩 담당자의 노트에서 이어집니다. 같은 팀이 같은 태도로 다룹니다.
INP를 진지하게 보는 사람이, 사이트 전체를 만듭니다
INP는 결국 ‘사용자를 기다리게 하지 않겠다’는 약속입니다. 클릭 한 번의 0.2초를 지키려고 작업을 쪼개고 미루고 묶는 사람이라면, 로딩도 폼도 같은 태도로 다룹니다. Findable에서는 이런 반응성 설계가 기본값입니다. 당신의 사이트, 어디서 멈칫하고 있을까요?
INP가 FID를 대체했다는 게 무슨 뜻인가요?
좋은 INP 점수는 얼마인가요?
왜 클릭이 가끔 ‘멈칫’하나요?
긴 작업을 쪼갠다는 건 어떻게 하나요?
INP를 좋게 만들면 검색 순위가 오르나요?
이 글의 두 버튼은 이 페이지에서 실제로 동작하는 코드입니다(외부 라이브러리 0). INP의 임계값(75번째 백분위 200ms 등)은 핵심 웹 바이탈의 표준 기준이며, 측정 환경에 따라 다르게 나타날 수 있습니다. 검색 순위·전환 수치는 일반 원칙이며 특정 성과를 보장하지 않습니다. 날조된 사례·수치는 사용하지 않았습니다.