본문 바로가기

JavaScript

[JavaScript] 디바운스와 쓰로틀링 구현하고 활용하기

들어가며

안녕하세요! 오늘은 프론트엔드 개발자라면 반드시 알아야 할 디바운스(Debounce)와 쓰로틀링(Throttling)에 대해 알아볼게요.
스크롤 이벤트나 검색 입력 등에서 성능 최적화가 필요하다면, 이 두 기법은 여러분의 강력한 무기가 될 거예요! 😊

디바운스와 쓰로틀링이 필요한 이유

먼저, 문제 상황을 한번 볼까요?

// 😱 이런 코드는 성능 저하의 지름길입니다!
window.addEventListener('scroll', () => {
  // 스크롤할 때마다 실행되는 무거운 작업
  doSomethingHeavy();
});

// 🤔 검색창에서도 문제가 발생해요
searchInput.addEventListener('input', () => {
  // API 호출이 입력할 때마다 발생!
  searchAPI(input.value);
});

디바운스(Debounce) 구현하기

디바운스는 마지막 호출 이후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 기법이에요.

// ✨ 깔끔한 디바운스 구현
function debounce(callback, delay) {
  let timerId = null;

  return (...args) => {
    // 이전 타이머가 있다면 제거
    if (timerId) clearTimeout(timerId);

    // 새로운 타이머 설정
    timerId = setTimeout(() => {
      callback(...args);
    }, delay);
  };
}

// 🎯 실제 사용 예시
const searchInput = document.querySelector('#search');
const handleSearch = debounce((value) => {
  console.log('API 호출:', value);
  // 실제 API 호출 로직
}, 500);

searchInput.addEventListener('input', (e) => {
  handleSearch(e.target.value);
});

쓰로틀링(Throttling) 구현하기

쓰로틀링은 정해진 시간 동안 최대 한 번만 실행되도록 하는 기법이에요.

// ✨ 깔끔한 쓰로틀링 구현
function throttle(callback, limit) {
  let waiting = false;

  return (...args) => {
    if (!waiting) {
      callback(...args);
      waiting = true;

      setTimeout(() => {
        waiting = false;
      }, limit);
    }
  };
}

// 🎯 실제 사용 예시
const handleScroll = throttle(() => {
  console.log('스크롤 이벤트 처리!');
  // 실제 스크롤 처리 로직
}, 300);

window.addEventListener('scroll', handleScroll);

실전 활용 사례

1. 실시간 검색 구현하기

// 🔍 사용자 경험을 고려한 실시간 검색
const searchWithDebounce = debounce(async (value) => {
  try {
    const results = await searchAPI(value);
    updateSearchResults(results);
  } catch (error) {
    console.error('검색 중 오류 발생:', error);
  }
}, 400); // 0.4초 대기

searchInput.addEventListener('input', (e) => {
  // 입력 중임을 표시
  showLoadingIndicator();
  searchWithDebounce(e.target.value);
});

2. 무한 스크롤 구현하기

// 📜 부드러운 무한 스크롤
const handleInfiniteScroll = throttle(() => {
  const { scrollTop, scrollHeight, clientHeight } = document.documentElement;

  if (scrollTop + clientHeight >= scrollHeight - 100) {
    loadMoreContent();
  }
}, 500); // 0.5초마다 최대 한 번 실행

window.addEventListener('scroll', handleInfiniteScroll);

3. 실시간 저장 기능

// 💾 자동 저장 기능
const autoSave = debounce(async (content) => {
  try {
    await saveToServer(content);
    showSaveSuccess();
  } catch (error) {
    showSaveError();
  }
}, 1000); // 마지막 입력 1초 후 저장

editor.addEventListener('input', (e) => {
  showSavingIndicator();
  autoSave(e.target.value);
});

🚨 주의사항과 팁

1. 클린업 함수 구현하기

function debouncedFunction() {
  const debounced = debounce(() => {
    // 로직
  }, 300);

  // 컴포넌트가 언마운트될 때 클린업이 필요해요!
  return () => {
    clearTimeout(debounced.timerId);
  };
}

2. React에서 활용하기

// ⚛️ React Custom Hook으로 만들기
function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(timer);
    };
  }, [value, delay]);

  return debouncedValue;
}

성능 비교

// 📊 일반 이벤트 vs 최적화된 이벤트

// 처리된 이벤트 수 비교
let normalCount = 0;
let optimizedCount = 0;

// 일반 이벤트
window.addEventListener('scroll', () => normalCount++);

// 쓰로틀링 적용
window.addEventListener('scroll', 
  throttle(() => optimizedCount++, 100)
);

// 1분 후 결과 확인
setTimeout(() => {
  console.log(`일반 이벤트 호출 수: ${normalCount}`);
  console.log(`최적화된 호출 수: ${optimizedCount}`);
}, 60000);

마치며

디바운스와 쓰로틀링은 프론트엔드 최적화의 기본이면서도 강력한 도구예요.
상황에 맞게 적절히 활용한다면, 여러분의 애플리케이션은 훨씬 더 효율적이고
사용자 친화적으로 동작할 거예요! 😊

항상 기억하세요:

  • 디바운스: 연이은 호출을 하나로 묶을 때 (예: 검색, 저장)
  • 쓰로틀링: 일정 주기로 호출을 제한할 때 (예: 스크롤, 리사이즈)

즐거운 코딩 되세요! 🚀