Computer Science/Network

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

둉이 2022. 5. 25. 01:55

오늘은 프론트엔드 개발에서 많이 사용되는 디바운싱과 쓰로틀링 개념에 대해 araboza.

 

디바운싱과 쓰로틀링은 성능을 목적으로 자바스크립트의 이벤트를 제어하기 위해 사용되는 기법이다.

 

 

디바운싱(debouncing)

디바운싱 설명 이미지

 

디바운싱은 스위치 회로에서 채터링(바운싱) 현상을 해결하기 위해 사용되는 개념이다.

 

기본 상태가 LOW, 스위치를 눌렀을 때의 상태가 HIGH인 스위치가 있다고 하자.

 

스위치를 한 번만 눌렀다 떼는 경우에도 눌림 상태일 때의 지속 시간이나 외부 진동, 충격에 의해 스위치가 여러 번 눌린 것처럼 동작하게 되는데, 이러한 현상을 바운싱이라고 한다.

 

바운스 구간의 값을 무시하기 위해 처음 HIGH 값으로 상태가 바뀌었을 때부터 특정 시간 동안은 판단을 지연했다가 해당 시간 이후에도 HIGH 상태라면 그제서야 HIGH 상태라고 결정한다.

 

자바스크립트에서의 디바운싱도 이와 마찬가지로 마지막으로 발생한 이벤트를 기준으로 특정 time이 지난 후 하나의 이벤트만 실행되도록 한다.

 

디바운싱을 적용하면 좋은 예시로는 검색 기능의 키 이벤트(input)가 있다.

 

검색 버튼을 누르지 않아도 사용자가 입력한 값이 변경될 때마다 자동으로 검색 요청을 보내는 기능을 만든다고 한다면, 사용자가 검색어를 다 입력하지 않았을 때에도 요청이 보내지게 되므로 비효율적이다.

 

이러한 경우에 디바운싱을 적용하면 사용자의 입력이 끝나고 일정 시간이 지난 후에 검색 요청이 1번만 보내지므로 성능을 향상시킬 수 있다.

 

디바운싱을 코드로 표현하면 다음과 같다.

const inputElement = document.querySelector('#ID값');
const DELAY = 500;
const handleChange = () => {
  console.log('요청!');
}

let timer;

inputElement.addEventListener('input', () => {
  if (timer) clearTimeout(timer);
  timer = setTimeout(handleChange, DELAY);
});

 

디바운싱을 구현하기 위해서는 setTimeout 함수와time(기준 시간)이 필요하다.

 

이벤트가 발생할 때마다 timer 변수에 콜백 함수를 반환하는 setTimeout 함수를 값으로 할당한다.

 

time 이내에 새로운 이벤트가 발생할 때마다 타이머를 초기화 해준다고 이해하면 된다.

 

time 이내에 새로운 이벤트가 발생한다면 clearTimeout을 해줌으로써 콜백 함수는 실행되지 않으며, 발생하지 않으면 setTimeout은 콜백 함수를 실행하게 된다. 

 

 

리액트에서는 다음과 같이 함수 실행에 디바운싱을 적용한 커스텀 훅을 만들어 사용할 수도 있다.

import { useCallback, useEffect } from 'react';

const useDebounceFunc = (func: () => void, delay: number, deps: any[]) => {
  const callback = useCallback(func, deps);

  useEffect(() => {
    const timer = setTimeout(() => callback(), delay);
    return () => clearTimeout(timer);
  }, [callback, delay]);
};

export default useDebounceFunc;

 

 

쓰로틀링(throttling)

쓰로틀링 설명 이미지

 

조절 혹은 목을 조른다는 뜻으로, 주로 PC나 하드웨어의 성능 제한과 관련하여 많이 사용되는 개념이다.

 

기기의 CPU, GPU 등이 지나치게 과열되어 일정 온도 이상에 다다르면 기기에 손상을 일으키게 되는데, 이를 방지하기 위해 클럭이나 인가 전압을 제한하여 발열을 낮추는 기법이다.

 

자바스크립트에서의 쓰로틀링은 짧은 시간 내에 과도한 이벤트 실행으로 인해 발생하는 성능 저하를 막기 위해 일정한 time slice를 주기로 이벤트가 한 번만 실행되도록 제한한다.

 

예를 들어 1초에 100번의 업데이트가 일어나는 DOM 요소에 이벤트 핸들러를 추가한다면, 이벤트는 1초에 100번이나 실행되므로 성능에 무리를 줄 수 있다.

 

쓰로틀링을 적용하게 되면 불필요한 이벤트 실행 횟수를 줄일 수 있어 성능을 향상시킬 수 있다.

 

쓰로틀링을 적용하면 좋은 예시로는 scroll, resize, drag, mouse 이벤트, 애니메이션 등이 있다.

 

예를 들어 스크롤 이벤트의 경우, 크롬 기준 2px 단위로 이벤트가 발생하게 된다.

 

사용자가 1초에 1000px의 스크롤을 하게 되는 경우에는 약 500번의 이벤트가 발생되므로 성능에 좋지 않을 수 있다.

 

이러한 경우에 time = 100 간격으로 이벤트가 실행되도록 쓰로틀링을 적용하면 실행 횟수를 10번으로 줄일 수 있다.

 

쓰로틀링을 코드로 표현하면 다음과 같다.

const DELAY = 500;
const handleScroll = () => {
  timer = null;
  console.log('스크롤!');
}
        
let timer;  
   
window.addEventListener('scroll', () => {
  if (!timer) timer = setTimeout(handleScroll, DELAY);
});

 

쓰로틀링도 디바운싱과 마찬가지로 setTimeout 함수와time(기준 시간)이 필요하다.

 

이벤트가 발생했을 때, timer 변수의 값이 비어있다면 콜백 함수를 반환하는 setTimeout 함수를 값으로 할당한다.

 

그리고 콜백 함수 내에서는 timer를 null 값으로 초기화 해준다.

 

이러한 방법으로 time 이내에 최대 1번의 콜백 함수만 실행하도록 보장하게 된다. 

 

 

lodash를 사용한 debounce, throttle 적용

lodash에서는 위에서 설명한 디바운싱과 쓰로틀링을 쉽게 적용할 수 있도록 debounce, throttle 메소드를 제공한다.

 

아래와 같이 디바운싱 혹은 쓰로틀링을 적용할 함수를 메소드로 감싸고 2번째 인자로 delay 값을 전달하면 된다.

 

- 디바운싱 예제

import { debounce } from 'lodash';

const DebounceInput = () => {
  const handleChange = debounce(({ target }) => {
    console.log('debounce', target.value);
  }, 500);

  return <input type="text" onChange={handleChange} />;
};

export default DebounceInput;

 

- 쓰로틀링 예제

import { throttle } from 'lodash';

const ThrottleInput = () => {
  const handleChange = throttle(({ target }) => {
    console.log('throttle', target.value);
  }, 500);

  return <input type="text" onChange={handleChange} />;
};

export default ThrottleInput;