쓰로틀
연속으로 발생하는 이벤트를 일정 시간 간격마다 최대 한 번만 실행되도록 제한하는 기법입니다.
동작 원리
이벤트 발생 타임라인 (간격: 200ms)
이벤트: ● ● ● ● ● ● ● ● ● ● ● ● ● ●
| | | |
실행: ○ ○ ○ ○ (200ms 간격으로 주기적 실행)
첫 이벤트 발생 시 즉시 실행하고, 이후 설정한 간격이 지나기 전까지 추가 이벤트는 무시합니다.
대표 사용 사례
| 사용 사례 | 설명 |
|---|---|
| 스크롤 이벤트 | 스크롤 위치에 따른 UI 업데이트 (헤더 고정, 애니메이션 등) |
| 무한 스크롤 | 일정 간격으로 스크롤 위치를 체크하여 데이터 추가 로딩 |
| 마우스 이동 추적 | 드래그, 그리기 등에서 과도한 이벤트 호출 방지 |
| 실시간 대시보드 | 빈번한 데이터 업데이트를 일정 주기로 제한 |
| 게임 입력 처리 | 연속 클릭/키 입력의 실행 빈도 제한 |
JavaScript 구현
기본 구현
function throttle(fn, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// 사용 예: 스크롤 이벤트
window.addEventListener('scroll', throttle(() => {
console.log('스크롤 위치:', window.scrollY);
}, 200));
타이머 기반 구현 (trailing 지원)
function throttle(fn, interval, { leading = true, trailing = true } = {}) {
let lastTime = 0;
let timer = null;
return function (...args) {
const now = Date.now();
const remaining = interval - (now - lastTime);
if (remaining <= 0) {
clearTimeout(timer);
timer = null;
lastTime = now;
if (leading) fn.apply(this, args);
} else if (!timer && trailing) {
timer = setTimeout(() => {
lastTime = Date.now();
timer = null;
fn.apply(this, args);
}, remaining);
}
};
}
React 예시: 무한 스크롤
import { useState, useEffect, useCallback } from 'react';
function InfiniteList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const handleScroll = useCallback(
throttle(() => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
if (scrollHeight - scrollTop - clientHeight < 200) {
setPage(prev => prev + 1);
}
}, 300),
[]
);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [handleScroll]);
useEffect(() => {
fetch(`/api/items?page=${page}`)
.then(res => res.json())
.then(data => setItems(prev => [...prev, ...data]));
}, [page]);
return (
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
디바운스 vs 쓰로틀 상세 비교
| 항목 | 디바운스 (Debounce) | 쓰로틀 (Throttle) |
|---|---|---|
| 핵심 전략 | 마지막 이벤트 기준으로 지연 실행 | 일정 주기마다 실행 허용 |
| 이벤트 중 실행 | X 실행하지 않음 | O 주기적으로 실행 |
| 실행 보장 | 이벤트가 멈춰야 실행 | 이벤트 중에도 주기적 실행 |
| 중간 상태 반영 | X 최종 상태만 반영 | O 중간 상태도 반영 |
| 대표 사례 | 검색 입력, 폼 저장 | 스크롤, 리사이즈, 드래그 |
5초 동안 연속 이벤트 발생 시 (간격: 1초)
디바운스: ○ → 총 1회
쓰로틀: ○ ○ ○ ○ ○ → 총 5회
선택 가이드
이벤트 처리 전략 선택
사용자 입력이 "멈춘 후" 처리해야 하는가?
├── YES → 디바운스 (검색, 자동 저장)
└── NO
└── 이벤트 "도중에도" 주기적 반응이 필요한가?
├── YES → 쓰로틀 (스크롤, 드래그, 리사이즈)
└── NO → 일반 이벤트 핸들러
실무 팁:
resize나scroll이벤트는 초당 수십~수백 회 발생할 수 있어, 쓰로틀 없이 처리하면 렌더링 성능이 급격히 저하됩니다. 보통 100~300ms 간격이 사용자 경험과 성능 사이의 적절한 균형점입니다.