디바운싱과 쓰로틀링 이해하기

Goal

  • 디바운싱의 개념을 이해한다.
  • 쓰로틀링의 개념을 이해한다.

일단 디바운싱과 쓰로틀링 모두 매우 자주 일어나는 연산에 대해 브라우저 퍼포먼스를 향상 시키기 위해 사용됩니다. 자바스크립트는 싱글 스레드 언어이기 때문에, 특정 연산이 너무 자주 일어나면 성능에 영향을 미치게 됩니다. 그렇다면 디바운싱과 스로틀링이 어떻게 퍼포먼스를 향상시켜주는지 살펴봅시다.

디바운싱(debouncing) 이란?

디바운싱은 특정 연산이 일정한 시간이 지난 후에 최종적으로 실행되도록 합니다.

따라서 디바운싱을 통해 요청이 들어왔을 때, 해당 요청이 들어오고 일정 시간 후에 비로소 요청을 수행할 수 있습니다.

예시

연관검색어를 띄워주는 검색창 예시를 통해 디바운싱을 응용 할 수 있습니다. 특정 단어를 검색창에 타이핑하면 구글이나 네이버는 연관검색어를 보여줍니다. 하지만 사용자가 타이핑하는 모든 글자에 대해 연관 검색어를 불러오는 요청을 수행한다면 너무 비효율적이겠지요.특히나 사용자가 완성하지도 않은 글자에 대해 연관검색어를 보여줄 필요는 없습니다.

따라서 디바운싱을 이용하여 사용자가 타이핑을 끝냈다고 생각했을 때 요청을 수행하면 됩니다. 즉, 일정 시간을 정해놓고 일정 시간 내에 사용자의 input 이벤트가 들어오지 않으면 타이핑을 끝냈다고 가정하여 연관검색어를 불러오는 요청을 보내면 됩니다.

만약 일정 시간이 지나지 않았는데 또 input 이벤트가 들어오면 타이핑을 끝내지 않았다고 가정하여 다시 일정 시간을 세도록 합니다.

아래 코드를 통해 더 살펴봅시다.

const $input = document.querySelector('.search')

const debouncing = (func, limit) => {
  let inDebounce
  // debouncing 실행컨텍스트는 처음에 호출과 함께 종료되어서 event에 접근할 수 없음
  // 클로저에 의해서 debouncing 함수의 실행컨텍스트가 종료된 후에도 inDebounce 변수에 계속 접근 가능
  return function(event) {
    // 이벤트 리스너의 콜백함수이므로 event 위임
    const value = event.target.value
    const context = this
    if (inDebounce) {
      // 900ms가 지나지 않았는데 input 이벤트가 들어왔다면
      clearTimeout(inDebounce) // 다시 900ms 세기
    }
    inDebounce = setTimeout(func.bind(context, value), limit) // Input 이벤트 발생후 최소 900ms 이후에 ajax 요청 보내기
  }
}

$input.addEventListener('input', debouncing(sendRequest, 900)) // 인풋 이벤트 리스너를 등록한다.
  • debouncing 함수의 매개 변수인 func은 요청 수행 함수를 받아오고, limit은 일정 시간을 받아옵니다.
  • 일정 시간(limit)이 지나면 func을 수행하도록 합니다.
  • 일정 시간(limit)이 지나지 않았는데 다시 이벤트가 들어왔다면 시간을 다시 세도록 합니다 (clearTimeout)

디바운싱 전체 코드 보기


쓰로틀링(Throttling) 이란 ?

쓰로틀링은 일정 시간동안 여러번 요청이 들어오더라도 딱 한번만 수행되도록 합니다.

따라서 쓰로틀링을 통해 일정 시간동안 여러번 요청이 들어오더라도 딱 한번만 요청을 수행할 수 있습니다.

예시

스크롤 이벤트 발생시 특정 요청을 수행하는 예시를 통해 쓰로틀링을 응용 할 수 있습니다. 스크롤링 이벤트는 굉장히 빈번히 일어납니다. 사실 특정 스크롤 이벤트 발생시에 어떤 요청을 수행한다면 연산이 매우 많이 일어날 수 밖에 없습니다. 또한 사용자가 실수로 스크롤 이벤트를 발생시키는 경우도 빈번합니다.

따라서 쓰로틀링을 통해서 일정 시간 내에 일어나는 스크롤 이벤트에 대해서 요청을 수행하도록 하여 퍼포먼스를 향상 시킬 수 있습니다. 아래 예시는 스크롤링이 발생하면 스크롤 위치에 따라서 background-color를 변경하는 연산을 수행합니다.


아래코드를 통해 더 살펴봅시다.

const [red, green, blue] = [5, 253, 166]
const $body = document.querySelector('body')

const changeColor = () => {
  // 스크롤 이벤트에 따른 색상 변경
  let y = 1 + (window.scrollY || window.pageYOffset) / 150
  y = y < 1 ? 1 : y
  const [r, g, b] = [red / y, green / y, blue / y].map(Math.round)
  $body.style.backgroundColor = `rgb(${r},${g},${b})`
}

const throttle = (func, limit) => {
  let inThrottle
  return function() {
    const context = this // 콜백함수이므로 addEventListener의 this 위임
    // 클로저에 의해서 throttle 함수의 실행컨텍스트가 종료된 후에도 inThrottle 변수에 계속 접근 가능
    if (!inThrottle) {
      func.apply(context)
      inThrottle = true // limit.ms에 한번씩만 함수 실행
      setTimeout(() => (inThrottle = false), limit)
    }
  }
}

window.addEventListener('scroll', throttle(changeColor, 100))
  • 전체적인 구조는 디바운싱함수와 같습니다.
  • inThrottle 이라는 변수를 통해서 일정 시간 (limit) 이 지났는지 확인 하여 changeColor (func) 을 실행합니다.
  • func이 한번 실행되면 inThrottle 을 true로 바꾸어 주고, limit 시간 후에 다시 false로 바꿉니다.
  • inThrottle이 true라면 다시 func을 실행하지 않도록 하고 false라면 다시 실행할 수 있도록 합니다.

쓰로틀링 전체 코드 보기



이렇게 디바운싱과 쓰로틀링을 예시를 통해 살펴 보았습니다. 적재적소에 디바운싱과 쓰로틀링을 사용하면 퍼포먼스 향상에 도움이 될 것입니다! 특히나 스크롤링이벤트나 인풋 이벤트와 같이 사용자에 의해 빠른 시간 내 여러번 일어나는 이벤트에 따라 특정 요청을 매번 수행하는 것은 매우 비효율적입니다. 이럴 때 디바운싱과 쓰로틀링을 이용하면 매우 효율적일 것입니다


Reference


This is@moonee
프론트엔드 개발 공부 블로그

GitHub