Language/JavaScript

🚀 디바운스(Debounce) vs 쓰로틀(Throttle) 완벽정리

데범 2025. 3. 16. 01:40

웹 개발을 하다보면 사용자의 입력이나 스크롤 같은 이벤트가 너무 자주 발생해 성능이 떨어질 수 있다.

이때 등장하는 두 가지 해결 방법이 바로 디바운스(Debounce)와 쓰로틀(Throttle)인데

이는 성능 최적화에 사용되는 기법이다.

 

얼핏 보면 이 둘은 되게 비슷해 보이는데, 각각의 사용 목적과 동작 방식을 알면

각 상황에 맞는 최적의 기법을 찾아서 적용할 수 있다.

 

복잡한 코드를 작성하지 않고도 lodash라는 JavaScript 라이브러리에서 디바운스와 쓰로틀을 간편하게 사용할 수 있지만

이번 글에선 순수 JavaScript로 작성된 코드를 기반으로 동작 원리를 살펴보자.


🔍 디바운스(Debounce)?

"일정 시간이 지난 후 마지막 이벤트만 실행하는 방법"

예를 들어, 사용자가 입력을 멈춘 후 일정 시간이 지나야만 함수가 실행된다.

 

자주 사용되는 예시

- 검색어 자동완성

- 윈도우 사이즈 변경(resize) 이벤트

 

✅ 디바운스 동작 원리

const debounce = (func, delay) => {
  let timeout;
  return (...args) => {
    clearTimeout(timeout); // 기존 타이머 취소
    timeout = setTimeout(() => func(...args), delay); // 새 타이머 예약
  };
};

const handleSearch = debounce((search) => {
  console.log('입력된 검색어:', search);
}, 300);

 

1. 타이머 설정

    이벤트가 발생하면 지정한 delay 시간 동안 타이머를 설정한다.

2. 타이머 초기화

    delay 시간 내에 동일한 이벤트가 다시 발생하면 이전 타이머를 취소하고 새 타이머를 설정한다.

3. 함수 실행

    delay 시간 동안 추가 이벤트가 발생하지 않으면 마지막 이벤트에 대한 함수가 실행된다.

 

❓ clearTimeout(timeout)이 기존 타이머를 취소할 수 있는 이유

1. handleSearch는 debounce 함수가 반환한 함수임.

2. timeout 변수는 debounce 함수의 스코프 내에서 유지되며, 반환된 함수가 이 변수에 접근할 수 있음.

3. timeout 변수는 debounce 함수가 호출될 때만 초기화되고 반환된 함수가 호출될 때 마다 이 변수를 사용함.

 

따라서 handleSearch는 debounce가 반환한 함수이며, timeout 변수는 클로저를 통해 유지되기 때문이다.

 

✏️ 디바운스 실습 예제

// debounce.js

const debounce = (func, delay) => {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), delay);
  };
};

const debouncedFunction = debounce(() => {
  console.log("✅ Debounce 실행됨!");
}, 2000);

console.log("🚀 debouncedFunction 예제 시작");
setTimeout(() => debouncedFunction(), 0); // 첫 번째 요청 (0초)
setTimeout(() => debouncedFunction(), 2100); // 두 번째 요청 (2.1초)
setTimeout(() => debouncedFunction(), 3000); // 세 번째 요청 (3초)

debounce.js의 실행 결과를 예상해보자.

 

1. 첫 번째 실행 (0ms 요청)

    - setTimeout(1000ms) 예약됨.

    - 1초 후 "✅ Debounce 실행됨!" 출력됨.

2. 두 번째 실행 (1000ms 요청)

    - 기존 setTimeout(1000ms)이 실행된 후 100ms 뒤 발생.

    - 기존 타이머가 실행된 후이므로, 새로운 setTimeout(1000ms)이 예약됨.

    - 즉, 실행이 취소되지 않고 다시 실행된다.

3. 세 번째 실행 (2000ms 요청)

    - 기존 setTimeout(1000ms)이 실행되기 전에 호출됨.

    - 기존 타이머가 취소되고, 새로운 setTimeout(1000ms)이 예약됨.

    - 3초 후 실행됨

debounce.js 실행 결과

이러한 Debounce 기법을 사용하면 검색어 입력 시 자동완성 키워드를 가져오는 API 요청이 있을 때

불필요한 네트워크 요청을 줄일 수 있다.


🔍 쓰로틀(Throttle)이란?

"일정 시간 동안 이벤트가 여러 번 발생하더라도, 일정한 간격으로만 이벤트를 실행하는 방법"

예를 들어, 스크롤 이벤트가 발생하더라도 200ms마다 한 번만 함수를 실행할 수 있다.
(스크롤을 내리는 동안 계속 실행되는게 아니라 정해진 시간 간격동안 한번만 함수를 실행한다)

 

자주 사용되는 예시

- 무한 스크롤(Infinite Scrolling)

- 스크롤 위치에 따른 애니메이션 효과

- 버튼 클릭의 연속 실행 제한

 

✅ 쓰로틀 동작 원리

const throttle = (func, delay) => {
  let timer;
  return function (...args) {
    if (!timer) {
      func.apply(this, args); // 즉시 실행
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    }
  };
};

  // ✅ Throttle 적용 (버튼 클릭)
  const handleClick = throttle(() => {
    console.log("Throttle 버튼 클릭!");
  }, 1000);

1. 타이머 설정

    - 이벤트가 발생하면 함수를 즉시 실행하고 setTimeout()을 이용해 타이머를 설정한다.

2. 실행 제한 (타이머 유지)

    - timer가 설정된 동안(delay 시간이 지나기 전까지) 새로운 호출이 들어와도 함수는 실행되지 않는다. (무시됨)

3. 타이머 초기화 (다시 실행 가능)

    - delay 시간이 지나면 timer는 null로 초기화되므로 다시 이벤트가 발생하면 즉시 실행된다.

 

✏️ 쓰로틀 실습 예제

 

import "./styles.css";
import { useState, useEffect, useRef } from "react";

// ✅ Throttle 함수 (정해진 간격마다 실행)
const throttle = (func, delay) => {
  let timer;
  return function (...args) {
    if (!timer) {
      func.apply(this, args); // 즉시 실행
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    }
  };
};

export default function App() {
  const [throttleCount, setThrottleCount] = useState(0);
  const throttledRef = useRef(null);

  // ✅ Throttle 적용 (버튼 클릭)
  useEffect(() => {
    throttledRef.current = throttle(() => {
      setThrottleCount((prev) => prev + 1);
      console.log("Throttled 버튼 클릭!");
    }, 1000);
  }, []);

  return (
    <div>
      <h1>Throttle 실습</h1>
      <button onClick={() => throttledRef.current()}>
        Throttle 버튼 (연타해도 1초마다 1회 실행)
      </button>
      <p>Throttle 클릭 횟수: {throttleCount}</p>
    </div>
  );
}

 

이 예제는 버튼 클릭에 쓰로틀을 적용한 예제이다.

따라서 버튼을 빠르게 클릭해도 1초에 한 번씩만 실행되도록 제한된다.


📌 결론

  디바운스 (Debounce) 쓰로틀(Throttle)
정의 일정 시간이 지난 후 마지막 이벤트만 실행 일정 간격마다 한 번씩만 실행
특징 이벤트 발생이 멈춘 후 일정 시간 이후 실행 이벤트가 연속해서 발생해도 일정한 간격으로 실행
타이머
동작 방식
새로운 이벤트 발생 시
기존 타이머 취소 후 새로 설정
일정 시간 동안 추가 실행을 막고
시간이 지나면 다시 실행 가능
사용 예시 검색어 자동완성, 창 크기 resize 이벤트 스크롤 이벤트, 연속 클릭 방지, API 요청 제한

 

✅ 디바운스(Debounce)를 사용해야 하는 경우 (예시)

👉 사용자의 마지막 입력을 기준으로 실행해야 하는 경우

- 검색어 자동완성

  - 사용자가 입력을 멈춘 후 API 요청 실행

- 윈도우 크기 조정 이벤트

  - 창 크기 조정이 끝난 후 UI 업데이트

- 폼 입력 검증

  - 사용자가 입력을 마친 후 유효성 검사 실행

 

✅ 쓰로틀(Throttle)을 사용해야 하는 경우 (예시)

👉 일정한 간격으로 실행해야 하는 경우 (이벤트 발생 빈도 조절)

- 스크롤 이벤트

  - 스크롤 시 너무 자주 발생하는 이벤트 최적화

- 무한 스크롤

  - API 요청이 너무 자주 발생하는 것을 방지

- 마우스 이동 이벤트

  - 마우스가 움직이는 동안 일정 간격으로만 실행

반응형