본문 바로가기
JavaScript

자바스크립트의 디바운싱과 쓰로틀링

by Vintz 2022. 6. 10.
반응형

디바운싱(debouncing)과 쓰로틀링(throttling). 언뜻 들어는 봤지만 유야무야 시간을 보내다가, 기술 과제 탈락 후 디바운싱과 같은 최적화 작업을 했으면 좋겠다는 피드백을 받았다. 그렇다면 어떨 때는 디바운싱을 사용해야하고, 쓰로틀링을 사용해야할까? 이번 기회에 제대로 개념을 잡고 가야겠다고 생각했다.

들어가기 전

  • setTimeout() - 함수의 실행을 예약하는 타이머 기능
  • clearTimeout() - 타이머의 실행을 취소하는 기능
  • 디바운싱 - 빈번하게 발생하는 이벤트를 '특정 시간 이후에 한번만' 실행 시키는 최적화 기법
  • 쓰로틀링 - 빈번하게 발생하는 이벤트를 '일정한 간격으로 한번만' 실행 시키는 최적화 기법

setTimeout() - 함수의 실행을 예약하는 타이머 기능

setTimeout() 함수는 브라우저에서 제공하는 Web API로, 지정된 시간 후에 함수를 실행 하는 데 사용한다.

const timerId = setTimeout(callbackFunction, timeout);
  • callbackFunction - 타이머가 만료되면 실행할 함수
  • timeout - 함수를 실행하기전까지 기다릴 밀리초 단위의 시간

timeout을 생략하거나 0으로 설정하면 즉시, 정확히는 다음 이벤트 사이클에 실행된다. 실제로는 딜레이가 0보다 더 길 수 있는데 그 이유는 어쨌든 이벤트 루프는 무엇입니까? 영상을 참고하자.

코드

function delayFunction() {
  console.log('300 밀리초 뒤에 호출됩니다.');
}

const timerId = setTimeout(delayFunction, 300);

console.log(`Timer ID: ${timerId}`);
// Timer ID: 2
// 300 밀리초 뒤에 호출됩니다.

clearTimeout() - 타이머의 실행을 취소하는 기능

clearTimeout() 함수는 타이머의 실행을 취소하는 기능을 한다. 그게 어떻게 가능할까? clearTimeout() 함수는 설정한 시간이 되기 전에 setTimeout() 함수를 호출하여 타임아웃(시간 초과)을 막는다.

 

clearTimeout() 함수는 setTimeout() 함수에서 반환하는 양의 정수 값을 통해 식별하여 취소할 수 있다.

setTimeout() 함수는 number 값을 반환한다. - lib.dom.d.ts

만약 식별할 수 있는 값이 제공되지 않는다면(수행할 작업이 없다면) clearTimeout은 아무 작업도 하지 않는다.

코드

function delayFunction() {
  // clearTimeout에 의해 취소되므로 해당 로그는 출력되지 않는다.
  console.log('300 밀리초 뒤에 호출됩니다.');
}

const timerId = setTimeout(delayFunction, 300);
console.log(`Timer ID: ${timerId}`);

clearTimeout(timerId);
// Timer ID: 2

디바운싱 - 특정 시간 이후에 한번만 실행하기

디바운싱이란 사용자가 이벤트를 몇 번이나 발생 시키든 이벤트 발생을 멈추고 지정된 시간까지 지난 후에야 이벤트가 한번만 실행 되도록 하는 기법이다. 설명이 길어서 이해하기가 힘드니 예를 들어보자.

사용자가 300밀리초 동안 버튼을 5번 클릭 했다고 가정해보자. 디바운싱은 클릭에 의한 이벤트 실행이 바로 되지 않는다. 디바운싱 시간이 300밀리초로 설정되어 있다면, 사용자가 클릭을 멈추고 300밀리초가 지나고 나서야 해당 함수가 한번만 실행된다. 이것은 마치 연이어 호출되는 함수들을 하나의 단일 이벤트로 만드는 것처럼 보인다. 따라서 사용자가 여러 번 클릭을 하면 마지막 함수만 호출하게 된다.

디바운싱은 주로 어디서 사용할까?

디바운싱은 성능뿐만 아니라 유료 API를 사용할 때 큰 효과를 볼 수 있다. 요청 하나하나가 모두 돈이기 때문이다. 따라서 디바운싱은 주로 연이어 발생하는 이벤트를 단일 이벤트로 만들고 싶은 곳, 즉 빈번하게 발생하는 타이핑 이벤트의 결과 표시(검색 결과 목록 표시), 블로그 글쓰기 에디터의 자동저장 기능 등에 쓰인다.

코드

const clickBtn = document.querySelector('#clickBtn');
const clickCnt = document.querySelector('#clickCnt');
const debouncingCnt = document.querySelector('#debouncingCnt');

let timerId;

function debouncing(func, timeout = 300) {
  clearTimeout(timerId);
  timerId = setTimeout(func, timeout);
}

function delayFunction() {
  debouncingCnt.innerHTML = parseInt(debouncingCnt.innerHTML) + 1;
}

clickBtn.addEventListener('click', () => {
  clickCnt.innerHTML = parseInt(clickCnt.innerHTML) + 1;
  debouncing(delayFunction);
});

쓰로틀링 - 일정한 간격으로 한번만 실행하기

쓰로틀링은 사용자가 이벤트를 몇 번이나 발생 시키든 일정한 시간 간격으로 한번만 실행하도록 하는 기법이다. 이것 역시 예를 들어보자.

사용자가 고의든 아니든 클릭에 의한 이벤트 실행을 연달아 호출했다고 가정해보자. 쓰로틀링은 마지막 함수를 기다리지 않으며, 첫 번째 클릭에 의한 이벤트만을 실행하고 주어진 시간 동안 나머지는 무시한다.

쓰로틀링은 주로 어디서 사용할까?

쓰로틀링은 어디에 적용하는 게 좋을까? 짧은 주기로 실행되는 이벤트를 '조절(throttling)'하고 싶은 곳에 사용하면 된다. 스크롤 이벤트나 마우스 움직임 이벤트와 같은 이벤트가 연속으로 실행 되는 곳, 그리고 예시로 보인 하나의 버튼을 여러 번 클릭하는 경우에는 타임아웃을 좀 더 길게 설정하고, 쓰로틀링을 적용하는 것이 더 좋을 것 같다.

코드

const clickBtn = document.querySelector('#clickBtn');
const clickCnt = document.querySelector('#clickCnt');
const throttlingCnt = document.querySelector('#throttlingCnt');

let timerId;

function throttling(func, timeout = 300) {
  if (timerId) {
    return;
  }

  timerId = setTimeout(() => {
    func();
    timerId = undefined;
  }, timeout);
}

function delayFunction() {
  throttlingCnt.innerHTML = parseInt(throttlingCnt.innerHTML) + 1;
}

clickBtn.addEventListener('click', () => {
  clickCnt.innerHTML = parseInt(clickCnt.innerHTML) + 1;
  throttling(delayFunction);
});

이전 이벤트를 무시하는 것과 후속 이벤트를 무시하는 것

이제 디바운싱과 쓰로틀링의 개념이 어느 정도 잡히는 것 같다. 디바운싱은 연이어 호출되는 함수들 중에서 이전 이벤트를 무시하고 마지막 함수만을 호출하는 것이고, 쓰로틀링은 연이어 호출되는 함수들 중에서 첫 번째 함수만을 호출하여 주어진 시간 동안 후속 이벤트를 무시하는 것이다. 빈번하게 발생하는 이벤트에 상황에 따라 적절하게 적용을 하면 성능향상과 여러 효과를 얻을 수 있을 것이다.

굳이 직접 구현하지 않아도 널리 사용되는 라이브러리가 있다. 또한 설명한 코드로는 부족할 수도 있다. 따라서 lodashunderscore.js를 통해 해결하는 것도 좋은 방법이 될 수 있다.

참고

Debounce – How to Delay a Function in JavaScript (JS ES6 Example) - freecodecamp
Debouncing and Throttling in JavaScript - telerik
쓰로틀링과 디바운싱 - zerocho
반응형

댓글 2

  • 핸디(Handy) 2022.06.10 10:24 신고

    좋은 글 잘보았습니다.

    저도 디바운스랑 쓰로틀링 쓸때 lodash 함수에서 코드만 들고와서 사용하고 있습니다. 물론 약간 변형을 하긴 하지만요.

    피드백을 이렇게 정리하시다니 대단합니다!!
    답글

    • Vintz 2022.06.10 10:52 신고

      그렇군요! 저는 사실 이번에 처음으로 lodash를 알게 되었어요.

      여태 모르다 이제야 알게 되었지만 앞으로는 사용할 곳이 많을 것 같아 자주 들여다보려고 합니다. ㅎㅎ

      감사합니다!! :)