[React] Debounce && Throttle: 개념, 장단점, 예시
` React에서 Debounce와 Throttle에 대해서 알아보자 `
React 어플리케이션에서 사용자 입력이나 이벤트가 빈번하게 발생 시 , 성능 문제를 야기 할 수 있다.
예를 들어 , 검색창에 한 글자 입력 할 때마다 API 호출을 하거나, 스크롤 이벤트 처리할 때마다 복잡한 계산을 수행한다면 불필요한 리소스 낭비가 발생할 수 있다.
이런 문제를 해결하기 위해서 Debounce와 Throttle을 사용할 수 있다.
1. Debounce
1) 개념, 작동방식
기본적으로 debounce와 Throttle 모두 특정 함수의 실행을 제한하는 목적을 두고 있는데,
이 녀석은 특정 기간 안의 함수 실행을 모두 취소하고 마지막에만 실행한다
디바운스는 지정해놓은 시간 동안 다시 한번 함수가 재호출되면 기존의 함수를 취소하고 재호출된 함수가 정해놓은 시간 동안 기다리고 실행한다. 즉 연달아 발생하는 이벤트 중 마지막 이벤트만 지정된 시간 후에 실행 된다.
2) 장단점
장점 | 단점 |
API 호출 및 불필요한 렌더링 최소화: 사용자가 입력을 멈출 때까지 기다렸다가 한 번만 액션을 수행하므로,서버 부하를 줄이고 불필요한 UI업데이트를 막을 수 있다. | 즉각적인 피드백 부족: 사용자가 특정 동작을 수행하고 나서 일정 시간 대기해야만 결과가 나타나므로, 즉각적인 피드백이 중요한 상황에서는 사용자 경험이 저하될 수 있다. |
정확한 최종 상태 반영: 연속적인 이벤트 중 최종 상태만을 반영하기에, 사용자의 최종 의도에 맞는 동작을 수행 가능 | 긴 대기 시간 설정 시 불편함: Debounce 시간을 너무 길게 설정하면 사용자가 답답함을 느낄 수 있다. |
3) 예시
import React, {useState, useEffect } from 'react';
function DebounceExample() {
const [searchTerm, setSearchTerm ] = useState('')
const [debouncedSearchTerm, setDebouncedSearchTerm ] = useState('');
//Debounce 로직을 위한 useEffect
useEffect(() => {
const handler = setTimeout(() => {
// 1000ms 동안 추가 입력 없으면 debouncedSearchTerm 업데이트
setDebouncedSearchedTerm(searchTerm);
}, 1000);
//cleanup 함수: searchTerm이 변경될 때마다 이전 타이머를 취소
return () => {
clearTimeout(handler);
};
}, [searchTerm]); // searchTerm이 변경될 때만 useEffect 실행
const handleChange = (event) => {
setSearchTerm(event.target.value);
};
return (
<div>
<h2>Debounce 예시: 검색어 입력 </h2>
<input
type="text"
placeholder="검색어를 입력하세요"
value={searchTerm}
onChange={handleChange}
/>
<p>현재 입력 중인 검색어: {searchTerm} </p>
<p>디바운스된 검색어 (api 호출 대상):{debouncedSearchTerm}</p>
</div>
);
}
// 사용자가 input에 텍스트 입력할 때마다 searchTerm은 즉시 업데이트 되는데 debounceSearchTerm은 사용자가 1000ms동안 입력을 멈췄을 때만 업데이트 됨
2. Throttle
1) 개념, 작동방식
Throttel은 debounce와 반대의 의미인데 , 디바운스가 특정기간 동안 다시 한번 함수가 재호출되면 기존의; 함수를 취소 후, 재호출된 함수가 지정해놓은 시간 동안 기다리고 실행되는반면
Throttel은 함수 호출 되면 바로 실행되어 버리고, 특정 기간 동안 다시 한번 함수가 재호출되면 마지막 함수를 취소한다.
상단의 이미지를 보면 throttel은 한번의 주기동안 처음에 한번 실행되고 주어진 시간동안 뒤에 일어나는 이벤트는 무시하고 시간이 지나고 나서야 새로운 이벤트를 실행한다. 이때 debounce와 다른 점은, 새로운 이벤트가 발생하더라도 타이머를 초기화하지 않는다.
2) 장단점
장점 | 단점 |
안정적인 이벤트 처리 빈도 : 일정한 간격으로 이벤트를 발생하므로, UI 업데이트나 특정 로직 실행의 빈도를 예측 가능하게 한다. | 최종이벤트 누락 가능성: 마지막 이벤트가 쿨타임 내에 발생하면 해당 이벤트는 누락될 수 있다. |
리소스 효율적 사용: 과도한 이벤트 발생으로 인한 성능 저하를 방지하면서, 일정 간격으로 피드백을 제공한다. | 즉각적인 반응성 저하: Debounce보다는 덜하지만, 그래도 일정 시간 동안은 이벤트가 무시될 수 있으므로 즉각적인 반응이 필요한 경우에는 적합하지 않을 수 있다. |
3) 예시
import React, { useState, useEffect } from 'react';
function Throttle() {
const [scrollCount, setScrollCount ] = useState(0); // 스크롤 이벤트 처리 횟수
const [isThrottling, setIsthrottling ] = useState(false) // 쿨타임 중인지 나타내는
useEffect(() => {
const handleScroll =() => {
//쿨타임 중이면 함수 실행을 중단
if (isThrottling) {
returnl
}
//쿨타임 시작: true로 설정하고, 일정 시간 후 false로 해제
setIsThrottling(true);
setScrollCount(prevCount => prevCount + 1);
console.log(`크롤 이벤트 처리됨! 총 ${scrollCount + 1}회`); //실제 스크롤 로직
//300ms 후에 쿨타임 해제
setTimeout (() => {
setIsThrottling(false);
}, 300);
};
//컴포넌트 마운트 시 스크롤 이벤트 리스너 추가
window.addEventListener('scroll', handleScroll);
//컴포넌트 언마운트 시 이벤트 리스너 제거 (cleanup)
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, [isThrottling]); // isThrottling 상태가 변경될 때마다 useEffect가 다시 실행
return (
<div style={{ height: '2000px', background: 'linear-gradient(to bottom, #fbc2eb, #a6c1ee)' }}>
<h2>Throttle 예시 (with useState)</h2>
<p style={{ position: 'fixed', top: '20px', left: '20px', backgroundColor: 'white', padding: '10px' }}>
스크롤 이벤트 처리 횟수: <strong>{scrollCount}</strong>
<br/>
현재 쿨타임 중: {isThrottling ? 'YES' : 'NO'}
</p>
<p style={{ marginTop: '500px' }}>아래로 스크롤하여 0.3초마다 한 번씩만 숫자가 올라가는 것을 확인하세요.</p>
</div>
);
}