Throttling & Debouncing
짧은 시간 간격으로 연속해서 이벤트가 발생했을 때 과도한 이벤트 핸들러 호출을 방지하는 기법
Throttling
짧은 시간 간격으로 연속해서 발생한 이벤트들을 일정시간 단위 (delay) 로 그룹화하여 처음 또는 마지막 이벤트 핸들러만 호출되도록 하는것.
주로 사용되는 예 : 무한스크롤
Debouncing
짧은 시간 간격으로 연속해서 이벤트가 발생하면 이벤트 핸들러를 호출하지 않다가 마지막 이벤트로부터 일정 시간(delay) 이 경과한 후에한번만 호출하도록 하는 것
주로 사용되는 예 : 입력값 실시간 검색, 화면 resize 이벤트
메모리 누수(Memory Leak)
필요하지 않은 메모리를 계속 점유하고 있는 현상
setTimeout 이 메모리 누수를 유발하는지?
- 하나의 페이지에서 페이지 이동없이 setTimeout 을 동작시키고 함수가 종료될때까지 기다리면 누수는 없으나, 페이지 이동을 하게되면 타이머는 여전히 메모리를 차지하고 있게 됨.
App.jsx
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>
);
}
1. 쓰로틀링
쓰로틀링 버튼을 누르고 가만히 있으면 2초 지난후에 요청을 받음.
쓰로틀링 버튼을 누르고 페이지 이동을 하면 기존요청에서 아무것도 리턴하지 않음 (추가 요청을 받지 않음)
2. 디바운싱
디바운싱 버튼을 반복해서 누르면 그 사이에는 실행되지 않고, 마지막요청 기준으로 2초 이후에 실행됨.
lodash
lodash 를 사용하면, useCallback 이 있어야함.
useCallback 을 통해 마운트 시에, debounce 를 기억하게되면 이 클로저 함수는 외부 함수의 변수에 계속해서 참조하여 갖고있기 때문에 타이머 아이디를 기억할 수 있게 됨.
import "./App.css";
import { useState, useCallback } from "react";
import _ from "lodash";
function App() {
const [searchText, setSearchText] = useState("");
const [inputText, setInputText] = useState("");
const handleSearchText = useCallback(
_.debounce((text) => setSearchText(text), 2000),
[]
);
const handleChange = (e) => {
setInputText(e.target.value);
handleSearchText(e.target.value);
};
return (
<div
style={{
paddingLeft: 20,
paddingRight: 20,
}}
>
<h1>디바운싱 예제</h1>
<br />
<input
placeholder="입력값을 넣고 디바운싱 테스트를 해보세요."
style={{ width: "300px" }}
onChange={handleChange}
type="text"
/>
<p>Search Text: {searchText}</p>
<p>Input Text: {inputText}</p>
</div>
);
}
export default App;
'React' 카테고리의 다른 글
[React] 인증/ 인가 (세션) (0) | 2023.07.05 |
---|---|
[React] 인증/ 인가 (쿠키) (0) | 2023.07.05 |
[React] React Query (0) | 2023.07.05 |
[React] custom hooks (0) | 2023.07.05 |
[React] Thunk (0) | 2023.07.04 |