(22.01.09) Debounce && Throttle

도깨비젤리

·

2022. 1. 9. 20:21

작은 지식이라도, 하루에 하나씩.


 

한 줄 요약


🦄쓰로틀링과 디바운싱은 언어에 종속된 것이 아닌, 디바이스의 부담을 덜기 위한 프로그래밍 기법이다🦄
🦄두 기법 모두 이벤트가 짧은 시간 내에 복수 발생했을때 사용할 수 있는 기법이다🦄
🦄이벤트가 반복적으로 발생하는 상황에서 쓰로틀링은 중간중간에 한번씩 이벤트를 실행하고, 디바운싱은 이벤트가 끝날때만 (혹은 이벤트가 시작할때만) 한번 실행함으로서 성능향상을 꾀한다. 🦄

 

 

 

본문


요즘 Vue를 메인으로한 사이드 프로젝트를 진행하고 있는데, Vue를 놓은지 꽤 되어서 다시 한번 공식 문서를 찬찬히 읽어보고 있었다.

 

"확실히 SPA에 익숙해지니 예전 초짜시절보다 이해가 잘되는군~" 라고 뿌뜻해하고 있었는데, 오잉? 처음 보는 개념이 나왔다.

 

https://v3.ko.vuejs.org/guide/data-methods.html#methods

 

 

이게 뭔가 싶어서 검색해보니, 둘 다 UI에서 발생하는 DOM 이벤트를 제어하는 방법이라고 한다.

 

생각해보면, 사용자는 View에서 엄청나게 많은 이벤트를 발생시킨다. 검색창에 검색어를 입력한다든지, 브라우저의 창 크기를 조절한다든지, 스크롤을 위 아래로 내린다든지... 

 

위와 같은 예시들은 사용자 입장에서는 단 한번의 행동이지만, 실제로는 수많은 이벤트들이 발생하게 된다.

 

만약 이런 이벤트가 발생할때마다 어떤 콜백함수를 실행시켜 사용자에게 피드백을 주기로 한 경우, 이 콜백 함수로 인하여 리소스 소모가 상당히 커지게 된다.  UX의 저하는 차치하더라도 만약 이런 이벤트가 발생할때마다 콜백이 유료 API를 호출한다면??  사용자가 스크롤을 위아래로 쉐이킷 하는 것만으로도 개발자의 지갑을 앵꼬 낼 수 있다는 것이다.

 

 

따라서, 사용자와 개발자 모두를 위해서  이벤트가 과도히 발생하는 경우에 제약을 걸어, 제어할 수 있는 수준으로 줄이는 기술이 필요하게 됩니다. 이를 위해 사용되는 방법이 ThrottleDebounce입니다.

 

 

 

 

Throttle


 

항공기에 사용되는 추력 레버, 엔진에 들어가는 공기흐름을 조절하여 출력을 제어한다.

 

 

사실 이 단어를 처음 듣자마자 든 생각은 "스로틀 풀로 당겨!" 라는 영화의 대사였다. 주인공이 스로틀을 끝까지 당기자 항공기는 굉음을 울리며 엄청난 속도로 달리기 시작했고, 악당의 추격을 뿌리칠 수 있었다.

 

그리고 놀랍게도 영화에서 말하는 스로틀이 우리가 이벤트를 제어하기 위해 사용하는 스로틀과유사하다

 

스로틀은 엔진에 들어가는 공기흐름을 조절하여 기체의 속도를 제어하는데, 스로틀 기법은 이벤트의 흐름을 제어하여 웹 페이지의 성능을 제어한다.

 

스로틀 기법은 전체 이벤트 흐름에 delay를 부여하여, 그 delay 동안 얼마나 많은 이벤트가 발생하였든간에 단 한번만 실행하게 한다.

 

 

 

Debounce


boucing을 표현한 그림

 

 

반면, Debounce은 전자 쪽에서 유래한 단어다. 전자 쪽에는 스위치들이 접점에서 떨어지거나 붙는 시점에 물리적으로 미세하게 스위치가 on/off가 되는 현상을 bouncing이라 하고, 이를 방지하는 것을 Debounce라고 한다.

 

매 on/off 마다 실행되는 기계적 동작을 막기 위해서 여러번의 on/off 동작을 하나의 큰 그룹으로 묶은 이후, 그룹 단위마다 동작이 실행되도록 하는데, 우리가 적용할 Debounce 기법도 이와 동일하다. 여러 이벤트가 발생할 때, 일정 그룹으로 묶어서 하나로 처리 하는 것이 debouncing의 본질이다.

 

 

 

 

코드로 보기


<!DOCTYPE html>
<html lang="en">
<!--중략-->
  <body>
    <div>
      <span>count: </span>
      <span id="debounce">디바운스 체크</span>
      <br />
      <button type="button" onclick="debounceTest()">디바운스</button>
    </div>
    <div>
      <span>count:</span>
      <span id="throttle">쓰로틀링 체크</span>
      <br />
      <button type="button" onclick="throttlingTest()">쓰로틀링</button>
    </div>
  </body>
  <script>
    let debounceCount = 0;
    let throttlingCount = 0;
    let debounce, thorottling;

    const debounceTest = () => {
      if (debounce) {
        clearTimeout(debounce);
      }
      debounce = setTimeout(() => {
        debounceCount += 1;
        document.querySelector("#debounce").innerText = debounceCount;
      }, 1000);
    };

    const throttlingTest = () => {
      if (!thorottling) {
        thorottling = setTimeout(() => {
          thorottling = null;
          throttlingCount += 1;
          document.querySelector("#throttle").innerText = throttlingCount;
        }, 1000);
      }
    };

  </script>
</html>

 

 

간단하게 버튼을 클릭하면 카운터가 올라가는 코드를 짜봤다. 단, 짧은 시간 (여기선 1초)내에 여러번의 클릭이 발생하여도, 한번의 이벤트로 처리한다는 것이 포인트이다. 

 

 

결과

 

보다시피, 두 기법 모두 매 클릭을 수용하는 게 아니라, 이벤트를 큰 덩어리로 분리하여 처리하고 있음을 확인할 수 있다. 

 

디바운스는 연속된 클릭이 끝나야지 하나의 카운터가 올라가고, 쓰로틀링은 연속된 클릭 중에도 일정 시간마다 카운터가 하나씩 올라가는 것을 확인할 수 있다.

 

이런 점 때문에, 성능적으로 디바운스 기법이 더 좋은거 아닌가? 라고 생각을 했는데, 검색어 자동 완성등 사용자의 입력 중에도 피드백이 필요한 기능에 대해서는 쓰로틀링 기법을 사용할 수 밖에 없구나 라는 생각을 했다.

 

 

코드 설명


 

  let debounceCount = 0;
  let debounce; //

    const debounceTest = () => {
    // 최초 debounceTest가 실행되면 debounce === undefined 이므로 넘어간다.
      if (debounce) {
        clearTimeout(debounce);
      }
    // 여기서 debounce가 timer 함수로 할당된다
      debounce = setTimeout(() => {
        debounceCount += 1;
        document.querySelector("#debounce").innerText = debounceCount;
      }, 1000);
    };

 

최초에서 debounceTest가 실행되면 debounce가 타이머 함수로 다시 할당되는데, 이 setTimeout이 실행되기 전에 다시 debounceTest가 호출되면, 아까와 달리 debounce가 타이머 함수로서 존재하므로, clearTimeout이 실행되어 어떤 동작도 하지 않는다.

 

debounce가 타이머로서 존재하면서, 1초가 지나야지 그제서야 콜백함수가 실행되는 것이다.

 

이처럼 이벤트가 연속해서 발생하더라도 하나의 큰 이벤트로 처리하는 것이 debounce 기법이다.

 

    let throttlingCount = 0;
    let thorottling;

    const throttlingTest = () => {
      if (!thorottling) {
        thorottling = setTimeout(() => {
          thorottling = null;
          throttlingCount += 1;
          document.querySelector("#throttle").innerText = throttlingCount;
        }, 1000);
      }
    };

 

반면, 스로틀링은 주기적으로 플래그를 비워 setTimeout의 콜백이 실행되게 된다. thorottling이 undefined가 아니라면 throttlingTest를 백날천날 호출해도 아무 일이 없다. 그러나, 한번 thorottling이 타이머 함수로서 할당되면, 1초 뒤에 자기자신을 null로 만들고 document....를 실행한다. 

 

스로틀링은 디바운스와 달리 연속된 이벤트가 발생 중이라도, 1초 뒤에 이벤트의 실행이 보장된다

 

 

 

참고한 글들


https://www.zerocho.com/category/JavaScript/post/59a8e9cb15ac0000182794fa

 

(JavaScript) 쓰로틀링과 디바운싱

안녕하세요. 이번 시간에는 쓰로틀링(throttling)과 디바운싱(debouncing)에 대해 알아보겠습니다. 원래 예정에 없던 강좌이지만 요청을 받았기 때문에 써봅니다. 프로그래밍 기법 중 하나입니다(아니

www.zerocho.com

 

https://webclub.tistory.com/607

 

디바운스(Debounce)와 스로틀(Throttle ) 그리고 차이점

Throttle, Debounce & Difference 스로틀(Throttle) 과 디바운스(Debounce) 란 무엇일까? 이 두 가지 방법 모두 DOM 이벤트를 기반으로 실행하는 자바스크립트를 성능상의 이유로 JS의 양적인 측면, 즉 이벤트(ev.

webclub.tistory.com

 

 

 

 

 

더보기

본문과 무관한 글


* SetInterval과 SetTimeout이 계속 헷갈려서 외울 요량으로 여기에 몰래 적는다

 

setInterval은 clear 하기 전까지 반복적으로 함수를 반복 호출하는 것,

setTimeout은 clear하기 전까지 일정시간 이후 함수를 한번만 호출 하는 것.

 

setInterval은 중간에 다른 Interval이 걸린다면 기존것은 없어짐

setTimeout은 중간에 다른 Timeout이 걸려도 기존것이 없어지지 않음

 

 ** 둘 다 사용이 끝나면 clear해서 메모리에서 해제해야한다.

    function interval() {
      setInterval(() => {
        console.log("fire");
      }, 1000);
    }
    function timeout() {
      setTimeout(() => {
        console.log("black Fire");
      }, 1000);
    }