[TIL] debounce & Throttle 구현해보기

박창조·2025년 9월 23일

TIL

목록 보기
5/6

📌 도입

Debounce 와 throttle은 무수히 많은 이벤트로 인해 CPU에 무리가 가는 상황이 발생했을 때, 성능 최적화를 위해 사용되는 프로그래밍 기법이다.

두 기법 모두 “연속적으로 발생하는 이벤트의 실행 빈도를 줄여 불필요한 함수 호출과 리소스 소모를 방지해주는 기법” 으로 동일한 목적을 가지고 있다.

하지만, 최적화를 처리하는 방식에서 차이를 보인다. 각각 어떻게 다른지, 그리고 어떻게 구현할 수 있는지 알아보자.

📌 Debounce 와 Throttle 의 의미와 예시

Debounce

“연속적으로 발생하는 이벤트를 하나의 그룹으로 묶어 처리하는 방식”

마지막 이벤트가 발생한 후 일정 시간이 경과했을 때만 함수를 실행하는 기법이다.

주요 특징

  • 이벤트 발생 후 지정된 대기 시간 동안 추가 이벤트가 없을 때만 실행
  • 연속적인 호출에서 마지막 호출만 유효하게 처리
  • 불필요한 API 요청을 완전히 차단하여, 리소스 부하 감소

사용 예시

  • 검색 자동완성 (300-500ms )
  • 폼 입력 검증 (500ms )
  • 자동 저장 기능 (1000 ~ 2000ms)
  • 버튼 중복 클릭 방지 (300ms)

Throttle

*“일정한 시간 간격만 함수를 실행하는 방식”*

이벤트가 연속적으로 발생하더라도, 설정된 주기마다 한 번씩만 함수가 실행되도록 한여, 일정한 간격의 처리를 보장한다.

주요 특징

  • 지정된 시간 간격마다 주기적으로 실행
  • 연속적인 이벤트에 대해서도 일정 수준의 처리를 보장
  • 실시간성이 중요한 이벤트에 적합

사용 예시

  • 무한 스크롤 (200 ~ 500ms)
  • 스크롤 위치 추적 (100ms)
  • 애니메이션 제어 (16ms → 60fps)
  • 창 크기 조절 감지 (250ms)

debounce, throttle timeline 비교 이미지

📌 구현

Debounce

구현 인사이트

  • 클로저
  • setTimeout 을 활용한 타이머 제어
  • this를 통한 컨텐스트 유지

구현 포인트

  • timeoutId 변수를 클로저로 유지하여 타이머 참조 관리
  • 매 호출마다 clearTimeout으로 기존 타이머를 취소
  • apply를 사용하여 원본 함수의 this 컨텍스트를 유지

구현 코드

function debounce(func: (...args: any[]) => void, delay: number) {
  // 클로저로 타이머 변수 저장
  let timeout: NodeJS.Timeout;

  // 인자를 받아서 타이머 설정
  return function (...args: any[]) {
    clearTimeout(timeout);
    // 타이머 설정
    timeout = setTimeout(() => func.apply(this, args), delay);
  };
}

Throttle

구현 인사이트

  • 상태 플래그 활용한 실행 제어
  • this 바인딩
  • setTimeout을 활용한 콜백 전달

구현 포인트

  • inThrottle 플래그로 실행 가능 상태 관리
  • 첫 번째 호출은 즉시 실행, 이후 지정된 시간동안 차단
  • 시간 경과 후 플래그 초기화

구현 코드

function throttle(func: (...args: any[]) => void, limit: number) {
  let inThrottle = false;

  return (...args: any[]) => {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;

      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

📌 실무 적용 가이드

  1. 주의 할점
  • React 컴포넌트에서 단순히 debounce, throttle을 사용하면 리렌더링마다 새로운 함수가 생성되기 때문에 효과가 의미 없어진다. 따라서 useCallback을 활용한 메모이제이션이 필요하다.
  • 메모리 누수 방지 : React 와 같은 컴포넌트 기반의 선언형 라이브러리에서는 언마운트시 타이머를 정리해주어야 한다.
  1. 적절한 딜레이 설정
  • 너무 짧으면 효과가 거의 없고, 너무 길면 사용자가 끊어지는 경험을 하기 때문에 상황에 따른 적절한 시간 적용이 필요
  1. 라이브러리 활용

이를 제공하는 주요 라이브러리는 많지만, 대표적인 두가지 lodash와 es-toolkit에 대한 비교이다.

항목lodashes-toolkit
성능표준적, 충분히 빠름평균 2~3배 더 빠름
번들 크기상대적으로 큼최대 97% 더 작음
트리 셰이킹lodash-es는 지원, 기본 lodash는 미흡매우 정밀하게 지원
타입 지원타입스크립트 지원, 일부 any 혼용안전하고 견고한 타입 제공
테스트 커버리지널리 검증됨100% 커버리지, 신뢰성 강조
생태계/호환성매우 넓음, 레거시 코드 호환최신 환경에 최적화, 일부 함수 미지원
사용법오랜 기간 표준, 자료 풍부lodash와 거의 동일, 문서화 진행중

es-toolkit이 번들 사이즈, 성능 측면에서 더 큰 이점을 가져오기 때문에, es-toolkit을 사용하는 것이 최신 프로젝트에서는 더 좋을 것 같다.

📌 Reference

[JS] 디바운싱(Debouncing)과 쓰로틀링(Throttling)

profile
사랑을 꿈꾸는 냄새나는 개발자 입니다 :)

0개의 댓글