🚀 디바운스(Debounce) vs 쓰로틀(Throttle) 완벽정리
웹 개발을 하다보면 사용자의 입력이나 스크롤 같은 이벤트가 너무 자주 발생해 성능이 떨어질 수 있다.
이때 등장하는 두 가지 해결 방법이 바로 디바운스(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 기법을 사용하면 검색어 입력 시 자동완성 키워드를 가져오는 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 요청이 너무 자주 발생하는 것을 방지
- 마우스 이동 이벤트
- 마우스가 움직이는 동안 일정 간격으로만 실행