쓰로틀링과 디바운싱
쓰로틀링(throttling)과 디바운싱(debouncing)에 대해 알아보겠습니다.
용어가 생소하신 분들을 위해 간단히 설명해보겠습니다.
- 쓰로틀링 : 마지막 함수가 호출된 후 일정시간이 지나기 전에 다시 호출되지 않도록 하는 것
- 디바운싱 : 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것
위 두 개는 underscore(_)에도 있는 기능입니다.
underscore나 lodash를 쓰고 계신 분들이라면 그 라이브러리의 메소드를 쓰시면 편합니다.
사용처가 궁금하실 겁니다.
디바운싱은 주로 ajax 검색에 자주 쓰입니다.
쓰로틀링은 스크롤을 올리거나 내릴 때 보통 사용합니다.
어디까지나 제 경험에 바탕한 사용처입니다.
디바운싱
요즘 서비스들은 검색어 치자마자 엔터 없이도 결과가 바로바로 나옵니다.
만약 ‘제로초’를 검색창에 친다고 합시다.
엔터없이도 결과를 즉시 보여주려면 항상 input
이벤트에 대기하고 있어야 합니다.
<input id="input">
document.querySelector('#input').addEventListener('input', function (e) {
console.log('여기에 ajax 요청', e.target.value);
})
실제 ajax 요청을 보내기는 힘드니 콘솔 로그로 대체합니다.
로그가 콘솔에 찍힐 때마다 ajax 요청이 실행된다고 생각하시면 됩니다.
문제는 한 글자 칠 때마다 ajax 요청이 실행된다는 것입니다.
‘ㅈ’, ‘제’, ‘젤’, ‘제로’, ‘제롳’, ‘제로초’ 모두 요청이 실행됩니다.
6번이나 요청을 했습니다(한글같은 조합형 언어는 사진처럼 6번보다 더 많이 이벤트가 발생할 수도 있습니다).
거기에 ‘ㅈ’, ‘젤’, ‘제롳’는 제대로 된 검색 결과가 나오지 않을 것 같은 검색어입니다.
이와 같은 낭비는 유료 API를 사용했을 때 큰 문제가 됩니다.
만약 구글지도 API 같은 것을 사용할 때 위와 같이 쿼리를 10번 날리면 어마어마한 손해입니다.
쿼리 하나가 다 돈이거든요.
따라서 디바운싱은 비용적인 문제와도 관련이 있습니다.
우리는 마지막 ‘제로초’를 다 쳤을 때 ajax 요청을 보내고 싶습니다.
먼저 어떻게 구현할지 생각해봅시다.
보통 사람들은 타자를 연달아 칩니다.
중간에 잠시 생각하느라 몇 초 쉴수는 있겠지만 대부분 한번에 검색어를 입력합니다.
따라서 입력이 다 끝난 후에 요청을 보내면 됩니다.
즉 타자를 칠 때(input 이벤트 발생)마다 타이머를 설정합니다.
200ms 동안 입력이 없으면 입력이 끝난 것으로 칩니다(시간은 적당히 설정하면 됩니다).
200ms 이전에 타자 입력이 발생하면 이전 타이머는 취소하고 새로운 타이머를 다시 설정하는 겁니다.
var timer;
document.querySelector('#input').addEventListener('input', function (e) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
console.log('여기에 ajax 요청', e.target.value);
}, 200)
})
이제 더는 여러번 호출되지 않습니다.
이게 바로 디바운싱입니다.
한글 특성상 마지막에 두번 호출되는 경우도 있습니다.
쓰로틀링
쓰로틀링은 보통 성능 문제 때문에 많이 사용합니다.
특성 자체가 실행 횟수에 제한을 거는 것이기도 하고요.
스크롤을 올리거나 내릴 때 scroll 이벤트가 매우 많이 발생합니다.
scroll 이벤트가 발생할 때 뭔가 복잡한 작업을 하도록 설정했다면 매우 빈번하게 실행되기 때문에 엄청 렉이 걸릴 것입니다.
그럴 때 쓰로틀링을 걸어줍니다.
몇 초에 한 번, 도는 몇 밀리초에 한 번씩만 실행되게 제한을 두는 것이죠.
디바운싱으로 구현했던 ajax 검색을 쓰로틀링으로 대체해도 됩니다.
물론 쿼리는 조금 더 날리겠지만요.
한번 구현해보겠습니다.
똑같이 200ms 초 제한을 걸었습니다.
타이머가 설정되어 있으면 아무 동작도 하지 않고, 타이머가 없다면 타이머를 설정합니다.
타이머는 일정 시간 후에 스스로를 해제하고, ajax 요청을 날리게 하면 됩니다.
var timer;
document.querySelector('#input').addEventListener('input', function (e) {
if (!timer) {
timer = setTimeout(function() {
timer = null;
console.log('여기에 ajax 요청', e.target.value);
}, 200);
}
});
이제 최소 200ms 마다 요청을 보냅니다.
물론 ajax 검색은 디바운싱으로 처리하는 게 더 나아보입니다.
하지만 중간 중간 검색 결과도 보여주고 싶다면 쓰로틀링도 괜찮은 옵션인 것 같습니다.
물론 매우 빠르게 구현한 것이기 때문에 이 코드로는 예외 사항들을 처리하지 못할수도 있습니다.
underscore의 _.debounce
와 _.throttle
을 추천합니다.
이렇게 디바운싱과 쓰로틀에 대해 알아보았습니다.
코드는 잊어버리셔도 좋습니다.
하지만 용어는 기억해두세요.
그래야 나중에 다시 검색할 수 있습니다.