무거운 이벤트 핸들러: 클릭이 500ms 걸리는 이유와 해결 방법
무슨 일이 일어나는가
사용자가 클릭하면 브라우저는 다음 프레임을 그리기 전에 이벤트 핸들러를 끝까지 실행합니다. 핸들러가 수백 ms의 동기 작업 — 큰 배열 필터링, DOM 구축, JSON 파싱 — 을 하면 페이지는 정확히 그만큼 멈춥니다. 핸들러가 반환될 때까지 사용자에게는 아무 일도 일어나지 않는 것처럼 보입니다.
이는 가장 흔한 INP 문제이며, INP 분해에서 긴 processing time 단계로 나타납니다.
알아보는 법
- INP 분해에서 processing time이 지배적(input delay와 presentation delay는 작음).
- DevTools Performance에서 이벤트 직후 하나의 긴 작업이 시작되며 핸들러 함수에 귀속됨.
- 느림이 데이터 크기에 비례: 행/항목이 많을수록 클릭이 더 무겁게 느껴짐.
해결 방법
- 먼저 UI를 갱신하고(눌린 상태, 스피너), 무거운 작업 전에 scheduler.yield()(폴백은 setTimeout(0))로 브라우저에 제어를 넘기세요.
- 큰 루프를 청크로 나누고 청크 사이에 제어를 넘겨 브라우저가 그릴 수 있게 하세요.
- 순수 계산(파싱, 필터링, 디핑)을 Web Worker로 옮기세요 — 메인 스레드는 입력만 보내고 결과만 받습니다.
문제
button.addEventListener("click", () => {
const results = filterRows(allRows) // 400 ms of sync work
renderTable(results) // user saw nothing until now
})
해결
button.addEventListener("click", async () => {
button.classList.add("loading") // instant feedback, painted first
await scheduler.yield() // let the browser paint
const results = filterRows(allRows)
renderTable(results)
button.classList.remove("loading")
})
사이트 무료로 스캔하기 →