Files
2nd/10_Wiki/Topics/Frontend/Throttling Debouncing.md
T
Antigravity Agent f8b21af4be Wiki cleanup: error-doc removal, dedup merge, link normalization
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>
2026-05-20 23:52:15 +09:00

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
Throttle
Debounce
Rate Limiting Frontend
none A 0.9 applied
frontend
performance
event-handling
javascript
2026-05-10 pending
language framework
typescript vanilla

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).

매 응용

  1. Search-as-you-type: debounce 300ms.
  2. Infinite scroll trigger: throttle 100ms.
  3. 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

🤖 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)