f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.3 KiB
6.3 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-throttling-debouncing | Throttling Debouncing | 10_Wiki/Topics | verified | self |
|
none | A | 0.9 | applied |
|
2026-05-10 | pending |
|
Throttling Debouncing
매 한 줄
"매 throttle 의 rate-limit, debounce 의 quiet-period wait". 두 기법 모두 high-frequency event (scroll/resize/input/keypress) 의 handler call 빈도를 줄이지만, semantics 가 정반대 — throttle 의 every N ms 1 call, debounce 의 last call after N ms idle.
매 핵심
매 Throttle
- Semantic: max 1 call per interval.
- Use: scroll, resize, mousemove — continuous events 의 sampling.
- Variants: leading (immediate first), trailing (last in window), both.
- Visualizable:
XX_XX_XX_XX_X(regular ticks).
매 Debounce
- Semantic: 1 call after last invocation + delay (no calls during burst).
- Use: search input, form validate, autocomplete, window-resize-finish.
- Variants: leading (immediate, then ignore), trailing (default), maxWait (force flush).
- Visualizable:
XXX____X(settles after burst).
매 응용
- Search-as-you-type: debounce 300ms.
- Infinite scroll trigger: throttle 100ms.
- Auto-save: debounce 1s with maxWait 10s.
💻 패턴
Basic debounce (trailing)
function debounce<T extends (...args: any[]) => void>(
fn: T,
ms: number
): (...args: Parameters<T>) => void {
let t: ReturnType<typeof setTimeout> | null = null;
return (...args) => {
if (t) clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
// Usage
const onSearch = debounce((q: string) => fetchResults(q), 300);
input.addEventListener('input', e => onSearch((e.target as HTMLInputElement).value));
Basic throttle (leading + trailing)
function throttle<T extends (...args: any[]) => void>(
fn: T,
ms: number
): (...args: Parameters<T>) => void {
let last = 0;
let t: ReturnType<typeof setTimeout> | null = null;
let lastArgs: Parameters<T> | null = null;
return (...args) => {
const now = Date.now();
const remaining = ms - (now - last);
lastArgs = args;
if (remaining <= 0) {
if (t) { clearTimeout(t); t = null; }
last = now;
fn(...args);
} else if (!t) {
t = setTimeout(() => {
last = Date.now();
t = null;
if (lastArgs) fn(...lastArgs);
}, remaining);
}
};
}
Debounce with cancel + flush
function debounceAdv<T extends (...args: any[]) => void>(fn: T, ms: number) {
let t: ReturnType<typeof setTimeout> | null = null;
let lastArgs: Parameters<T> | null = null;
const debounced = (...args: Parameters<T>) => {
lastArgs = args;
if (t) clearTimeout(t);
t = setTimeout(() => { fn(...lastArgs!); t = null; }, ms);
};
debounced.cancel = () => { if (t) { clearTimeout(t); t = null; } };
debounced.flush = () => {
if (t && lastArgs) { clearTimeout(t); fn(...lastArgs); t = null; }
};
return debounced;
}
React hook: useDebouncedValue
import { useEffect, useState } from 'react';
export function useDebouncedValue<T>(value: T, ms = 300): T {
const [v, setV] = useState(value);
useEffect(() => {
const t = setTimeout(() => setV(value), ms);
return () => clearTimeout(t);
}, [value, ms]);
return v;
}
// Usage
function Search() {
const [q, setQ] = useState('');
const debounced = useDebouncedValue(q, 300);
useEffect(() => { if (debounced) fetchResults(debounced); }, [debounced]);
return <input value={q} onChange={e => setQ(e.target.value)} />;
}
rAF throttle (60fps cap)
function rafThrottle<T extends (...args: any[]) => void>(fn: T) {
let scheduled = false;
let lastArgs: Parameters<T>;
return (...args: Parameters<T>) => {
lastArgs = args;
if (scheduled) return;
scheduled = true;
requestAnimationFrame(() => {
fn(...lastArgs);
scheduled = false;
});
};
}
// Best for scroll/resize on 60-120hz displays
window.addEventListener('scroll', rafThrottle(() => updatePosition()));
lodash equivalents
import { debounce, throttle } from 'lodash-es';
const onResize = throttle(handleResize, 200, { leading: true, trailing: true });
const onSearch = debounce(handleSearch, 300, { maxWait: 1000 });
// Cleanup on unmount
useEffect(() => () => { onResize.cancel(); onSearch.cancel(); }, []);
매 결정 기준
| 상황 | Approach |
|---|---|
| Search input | debounce 200-400ms |
| Scroll handler (positional) | rAF throttle |
| Resize end detection | debounce 150ms |
| Auto-save | debounce 1s + maxWait 10s |
| Rate-limited API call | throttle (leading) |
| Button double-click guard | debounce leading-only |
기본값: lodash debounce/throttle for non-trivial cases; rAF throttle for animation-tied work; useDebouncedValue hook for React inputs.
🔗 Graph
- 부모: Event Handling · Frontend Performance
- 변형: Rate Limiting · Backpressure
- 응용: Infinite Scroll
- Adjacent: requestAnimationFrame · Web Worker (웹 워커) · Reactive Streams
🤖 LLM 활용
언제: input handler optimization, scroll/resize perf, rate-limiting UI events. 언제 X: server-side rate limiting (use token bucket / Redis), animation timing (use rAF directly).
❌ 안티패턴
- Debounce on click button: user feels lag; use throttle leading-only.
- No cleanup on unmount: timer fires on unmounted component → setState warning / leak.
- Throttle 16ms manually: use rAF throttle — sync to display refresh.
- Debounce without maxWait on auto-save: user types continuously → never saves.
- Inline
() => debounce(fn, 300)in render: new function each render, debouncing breaks.
🧪 검증 / 중복
- Verified (lodash docs, MDN event handling, React docs useDeferredValue/useTransition equivalence).
- 신뢰도 A.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — full canonical (debounce/throttle/rAF/React hook + lodash) |