22sook00 logo
SookDev

debounce 와 throttle

tag
javascript
date
Jul 15, 2023
짧은 시간에 유저 입력 등의 이벤트가 많이 일어나는 경우 화면이 끊길 수 있다. 그래서 이런 경우 특별한 최적화가 필요하다.
📢
디바운스와 쓰로틀링 둘 다 DOM 이벤트를 기반으로 실행하는 자바스크립트 이벤트를 제어하는 방법.
이벤트 핸들러가 많은 연산(예 : 무거운 계산 및 기타 DOM 조작)을 수행하는 경우 에 대해 제약을 걸어 제어할 수 있는 수준으로 이벤트를 발생(해당 이벤트를 더 적게 실행하면 빠져 나갈 수 있음)시키는 것을 목표로 하는 기술, 즉 시간이 지남에 따라 함수를 몇번이나 실행 할 지 제어하는 기술.
ex ) 리사이즈 / 키보드입력 / 스크롤 / 드래그
가장 대표적인 예로 스크롤 이벤트가 있다. 스크롤 막대를 드래깅 할 시 몇백개 이상의 이벤트가 발생할 가능성이 있고 그에따른 콜백이 발생하여 리소스를 크게 잡아먹어 성능저하를 일으킬 수 있다.
 
물론 lodash 라는 라이브러리를 이용하여 사용할 수 있다. 하지만 단순 디바운스와 쓰로틀링을 위하여 install 하는 것은 무겁고 비효율적이기 때문에 직접 hook 또는 함수로 구현하여 사용할 수 있다.

🧩 debounce

서브 타이틀

이벤트를 그룹화 하여 특정시간이 지난 후 하나의 이벤트만 발생하도록 하는 기술.
순차적 호출을 하나의 그룹으로 “단일/그룹화" 할 수 있다. 연이어 호출되는 함수들 중 마지막함수 ( 또는 맨 처음) 호출하도록 하는 것.
만약 구글맵 API 와 같이 유료서비스를 이용 시 불필요한 쿼리를 여러번 날리면 손해 이므로,
비용적인 부분과도 관련이 되어 있다.
setTimeout 을 이용하여 내가 지정한 시간이 지나면 이벤트가 일어나도록 제어할 수 있다.
총 두가지 종류의 디바운스 훅을 만들어 보았다.

hook

const useDebounce: FC<debounceProps> = ({ value, delay }) => { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value]); return debouncedValue; };
//call 방식 const debounceText = useDebounce({ value: searchText1, delay: 500 }); useEffect(() => { // Do fetch here... // Triggers when "debouncedValue" changes }, [debounceText]); ... <Input handleValue={handleSearchDebounceHook} />
👉🏻 해당 방식은 내가 입력한 value 가 끝나면 이벤트가 일어나게 되므로서,
이메일이나 패스워드 검증 시 실시간으로 확인 할 때 유용하다. 하지만 이또한 디바운스를 걸더라도 해당 값이 맞는지 안맞는지 확인을 해야 하기 때문에 검증할때마다 리렌더링이 일어나는것은 마찬가지다.

function

const useDebounceFunc = (func: any, wait: number) => { let timeout: NodeJS.Timeout | null; return (...args: any) => { // eslint-disable-next-line @typescript-eslint/no-this-alias const context = this; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; func.apply(context, args); }, wait); }; }; export default useDebounceFunc;
//call 방식 const handleSearchDebounceFunc = useDebounceFunc( (e: React.ChangeEvent<HTMLInputElement>) => { setSearchText2(e.target.value); }, 500, ); ... <Input handleValue={handleSearchDebounceFunc} />
👉🏻 해당 방식은 value 값에 직접적으로 시간제어를 걸어주는것이 아니라 해당 이벤트가 담겨있는 함수 자체를 제어해주고 있기 때문에 처음 작성한 hook 과는 감지시간 차이가 있지만 이벤트가 완전히 멈춘 후 한번만 작동하기 때문에 첫번째보다 성능상 더 좋다.
만약 입력폼을 즉각적인 검증없이 입력해야 하는 경우라면 두번째 함수사용이 더 유용하다.

🧩 throttle

서브 타이틀

이벤트를 일정한 주기로 발생하도록 하는 기술
마지막함수가 호출된 후 일정시간이 지나기 전, 실행 횟수에 제한을 걸어 다시호출되지 않도록 하는 것.
스크롤 / 무한스크롤 에 많이 이용하며, 디바운스를 이용한다면 스크롤을 멈출때만 이벤트가 발생되기 때문에 스로틀이 더 적합할 수 있다.
setInterval 을 이용하여 일정 시간동안 일어나도록 제어할 수 있다.
const [btnStatus, setBtnStatus] = useState<boolean>(false); const handleScroll = () => { if (window.scrollY > 400) { setBtnStatus(true); } else { setBtnStatus(false); } }; useEffect(() => { const timer = setInterval(() => { window.addEventListener("scroll", handleScroll); }, 500); return () => { clearInterval(timer); window.removeEventListener("scroll", handleScroll); }; }, []);

🧩🧩 결론

🧸
debounce는 검색어 입력, 버튼 중복 클릭 방지 처리 에 적합, throttle은 스크롤이벤트에 더 적합!
 

📜 참조