[JavaScript] 쓰로틀링(throttling)과 디바운싱(debouncing)

Letmegooutside·2022년 1월 25일
0

JavaScript

목록 보기
16/25
post-thumbnail

쓰로틀링(throttling)과 디바운싱(debouncing)

이 두 가지 방법 모두 DOM 이벤트를 기반으로 실행하는 자바스크립트를 성능상의 이유로 JS의 양적인 측면, 즉 이벤트(event)를 제어(제한)하는 프로그래밍 기법이다.

예를 들어, 웹/앱 사용자가 스크롤(scroll wheel), 트랙패드, 스크롤 막대를 드래깅 한다고 가정해보자.
사용자는 크게 느끼지 못할 수 있으나 이 행위로 인해 수많은 스크롤 이벤트가 발생하게 된다.
이 때 매번 스크롤 이벤트에 대한 콜백(callback)이 발생하고 그 콜백이 수행하는 일은 매우 큰 리소스를 잡아먹게 될 것이다.

Throttle 과 Debounce 는 이벤트가 과도한 횟수로 발생하여 이벤트 핸들러가 무거운 연산을 수 없이 많이 수행하는 경우에 제약을 걸어 제어할 수 있는 수준으로 이벤트를 발생시키는 것을 목표로하는 기술이다.
(underscore나 lodash의 메소드를 사용하면 편리하다.)

  • 쓰로틀링 : 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것
  • 디바운싱 : 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것

디바운싱

만약 "시그널"을 엔터 없이도 검색 결과가 바로 나오는 검색창에 친다고 해보자.
엔터 없이도 결과를 즉시 보여주려면 항상 input 이벤트에 대기하고 있어야 한다.

<input id="input" />
document.querySelector('#input').addEventListener('input', function(e) {
  console.log(`api 요청 : ${e.target.value}`);
});

(실제 api 요청을 보내기는 힘드니 콘솔 로그로 대체한 코드이다.)
여기서 문제는 한 글자 칠 때마다 api 요청이 실행된다는 것이다.
'ㅅ', '시', '식', '시그', '시근', '시그너', '시그널' 모두 7번이나 요청을 했다.
(한글같은 조합형 언어는 사진처럼 7번보다 더 많이 이벤트가 발생할 수도 있다.)
거기에 'ㅅ', '식', '시근' 등은 제대로 된 검색 결과가 나오지 않을 것 같은 검색어이다.

이와 같은 낭비는 유료 API를 사용했을 때 큰 문제가 된다.
만약 구글지도 API같은 것을 사용할 때 위와 같이 쿼리를 10번 날리면 큰 손해를 입게 된다.

따라서 디바운싱은 비용적인 문제와도 관련이 있다.
우리는 마지막 "시그널"을 다 입력했을 때 api 요청을 보내고 싶다.

먼저 어떻게 구현할지 생각해보자.
보통 사람들은 타자를 연달아친다.
중간에 잠시 생각하느라 몇 초 쉴 수는 있겠지만 대부분 한 번에 검색어를 입력한다.
따라서 입력이 다 끝난 후에 요청을 보내고 싶다.

즉 타자를 칠 때(input 이벤트 발생)마다 타이머를 설정하고, 200ms동안 입력이 없으면 입력이 끝난 것으로 친다(시간은 적당히 설정하면 된다).
그리고 200ms 이전에 타자 입력이 발생하면 이전 타이머는 취소하고 새로운 타이머를 다시 설정하는 것이다.

var timer;
document.querySelector('#input').addEventListener('input', function(e) {
  // 아직 타이머 수행되지 않았으면(직전 입력으로부터 200ms가 지나지 않았다면) 타이머 취소
  // (타이머가 존재하면, 즉 직전 입력에서 타이머를 생성했다면)
  if (timer) {
    clearTimeout(timer);
  }
  // 타이머 설정
  timer = setTimeout(function() {
    console.log(`api 요청 : ${e.target.value}`);
  }, 200);
});

쓰로틀링

쓰로틀링은 보통 성능 문제 때문에 많이 사용한다. 특성 자체가 실행 횟수에 제한을 거는 것이기도 하다.

스크롤을 올리거나 내릴 때 scroll 이벤트가 매우 많이 발생하게된다.
scroll 이벤트가 발생할 때 뭔가 복잡한 작업을 하도록 설정했다면 매우 빈번하게 실행되기 때문에 렉이 걸릴 것이다.
그럴 때 쓰로틀링을 걸어서 몇 초에 한 번, 또는 몇 밀리초에 한 번씩만 실행되도록 제한을 두면 된다.

디바운싱으로 구현했던 검색을 쓰로틀링으로 대체해도 된다.
똑같이 200ms초 제한을 걸어두었다.
타이머가 설정되어 있으면 아무 동작도 하지 않고, 타이머가 없다면 타이머를 설정한다.
타이머는 일정 시간 후에 스스로를 해제하고, api 요청을 날린다.

var timer;
document.querySelector('#input').addEventListener('input', function (e) {
   // 타이머가 없을 때만 타이머 설정.
   // 만약 200ms가 지나서 해당 함수를 실행하면 타이머는 사라진다. (timer = null)
   // 만약 타이머 설정 후 200ms가 지나지 않았다면 아무 일도 일어나지 않는다.
   // 따라서 최소 200ms 마다 한번씩만 아래의 코드가 실행된다.
   if (!timer) {
      timer = setTimeout(function() {
          timer = null;
          console.log(`api 요청 : ${e.target.value}`);
      }, 200);
   }
});

이제 최소 200밀리초마다 한번씩만 요청을 보내게 된다.

(빠르게 구현한 것이기 때문에 이 코드로는 예외 사항들을 처리하지 못 할 수도 있어 underscore_.debounce_.throttle을 추천한다고 한다.)




Reference
https://www.zerocho.com/category/JavaScript/post/59a8e9cb15ac0000182794fa
https://webclub.tistory.com/607

0개의 댓글