디바운스와 스로틀은 짧은 시간에 연속해서 발생하는 이벤트를 그룹화해 이벤트 핸들러가 과도하게 호출되지 않도록 방지하는 기법이다.
scroll, resize, click 이벤트 등에 적용하기 전과 적용한 후 결과를 살펴보면 엄청난 차이가 있다.
따라서 이벤트 핸들러에서 무거운 작업을 수행하는 경우 디바운스와 스로틀 처리를 해주는 것이 성능 개선에 도움이된다. 현재 진행 중인 프로젝트에 적용하기 전에 간단한 개념과 예시 코드를 살펴보려 한다.
짧은 시간 간격으로 발생하는 이벤트를 그룹화해 마지막 한 번만 이벤트 핸들러가 호출된다.
검색과 같이 사용자가 입력한 값을 통해 비동기 요청을 보내야 하는 경우가 있다. 사용자의 입력이 끝나지 않았음에도 입력 이벤트가 발생할 때마다 비동기 요청을 보내게 되면 서버에 큰 부담을 줄 수 있다.
다음은 사용자의 입력이 0.5초동안 없다면 비동기 요청을 보내는 디바운스 함수다.
<!DOCTYPE html>
<html lang="en">
<body>
<input type="text" class="input" />
<div class="content" />
<script>
const input = document.querySelector('.input')
const content = document.querySelector('.content')
const debounce = (cb, delay) => {
let timerId
return (...args) => {
// 0.5초가 지나기 전에 이벤트가 발생하면
if (timerId) clearTimeout(timerId) // 이전 타이머를 종료하고
timerId = setTimeout(cb, delay, ...args) // 새로운 타이머를 설정
}
}
input.addEventListener(
'input',
debounce((e) => {
content.textContent = e.target.value
}, 500)
)
</script>
</body>
</html>
[참고]
setTimeout(cb, delay, ...args)
setTimeout()의 세 번째 인자는 첫 번째 인자인 함수가 인자를 받는 경우,
해당 함수에 넘길 인자를 명시해주기 위해 사용한다.
디바운스 이벤트는 보통 resize 이벤트 처리, 입력 필드 자동완성(auto complete), 중복 클릭 방지 등에 사용된다.
짧은 시간 간격으로 발생하는 이벤트를 그룹화해 일정 시간단위로 이벤트 핸들러가 호출된다. 이벤트의 호출 주기를 정한다고 할 수 있다.
다음은 스크롤 이벤트를 감지해 0.5초를 주기로 한 번의 작업만 수행하는 스로틀 함수다.
<!DOCTYPE html>
<html lang="en">
<style>
.container {
background: salmon;
width: 300px;
height: 300px;
overflow: scroll;
margin-bottom: 10px;
}
.content {
height: 500vh;
}
</style>
<body>
<div class="container">
<div class="content"></div>
</div>
일반 핸들러 : <span class="normal">0</span> <br />
스로틀 핸들러 <span class="throttle">0</span>
<script>
const container = document.querySelector('.container')
const normalDiv = document.querySelector('.normal')
const throttleDiv = document.querySelector('.throttle')
const throttle = (cb, delay) => {
let timerId
return (...args) => {
// delay가 지나지 않았다면 어떤 작업도 하지 않음
if (timerId) return
// delay 경과 후 이벤트 발생시 새로운 타이머 설정
// delay후 이벤트 실행 + timerId 빈값으로 초기화
timerId = setTimeout(() => {
cb(...args)
timerId = null
}, delay)
}
}
let normalCnt = 0
container.addEventListener('scroll', () => {
normalDiv.textContent = ++normalCnt
})
let throttleCnt = 0
container.addEventListener(
'scroll',
throttle(() => {
throttleDiv.textContent = ++throttleCnt
}, 300)
)
</script>
</body>
</html>
결과를 보면 엄청난 차이를 체감할 수 있다. 무한스크롤에 스로틀을 적용했으면 좋았을 텐테란 생각이 들었다.
디바운스
스로틀