웹 애플리케이션에서 사용자에 의해 발생하는 이벤트는 매우 빠른 속도로, 대량으로 발생할 수 있다. 예를 들어, 창 크기를 조절하거나, 입력 창에 글자를 입력하거나, 스크롤바를 움직이는 경우 등이 있다. 이러한 상황에서 이벤트를 최적화하지 않고 그대로 처리하면 성능 저하를 초래할 수 있다.
이를 해결하기 위해 프론트엔드에서는 자주 발생하는 이벤트를 제어하여 성능을 최적화하는 기법을 사용한다. 그 대표적인 방법이 바로 Debounce와 Throttle이다. 이 두 기술은 다음과 같은 공통점을 가지고 있다.
이제 각각의 기능의 특징과 차이점을 알아보자.
Debounce는 이벤트가 연속적으로 발생할 때, 일정 시간 동안 이벤트가 더 이상 발생하지 않을 때 마지막으로 감지된 이벤트를 실행시킨다. 이를 통해 너무 자주 발생하는 이벤트를 제어할 수 있으며. 예를 들어, 사용자가 검색창에 글자를 입력할 때, 마지막 글자를 입력하고 일정 시간 후에만 서버에 요청을 보낼 수 있다.
import { useState, useEffect } from 'react';
export const useDebounce = <T>(value: T, delay: number = 500): T => {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay)
// 클린업 함수로 타이머를 제거
return () => {
clearTimeout(timer)
}
}, [value, delay])
return debouncedValue
}
위와 같이 제너릭 타입으로 구현하면, ts환경에서 debounce를 적용시킬 값의 타입을 유동적으로 대입시켜줄 수 있다.
Throttle는 이벤트가 여러 번 발생해도 일정 시간 동안 한 번만 이벤트 핸들러가 호출되도록 하는 기법이다. 주로 스크롤 이벤트나 리사이즈 이벤트처럼 자주 발생하는 이벤트의 처리 빈도를 제한할 때 사용된다. 예를 들어, 사용자가 스크롤할 때 매번 이벤트를 처리하지 않고 일정 간격으로만 처리하고자 할 때 유용하다.
import { useRef, useEffect } from 'react';
const useThrottle = (callback: (...args: any[]) => void, delay: number = 500) => {
const lastCall = useRef<number>(0);
return (...args: any[]) => {
const now = new Date().getTime();
if (now - lastCall.current >= delay) {
callback(...args);
lastCall.current = now;
}
};
}
export default useThrottle;
debounce는 이벤트가 끝난 후 일정 시간이 지나기 전에 이벤트 핸들러가 호출되지 않도록 하기에, 주로 입력 필드와 같이 이벤트 빈도가 높은 경우에 사용된다.
throttle는 일정 시간 간격으로 이벤트 핸들러가 호출되도록 한다. 주로 스크롤, 리사이즈와 같은 빈번한 이벤트에 사용된다.
핵심은, debounce의 이벤트 호출 시점은 특정 이벤트 호출 작업이 모두 종료된 뒤 이고, throttle은 일정한 시간 간격으로 이벤트 호출이 실행된다는 것이다.