React에서 Debounce와 Throttle 패턴
입력 지연과 빈도 제한을 통해 React 애플리케이션의 응답성과 비용을 개선하는 디바운스와 쓰로틀 구현 패턴 모음
목차
개요
입력 최적화 리액트 환경에서 Debounce와 Throttle은 자주 발생하는 이벤트를 제어하는 핵심 패턴이다. 두 기법은 목적과 동작 방식이 다르므로, 상황에 맞게 선택하는 것이 중요하다. 이 글에서는 개념 비교부터 직접 쓸 수 있는 구현 예제, 실전에 가까운 사용법까지 단계별로 설명한다.
Debounce와 Throttle의 차이
Debounce (디바운스)
Debounce는 마지막 이벤트 발생 후 지정한 시간 동안 추가 호출이 없을 때 한 번만 실행하는 방식이다. 주로 검색 입력처럼 사용자가 입력을 멈춘 뒤에 API 호출을 하고 싶을 때 적합하다.
Throttle (쓰로틀)
Throttle은 일정 시간 간격으로 이벤트를 제한해서 실행한다. 스크롤이나 리사이즈처럼 연속 이벤트가 발생해도 초당 호출 횟수를 제어하고 싶을 때 유용하다.
언제 어떤 것을 선택할까
- 검색 입력: react debounce 구현을 사용해 불필요한 API 호출을 줄인다.
- 스크롤 감지나 위치 추적: throttle 사용법 react에 따라 호출 빈도를 제한한다.
- 폼 유효성 검사: 입력 최적화 리액트 방식으로 debounce를 활용하면 사용자 경험 개선에 도움된다.
간단한 구현
순수 자바스크립트로 구현한 debounce와 throttle 예제이다.
function debounce(fn, wait = 300) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, wait);
};
}
function throttle(fn, limit = 200) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
}
React Hook으로 만들기
React에서 재사용 가능하게 만들면 관리가 편해진다. 아래는 useDebounce와 useThrottle의 예시 구현이다.
import { useRef, useEffect } from 'react';
export function useDebounce(callback, delay) {
const ref = useRef();
useEffect(() => {
ref.current = callback;
}, [callback]);
return function(...args) {
if (ref.timer) clearTimeout(ref.timer);
ref.timer = setTimeout(() => ref.current(...args), delay);
};
}
export function useThrottle(callback, limit) {
const ref = useRef({ last: 0, cb: null });
useEffect(() => {
ref.current.cb = callback;
}, [callback]);
return function(...args) {
const now = Date.now();
if (now - ref.current.last >= limit) {
ref.current.last = now;
ref.current.cb(...args);
}
};
}
사용 예시: 검색 입력
검색 입력에서 debounce를 적용하면 API 호출 횟수가 크게 줄어든다. 아래 예시는 useDebounce를 사용한 컴포넌트 구조이다.
import React, { useState } from 'react';
import { useDebounce } from './hooks';
function Search() {
const [q, setQ] = useState('');
const debouncedSearch = useDebounce((val) => {
// fetch(`/api/search?q=${val}`)...
console.log('search', val);
}, 400);
return (
<input
value={q}
onChange={(e) => {
setQ(e.target.value);
debouncedSearch(e.target.value);
}}
placeholder="검색어 입력"
/>
);
}
사용 예시: 스크롤 이벤트
스크롤이나 리사이즈처럼 빈번한 이벤트에는 throttle 사용이 적절하다. 호출 빈도를 제한해 렌더와 계산 부하를 줄인다.
import React, { useEffect } from 'react';
import { useThrottle } from './hooks';
function ScrollTracker() {
useEffect(() => {
const onScroll = useThrottle(() => {
console.log('scroll position', window.scrollY);
}, 200);
window.addEventListener('scroll', onScroll);
return () => window.removeEventListener('scroll', onScroll);
}, []);
return <div>스크롤 상태 추적중</div>;
}
주의사항과 최적화 포인트
- 클린업: 컴포넌트 언마운트 시 타이머나 이벤트 리스너 제거가 필요하다.
- 의존성 관리: Hook 내부에서 참조하는 값이 변경되면 콜백 업데이트가 필요하다.
- 지연 시간 조정: 너무 길면 응답성이 떨어지고, 너무 짧으면 효과가 미미하다.
- 서버 비용 고려: 입력 최적화 리액트 전략으로 불필요한 네트워크 요청을 줄이면 비용 절감에 기여한다.
결론
react debounce 구현과 throttle 사용법 react은 목적에 따라 선택하면 입력 처리와 성능 개선에 큰 도움이 된다. 검색처럼 마지막 동작을 기다리는 경우에는 Debounce, 지속적인 상태 체크에서는 Throttle이 적합하다. 구현은 단순하지만, 컴포넌트 생명주기와 의존성 관리를 염두에 두면 안정적으로 활용할 수 있다.