이벤트에 Throttle과 Debounce를 사용해보자

은승균·2022년 3월 24일
1

회사에서 닉네임을 입력하는 도중 현재 입력되어있는 닉네임에 대한 중복 검사를 요청할 때 어떻게 요청할 지에 대한 고민을 하던 중 ThrottleDebounce에 대한 개념을 접하게 되었다.

Throttle과 Debounce

Throttle과 Debounce란?

두 개념 모두 비슷한 용도로 사용되는데, 이벤트나 함수의 실행 빈도를 조절하여 성능상의 이점을 누리기 위한 목적으로 사용한다.

Throttle : 동일 이벤트가 반복적으로 발생할 때, 이벤트의 실제 주기와는 관계없이 설정한 시간마다 핸들러가 실행되도록 제한하는 방법이다.

Debounce : 동일 이벤트가 반복적으로 발생할 때, 이벤트가 발생이 끝나고 설정한 시간 이후에 (이벤트 발생 후 설정 시간동안 이벤트가 발생하지 않으면) 핸들러가 실행되도록 제한하는 방법이다.

위의 설명에서 알 수 있는 점은 두 방식 모두 이벤트 발생 빈도를 조절할 수 있다는 점이다.
본인이 무엇에 중점을 두고 구현을 하느냐에 따라 둘 중 어떤 방식을 사용할 것인가를 결정할 수 있다.

Lodash의 throttle, debounce 이용하기

Throttle과 Debounce를 이벤트 핸들러에 적용하기 위해 lodash(_)와 같은 라이브러리를 사용할 수 있다.

	// Lodash의 debounce function
	_.debounce(func, [wait=0], [options={}])
	
	// 실제 사용 예시
	let count = 0
	const callback = ()=> {
		count++;
    	console.log(count); 
   }
	//마지막 scroll 이벤트 발생 500ms 이후 callback 실행	
   document.addEventListener('scroll',_.debounce(callback,500));
	

func : 실행할 callback 함수
wait : milliseconds / func 실행까지 delay 할 시간
options :

  • leading : boolean

  • maxWait : number

  • trailing : boolean

    스크롤 이벤트에 핸들러를 붙여놓으면 스크롤을 조금만 내려도 count가 매우 빠르게 올라갈 것이다. 하지만 debounce를 걸어주면 스크롤을 멈추고 500ms가 지났을때에서야 count가 증가하여 출력될 것이다. 그리고 500ms 전에 스크롤 이벤트가 발생한다면 callback 실행이 다시 지연될 것임을 추측해볼 수 있다.


	_.throttle(func, [wait=0], [options={}])
	
	// 실제 사용 예시
	let count = 0
	const callback = ()=> {
		count++;
     	console.log(count); 
    }
	//scroll 이벤트가 반복적으로 발생하면 500ms 마다 callback 실행
    document.addEventListener('scroll',_.throttle(callback,500));

func : 실행할 callback 함수
wait : milliseconds / func 실행 주기
options :

  • leading : boolean
  • trailing : boolean

Throttle의 경우에는 실제 이벤트 발생 주기와는 관계없이 500ms마다 count가 출력될 것이다.

Vanila JS로 구현하기

실제 throttle과 debounce함수가 내부적으로 어떻게 작동하는지 감을 잡아보기 위해서 바닐라 JS로 구현해보았다.

debounce 구현

	function debounce (handler, delay) {
    	let debouncer = null
        return function () {
          // 이벤트가 발생하면 타임아웃을 초기화
            clearTimeout(debouncer)
          // 이후 다시 타임아웃을 걸어준다
            debouncer = setTimeout(() => {
              handler.apply(this, arguments)
            }, delay)
        }
  	}
	

throttle 구현

   function throttle(handler, delay) {
       let throttler
       return function() {
         // timeout이 실행중이지 않다면
           if (!throttler){
               throttler = setTimeout(() => {
                   throttler = null
                   handler.apply(this, arguments)
               }, delay)
           }
       }
   }

실제 프로젝트에서는 lodash 라이브러리를 설치해서 사용을 하면 편하지만 한번 관련 자료들을 참고해가면서 구현해보았다. lodash의 기능을 거의 사용하지 않거나 debounce, throttle을 걸어주는 곳이 몇군데 없다면 직접 구현해서 사용하는 것도 나쁘지 않은 것 같다.

Vue에서 사용해보기

Vue3를 사용중이며 검색 컴포넌트에서 Input을 위한 로직을 composition을 분리하였다.
Input 컴포넌트에서는 value를 담은 이벤트를 부모 컴포넌트로 날려주고 부모 컴포넌트에서는 해당 value를 바인딩한다.

	const useInput = () => {
		const searchInputValue = ref('');
		const debouncer = ref(null);
		const bindSearchInput = (value) => {
			searchInputValue.value = value;

			if (searchInputValue.value.length !== 0) {
				clearTimeout(debouncer.value);
				debouncer.value = setTimeout(() => {	
					searchKeyword(searchInputValue.value);
				}, 1000);
			}
		};
		return {
			searchInputValue,
			bindSearchInput,
		};
	};

bindSearchInput함수에서 value를 받으면 searchKeyword함수를 호출하여 입력하고 있는 검색어와 연관된 검색어를 불러올 수 있도록 구성하였다.
1000ms 이내에 새로운 키워드를 입력하면 함수 실행이 debounce 된다.
키워드 입력을 멈추면 1000ms가 지나고 api를 호출하여 결과를 받아올 수 있다

참고 자료

profile
3대 500을 향해서

0개의 댓글