Throttle and Debounce
Use 'throttle' and 'debounce' to improve performance.
1. Why are they necessary? π©βπ»
μμ¦ κ°λ°μ Reactive Programming μ΄ λμΈκ° λλ©° Event Listening κΈ°λ°μ νλ‘κ·Έλλ°μ΄ λ§μμ‘λ€. μ΄λ¬ν μ΄λ²€νΈ κΈ°λ° νλ‘κ·Έλλ°μ μ’μ UX λ₯Ό μ 곡ν μ μμΌλ κ³Όλν Event λ μ€νλ € μ±λ₯ μ νλ‘ μ΄μ΄μ§κ³ , UX μ λμ μν₯μ λ―ΈμΉ μ μλ€.
Throttle κ³Ό Debounce κ° μ£Όλ‘ μ¬μ©λλ λνμ μΈ μλ₯Ό ν΅ν΄ μ΄ν΄λ³΄μ.
1. Throttle
Throttle μ μ΅μ μ¬μ
λ ₯ μκ°
μ μ£Όλ κ²κ³Ό κ°λ€. κ°μ₯ μ¬μ΄ μλ‘λ ν€λ³΄λ μ€μ μ μλ ν€ λ°λ³΅ μλλ€. ν€ λ°λ³΅ μλκ° λ리면
λμΌ μκ° ν€λ₯Ό λλ₯΄κ³ μμ΄λ λ°λ³΅ μλκ° λΉ λ₯Ό λλ³΄λ€ μ€μ μ
λ ₯μ΄ μ κ² λλ€.
μΉμμλ μ΄λ€ κ²½μ°μ μ¬μ©ν κΉ?
κ°μ₯ λ§μ΄ μ¬μ©λλ μλ λ°λ‘ λΈλΌμ°μ κ΄λ ¨ μ΄λ²€νΈλ€. νλ©΄μ μ€ν¬λ‘€ νκ±°λ νλ©΄μ 리μ¬μ΄μ¦ νλ κ²½μ°μ λ°μνλ μ΄λ²€νΈλ 리μ€λμ μ½μμ μΆλ ₯ν΄λ³΄λ©΄ μμ²λκ² λ§μ μ΄λ²€νΈκ° μμ λλ κ²μ μ μ μλ€. νμ§λ§ μ΄ μλ§μ μ΄λ²€νΈλ₯Ό λͺ¨λ μ²λ¦¬νλ©΄ μ€νλ € μ±λ₯ λ¬Έμ κ° μκΈ°κ³ μ΄λ‘ μΈν΄ μ’μ§ μμ UX κ° λμ΄λ²λ¦°λ€.
μμμ΄ λ°μλλ μ΄λ²€νΈλ₯Ό μΌμ μ£ΌκΈ°λ§λ€ μ²λ¦¬νκΈ° μν΄ Throttle μ²λ¦¬λ₯Ό ν΄μ£Όλ©΄ Reactive Programming μ μ μ§νλ©΄μ μ²λ¦¬ νμλ₯Ό μ€μ¬ μμ²λ μ±λ₯ κ°μ μ ν μ μλ€.
μ΄ μΈμλ κ²μμ°½μ ν€λ³΄λ μ λ ₯μλ§λ€ μλμμ± λͺ©λ‘μ λΆλ¬μ€λ κ²½μ°μλ μΌμ μ λ ₯ μ£ΌκΈ°λ§λ€ κ²°κ³Όλ₯Ό λ³κ²½ν΄ μ±λ₯ κ°μ μ ν μ μκ³ , Post μ κ°μ μμ²μ΄ μλ μ¬λ¬ λ² μ μ‘μ΄ κ°λ₯ν Get μμ²μ κ²½μ° κ³ μ₯λ λ§μ°μ€ λ±μΌλ‘ μΈν΄ μ°μμΌλ‘ 2λ² μμ²μ΄ λ μ μλ κ²½μ°μ Throttle μ μΆκ°ν΄ μ¬μ©μ μλκ° μλ λΆνμν μ€λ³΅ μμ²μ λ§λ λ° μ¬μ©λκΈ°λ νλ€.
2. Debounce
Debounce λ μ€νμ μ§μ°
μν€λ ν¨κ³Όλ₯Ό λΈλ€. Throttle μ 첫 λ²μ§Έ μ€νμ μ¦μ μ΄λ£¨μ΄μ§κ³ , μ΄ν μμ²κ±΄μ λν΄ μΌμ μκ° λμ
μμ²μ Block μν¨λ€. λ°λΌμ μΌμ μκ°μ΄ μ§λλ©΄ μ¬μμ²μ΄ κ°λ₯νλ€. λ°λ©΄ Debounce λ μ°μλ μμ²κ±΄μ΄ λ€μ΄μ¬ λ μΌμ μκ° μμ²μ΄
μ€λ¨λλ©΄ μ€νμ νλ€. λ°λΌμ μ€ν μ§μ° μκ°λ³΄λ€ 짧μ μ£ΌκΈ°λ‘ μ§μμ μΈ μμ²κ±΄μ΄ λ°μνλ©΄ κ³μν΄μ μ€νμ νμ§ μλλ€.
μ΄κ² μμ κ²μμ°½μ μ£Όλ‘ μ¬μ©λλ€. Throttle κ°μ κ²½μ°λ μ€κ° μ λ ₯ κ²°κ³Όκ° μλ―Έκ° μλ κ²½μ° μ¬μ©μμκ² μ§μμ μΈ νΌλλ°±μ μ£ΌκΈ° μν΄ μ¬μ©λμ§λ§ μ΄ κ²½μ°λ μ€κ° μ λ ₯ κ²°κ³Όκ° λ¬΄μλ―Έν κ²½μ° μ¬μ©νλ©΄ μ’λ€. μ΄μ°¨νΌ μ€κ° μ λ ₯ κ²°κ³Όκ° λ¬΄μλ―Ένλ€λ©΄ κ΅³μ΄ λ§μ μμ²μ λ³΄λΌ νμκ° μκΈ° λλ¬Έμ΄λ€.
μν κ²μμ μλ‘ λ€μ΄λ³΄μ. κ²μμ°½μ μνμ μ λͺ©μ μ λ ₯ν λ μ¬μ©μμ μ λ ₯μ λκΈ° μν΄ κ²μ ν μ€νΈ μλμμ±μ Throttle μ μ΄μ©ν΄ μ£ΌκΈ°μ μΌλ£ μμ²μ 보λ΄λλ‘ μ²λ¦¬νλ©΄ μ’λ€. νμ§λ§ ν μ€νΈμ λ¬λ¦¬ μ΄λ―Έμ§λ₯Ό λΆλ¬μ€λ κ²μ λ§μ μμ λ°μ΄ν°λ₯Ό νμλ‘ νλ©°, μ¬μ©μλ μμ§ κ²μμ μν΄ μν μ λͺ©μ μ λ ₯μ€μ΄κΈ° λλ¬Έμ μλμ μΌλ‘ μν ν¬μ€ν°μ λν κ΄μ¬λλ λ¨μ΄μ§λ€. λ°λΌμ μν ν¬μ€ν°λ μ¬μ©μκ° μΌμ μκ° μ λ ₯μ΄ μ μ§λμμ λ ν λ²μ© μ λ°μ΄νΈ νλλ‘ νλ κ²μ΄ λμ± ν¨μ¨μ μ΄λ€. λ°λΌμ μ΄ κ²½μ° Denounce λ₯Ό μ¬μ©ν΄ μ²λ¦¬νλ©΄ μ±λ₯μ λμ΄κ³ ν΅μ λ° μλ² λΉμ©μ μ€μΌ μ μλ€.
2. Implementation - Throttle π©βπ»
λ΄λΆ μ»€λ§ ν¨μκ° ν¨μ μ μΈμμ΄λ ννμμ΄λμ λ°λΌ this
binding μ΄ νμν΄ μ‘°κΈ λ€λ₯΄κ² ꡬνν΄μΌνλ€. JavaScript μ
TypeScript λ²μ μΌλ‘ κ°κ° ꡬν λ΄μ©μ μ΄ν΄λ³Έλ€.
1. JavaScript
- Function Declaration
export const throttle = (fn, delay = 500) => {
let available = true;
return function () {
if (available) {
available = false;
fn.apply(this, arguments);
setTimeout(() => {
available = true;
}, delay);
}
};
};
- Function Expression
export const throttle = (fn, delay = 500) => {
let available = true;
return (...args) => {
if (available) {
available = false;
fn(...args);
setTimeout(() => {
available = true;
}, delay);
}
};
};
2. TypeScript
- Function Declaration
export const throttle = (fn: Function, delay = 500) => {
let available = true;
return function $fn() {
if (available) {
available = false;
fn.apply($fn, arguments);
setTimeout(() => {
available = true;
}, delay);
}
};
};
TypeScript μμ
apply
λ₯Ό μ μν λthis
λ₯Ό μ¬μ©νλ©΄ Lint μμany
Types λ₯Ό κ°κΈμ μ¬μ©νμ§ λ§λΌκ³ μλ €μ£Όλ κ² λλ¬Έμ// @ts-ignore
λ₯Ό λͺ μνκ±°λthis
κ° μλ μ νν λͺ μλ λμμ μ 곡ν΄μΌνλ€.무μνλλ‘ μ£Όμ μ²λ¦¬λ₯Ό νλ κ² λ³΄λ€λ
$fn
μ΄λΌλ μ΄λ¦μ μ£Όλ κ²μΌλ‘ μ²λ¦¬νλ€.
- Function Expression
export const throttle = (fn: Function, delay = 500) => {
let available = true;
return (...args: unknown[]) => {
if (available) {
available = false;
fn(...args);
setTimeout(() => {
available = true;
}, delay);
}
};
};
λ€μκ³Ό κ°μ΄ μ¬μ©νλ€.
const throttledFoo = throttle(foo)
const throttledBar = throttle(bar, 2000)
3. Implementation - Debounce π©βπ»
λ΄λΆ μ»€λ§ ν¨μκ° ν¨μ μ μΈμμ΄λ ννμμ΄λμ λ°λΌ this
binding μ΄ νμν΄ μ‘°κΈ λ€λ₯΄κ² ꡬνν΄μΌνλ€. JavaScript μ
TypeScript λ²μ μΌλ‘ κ°κ° ꡬν λ΄μ©μ μ΄ν΄λ³Έλ€.
1. JavaScript
- Function Declaration
export const debounce = (fn, delay = 500) => {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
};
- Function Expression
export const debounce = (fn, delay = 500) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, delay);
};
};
2. TypeScript
- Function Declaration
export const debounce = (fn: Function, delay = 500) => {
let timer: ReturnType<typeof setTimeout>;
return function $fn() {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply($fn, arguments);
}, delay);
};
};
TypeScript μμ
apply
λ₯Ό μ μν λthis
λ₯Ό μ¬μ©νλ©΄ Lint μμany
Types λ₯Ό κ°κΈμ μ¬μ©νμ§ λ§λΌκ³ μλ €μ£Όλ κ² λλ¬Έμ// @ts-ignore
λ₯Ό λͺ μνκ±°λthis
κ° μλ μ νν λͺ μλ λμμ μ 곡ν΄μΌνλ€.무μνλλ‘ μ£Όμ μ²λ¦¬λ₯Ό νλ κ² λ³΄λ€λ
$fn
μ΄λΌλ μ΄λ¦μ μ£Όλ κ²μΌλ‘ μ²λ¦¬νλ€.
- Function Expression
export const debounce = (fn: Function, delay = 500) => {
let timer: ReturnType<typeof setTimeout>;
return (...args: unknown[]) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, delay);
};
};
λ€μκ³Ό κ°μ΄ μ¬μ©νλ€.
const debouncedFoo = debounce(foo)
const debouncedBar = debounce(bar, 2000)
4. Examples π©βπ»
Throttle
Debounce
5. React - Hooks π©βπ»
1. useThrottle, useDebounce
React μμλ μ»΄ν¬λνΈμ Life Cycle κ³Ό λ λλ§μΌλ‘ μΈν΄ μμ ν¨μλ‘ μ¬μ©νλ κ² λ³΄λ€ useState
μ useEffect
λ₯Ό μ¬μ©ν΄
Custom Hooks λ‘ κ΅¬ννλ κ²μ΄ μ’λ€.
κ·Έ μ€ useThrottle κ³Ό useDebounce λ useState
μ value λ₯Ό λ°μμ Debounce, Throttle ν¨κ³Όλ₯Ό μΆκ°νκΈ° μν΄
μ¬μ©λλ€.
- useThrottle.ts
import { useEffect, useRef, useState } from "react";
const useThrottle = <T>(value: T, delay = 500): T => {
const [throttledValue, setThrottledValue] = useState(value);
const available = useRef(true);
useEffect(() => {
if (available.current) {
available.current = false;
setThrottledValue(value);
setTimeout(() => (available.current = true), delay);
}
}, [value, delay]);
return throttledValue;
};
export default useThrottle;
- useDebounce.ts
import { useEffect, useState } from "react";
const useDebounce = <T>(value: T, delay = 500): T => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
};
export default useDebounce;
useThrottle
ν
μ μ€ν¬λ‘€ μ΄λ²€νΈλ μλμ° λ¦¬μ¬μ΄μ¦ μ΄λ²€νΈμ κ°μ κ²μ μ€λ‘νμ κ±°λ λ° μ¬μ©ν μ μλ€.
const [scrollPosition, setScrollPosition] = useState<number>(window.scrollY);
const throttledScrollPosition = useThrottle<number>(scrollPosition, 500);
useDebounce
ν
μ κ°μ μ
λ ₯μ λ°λ₯Έ API μμ² μ§μ°, λ λλ§ μ§μ°κ³Ό κ°μ κ²μ μ¬μ©ν μ μλ€.
const [searchTerm, setSearchTerm] = useState<string>('');
const debouncedSearchTerm = useDebounce<string>(searchTerm, 500);
2. useThrottleFn, useDebounceFn
- useThrottleFn.ts
import { useRef } from "react";
const useThrottleFn = (fn: Function, delay = 500) => {
const available = useRef(true);
return (...args: unknown[]) => {
if (available.current) {
available.current = false;
fn(...args);
setTimeout(() => {
available.current = true;
}, delay);
}
};
};
export default useThrottleFn;
- useDebounceFn.ts
import { useRef } from "react";
const useDebounceFn = (fn: Function, delay = 500) => {
const timer = useRef<ReturnType<typeof setTimeout>>();
return (...args: unknown[]) => {
clearTimeout(timer.current);
timer.current = setTimeout(() => {
fn(...args);
}, delay);
};
};
export default useDebounceFn;
μ¬μ€ ν¨μμ μ€λ‘νμ΄λ λλ°μ΄μ€λ₯Ό μ μ©νλ κ²μ Closures μ μν΄ κ²©λ¦¬λ λ©λͺ¨λ¦¬ 곡κ°μ λ³μκ° μμ νκ² μ μ₯λκΈ° λλ¬Έμ κ΅³μ΄
useThrottleFn
, useDebounceFn
μ μ¬μ©ν νμλ μλ€. κ·Έλ₯ μμμ μμ±ν throttle
μ debounce
μ TypeScript
λ²μ μ μ¬μ©ν΄λ λλ€.