[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -1,97 +1,201 @@
|
||||
---
|
||||
id: wiki-2026-0508-throttling-debouncing
|
||||
title: Throttling Debouncing
|
||||
title: Throttling Debouncing
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [P-Reinforce-AUTO-92BBDE]
|
||||
aliases: [Throttle, Debounce, Rate Limiting Frontend]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
tags: [auto-reinforced]
|
||||
verification_status: applied
|
||||
tags: [frontend, performance, event-handling, javascript]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-04-20
|
||||
github_commit: "[P-Reinforce] Continuous Worker - Throttling Debouncing"
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: typescript
|
||||
framework: vanilla
|
||||
---
|
||||
|
||||
# [[Throttling Debouncing|Throttling Debouncing]]
|
||||
# Throttling Debouncing
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
> 스크롤, 화면 크기 조절(Resize), 타이핑 등 짧은 시간 동안 과도하게 발생하는 사용자 이벤트의 실행 빈도를 제어하여 애플리케이션의 성능 병목 현상을 방지하는 최적화 기법입니다.
|
||||
## 매 한 줄
|
||||
> **"매 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.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
**1. 필요성 및 성능 영향** 사용자의 화면 크기 조절, 키보드 타이핑, 마우스 스크롤 같은 이벤트는 아주 짧은 시간에 수십~수백 번씩 연속적으로 발생합니다. 이 이벤트마다 무거운 연산이나 React의 상태 업데이트를 수행하면 매 틱(tick)마다 불필요한 리렌더링이나 재계산이 발생하여 심각한 성능 병목(Bottleneck) 현상을 초래할 수 있습니다.
|
||||
## 매 핵심
|
||||
|
||||
**2. 디바운싱 (Debouncing)** 연이어 발생하는 이벤트를 하나로 묶어, 특정 시간 동안 추가적인 이벤트가 발생하지 않을 때 마지막에 단 한 번만 이벤트 핸들러를 실행하는 기법입니다. 예를 들어 `lodash` 라이브러리의 `debounce` 함수를 사용하여 사용자가 창 크기 조절을 멈춘 뒤 200ms가 지났을 때만 리사이즈 로직이 처리되도록 제한할 수 있습니다.
|
||||
### 매 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).
|
||||
|
||||
**3. 스로틀링 (Throttling)** 이벤트가 연속해서 발생하더라도 정해진 시간 간격 당 최대 한 번씩만 함수가 실행되도록 보장하는 기법입니다. (이전 대화 참고: Web Worker로 마우스의 위치를 지속적으로 포워딩할 때, 메시지 큐에 과부하가 걸리지 않도록 주기를 조절하는 데 유용하게 사용됩니다.)
|
||||
### 매 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).
|
||||
|
||||
**4. 구현 시 번들 최적화 (Tree Shaking) 주의사항** 이러한 기법을 적용하기 위해 `lodash`와 같은 외부 유틸리티 라이브러리를 사용할 때는 번들 크기 최적화에 주의해야 합니다. `import _ from 'lodash';`와 같이 전체 라이브러리를 불러오면 번들 크기가 크게 증가하므로, `import { debounce } from 'lodash-es';` 또는 `import debounce from 'lodash-es/debounce';` 형태로 필요한 개별 함수만 가져와 트리 쉐이킹(Tree Shaking)이 완벽히 적용되도록 작성해야 합니다.
|
||||
### 매 응용
|
||||
1. Search-as-you-type: debounce 300ms.
|
||||
2. Infinite scroll trigger: throttle 100ms.
|
||||
3. Auto-save: debounce 1s with maxWait 10s.
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
- **과거 데이터와의 충돌:** 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
|
||||
- **정책 변화:** Programming & Language 분야의 자동 자산화 수행.
|
||||
## 💻 패턴
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
- **Related Topics:** [[React Performance Optimization|React Performance Optimization]], Tree Shaking (번들 크기 최적화), 이벤트 포워딩 (Event Forwarding), [[불필요한 리렌더링 방지|불필요한 리렌더링 방지]]
|
||||
- **Projects/Contexts:** 실시간 검색 입력(Typing) 지연 처리, [[반응형 윈도우 리사이즈(Resize) 이벤트 처리|반응형 윈도우 리사이즈(Resize) 이벤트 처리]], 웹 워커 통신 지연 최소화
|
||||
- **Contradictions/Notes:** 스로틀링과 디바운싱은 무거운 연산을 줄이고 프레임 드랍을 막아주지만, 설정한 대기 시간이 너무 길면 화면 업데이트가 늦어져 사용자에게 끊기는 느낌을 줄 수 있습니다. 따라서 작업의 성격(예: 즉각 반응이 필요한 드래그, 멈춘 후 반응해도 되는 윈도우 리사이즈)에 맞추어 적절한 지연 시간을 타협하는 것이 중요합니다.
|
||||
### Basic debounce (trailing)
|
||||
```typescript
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
_Last updated: 2026-04-14_
|
||||
|
||||
---
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
// Usage
|
||||
const onSearch = debounce((q: string) => fetchResults(q), 300);
|
||||
input.addEventListener('input', e => onSearch((e.target as HTMLInputElement).value));
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Basic throttle (leading + trailing)
|
||||
```typescript
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Debounce with cancel + flush
|
||||
```typescript
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### React hook: useDebouncedValue
|
||||
```typescript
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
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;
|
||||
}
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
// 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)} />;
|
||||
}
|
||||
```
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
### rAF throttle (60fps cap)
|
||||
```typescript
|
||||
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
|
||||
```typescript
|
||||
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]]
|
||||
- 응용: [[Search Autocomplete]] · [[Infinite Scroll]] · [[Auto Save]]
|
||||
- Adjacent: [[requestAnimationFrame]] · [[Web Workers]] · [[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) |
|
||||
|
||||
Reference in New Issue
Block a user