짧은 시간 간격으로 연속해서 이벤트가 발생했을 때 과도한 이벤트 핸들러 호출을 방지하는 기법이다.
짧은 시간 간격으로 연속해서 발생한 이벤트들을 일정시간 단위(delay)로 그룹화하여 처음 또는 마지막 이벤트 핸들러만 호출되도록 하는 것
ex) 무한스크롤
짧은 시간 간격으로 연속해서 이벤트가 발생하면 이벤트 핸들러를 호출하지 않다가 마지막 이벤트로부터 일정시간(delay)이 경과한 후에 한번만 호출하도록 하는 것
ex) 화면 resize 이벤트
필요하지 않은 메모리를 계속 점유하고 있는 현상
ex) 리액트로 만든 SPA 웹사이트는 페이지 이동 시 컴포넌트가 언마운트 된다. 그런데 페이지 이동 전에 setTimeout으로 인해 타이머가 동작중인 상태에서 clearTimeout을 안 해주고 페이지 이동 시 컴포넌트는 언마운트 되었음에도 불구하고 타이머는 여전히 메모리를 차지하고 동작하고 있다. 이러한 상황이 메모리 누수에 해당한다고 볼 수 있다.
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
export default function Home() {
// const [state, setState] = useState(false);
const navigate = useNavigate();
let timerId = null;
// Leading Edge Throttling
const throttle = (delay) => {
if (timerId) {
// timerId가 있으면 바로 함수 종료
return;
}
// setState(!state);
console.log(`API요청 실행! ${delay}ms 동안 추가요청 안받음`);
timerId = setTimeout(() => {
console.log(`${delay}ms 지남 추가요청 받음`);
// alert("Home / 쓰로틀링 쪽 API호출!");
timerId = null;
}, delay);
};
// Trailing Edge Debouncing
const debounce = (delay) => {
if (timerId) {
// 할당되어 있는 timerId에 해당하는 타이머 제거
clearTimeout(timerId);
}
timerId = setTimeout(() => {
// timerId에 새로운 타이머 할당
console.log(`마지막 요청으로부터 ${delay}ms지났으므로 API요청 실행!`);
timerId = null;
}, delay);
};
useEffect(() => {
return () => {
// 페이지 이동 시 실행
if (timerId) {
// 메모리 누수 방지
clearTimeout(timerId);
}
};
}, [timerId]);
return (
<div style={{ paddingLeft: 20, paddingRight: 20 }}>
<h1>Button 이벤트 예제</h1>
<button onClick={() => throttle(2000)}>쓰로틀링 버튼</button>
<button onClick={() => debounce(2000)}>디바운싱 버튼</button>
<div>
<button onClick={() => navigate("/company")}>페이지 이동</button>
</div>
</div>
);
}
throttle함수와 debounce함수가 어떻게 구동되는지 천천히 상상해보자.