블로그로 돌아가기
Javascript

javascript throttle 구현: 실무 적용 가이드와 예제 모음

javascript throttle 구현을 통해 이벤트 빈도 제어와 렌더링 비용을 줄이는 실무 기법을 정리합니다. 핵심 개념, 코드 예제, 비교표, 체크리스트까지 바로 적용 가능한 내용으로 구성했습니다.

throttledebounce이벤트성능실무패턴
브라우저에서 이벤트를 제어하는 javascript throttle 구현 예시를 보여주는 다이어그램

javascript throttle 구현이 필요한 상황

javascript throttle 구현은 사용자 입력(스크롤, 리사이즈, 마우스 이동 등)이나 자주 발생하는 비동기 호출을 일정 주기로 제한해 브라우저 렌더링과 서버 요청 부담을 줄이는 방법입니다. 이 글에서는 개념 이해부터 실무 적용 예제, leading/trailing 옵션 설명, 성능·보안·운영 관점의 주의점, 체크리스트까지 제공해 바로 적용할 수 있도록 구성합니다.

javascript throttle 구현 핵심 개념

Throttle은 일정 시간(window) 동안 최초 또는 마지막 호출만 허용해 이벤트 처리 빈도를 제한합니다. Debounce와 달리 이벤트가 계속 발생해도 주기적으로 함수를 실행하므로 호흡이 필요한 실시간 피드백(예: 스크롤 위치 동기화, 실시간 위치 업데이트)에 적합합니다.

  • 기본 동작: 호출 시점에서 마지막 실행 시간과 비교해 허용 시간 경과 시 즉시 실행.
  • 옵션: leading(첫 호출 즉시 실행), trailing(마지막 호출 실행) 조합으로 동작을 제어.

javascript throttle 구현 실무 예제

아래는 가장 기본적인 throttle 구현 예제입니다. 옵션으로 leading/trailing을 지원합니다.

// throttle.js function throttle(fn, wait = 200, options = { leading: true, trailing: true }) { let lastCallTime = 0; let timeoutId = null; let lastArgs = null; return function (...args) { const now = Date.now(); if (!lastCallTime && options.leading === false) lastCallTime = now; const remaining = wait - (now - lastCallTime); lastArgs = args; if (remaining <= 0) { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } lastCallTime = now; fn.apply(this, args); } else if (!timeoutId && options.trailing) { timeoutId = setTimeout(() => { timeoutId = null; lastCallTime = options.leading === false ? 0 : Date.now(); fn.apply(this, lastArgs); }, remaining); } }; } // 사용 예 const onScroll = throttle((e) => { // 스크롤 처리 로직 console.log('throttled scroll', window.scrollY); }, 100); window.addEventListener('scroll', onScroll);

실무 팁: React/Framework 내에서는 ref 또는 useCallback으로 래핑해 동일 함수 참조를 유지하세요. 그렇지 않으면 addEventListener/removeEventListener 시 등록/해제가 달라져 누적될 수 있습니다.

leading/trailing 옵션과 실제 동작 비교

옵션에 따라 동작 차이가 크게 발생합니다. 다음 표는 세 가지 설정의 행동을 요약합니다.

구분항목1항목2항목3
내용leading: true, trailing: true — 즉시 실행 + 마지막 실행 보장leading: true, trailing: false — 즉시 실행만, 마지막 호출 무시leading: false, trailing: true — 첫 호출 지연, 주기적 마지막 호출 실행

requestAnimationFrame을 활용한 throttle

애니메이션 관련 이벤트(스크롤, 마우스 이동)에서는 requestAnimationFrame(rAF) 기반 throttle이 더 부드러운 렌더링을 제공합니다. rAF는 브라우저의 재페인트 주기와 동기화되어 레이아웃 정합성에 유리합니다.

function rafThrottle(fn) { let ticking = false; return function (...args) { if (!ticking) { ticking = true; requestAnimationFrame(() => { fn.apply(this, args); ticking = false; }); } }; } const onMouseMove = rafThrottle((e) => { // 부드러운 마우스 추적 });

사용 사례: 고빈도 UI 업데이트(스크롤 위치 동기화, 패럴럭스 등) — rAF 기반이 좋습니다. 서버 호출 빈도 제어에는 setTimeout 기반 throttle을 사용하세요.

언제 쓰면 좋고 언제 피해야 할까

언제 쓰면 좋은가:

  • 빈번히 발생하는 이벤트(스크롤, 리사이즈, 마우스무브)에서 렌더링/연산을 줄이고 싶을 때
  • 서버로의 중복 요청을 제한해 비용을 낮추고 싶을 때
  • 실시간성이 조금은 허용되는 UI 업데이트(예: 위치 동기화) 필요할 때

언제 피해야 하는가:

  • 각 호출의 결과가 즉각적으로 필요하고 지연이 허용되지 않는 경우(예: 결제 버튼 중복 방지와는 다른 엄격한 제어 필요)
  • 아주 짧은 지연(수 ms 수준)도 사용자 경험을 크게 저해하는 상황
  • 정확한 마지막 상태가 반드시 즉시 반영되어야 하는 경우(trailing만 의존 시 상태 지연 발생 가능)

실무 체크리스트

  • 이벤트 종류에 따라 rAF vs setTimeout 방식 선택했는가?
  • throttle 함수의 참조가 변경되지 않도록(useCallback/ref) 처리했는가?
  • leading/trailing 옵션이 요구 UX를 만족시키는가?
  • 서버 호출 제한 시 백엔드 rate-limit과 중복되지 않게 설계했는가?
  • 메모리 누수(이벤트 리스너 누락) 여부를 확인했는가?

체크리스트 예시:

  • 함수 참조 유지(useCallback/ref)
  • cleanup에서 removeEventListener 수행
  • 서버 측 재시도와 중복 요청 정책 점검

성능·보안·운영 주의점

성능:

  • 지나치게 짧은 wait 값은 throttle 효과를 잃게 하고 오히려 오버헤드를 증가시킬 수 있음.
  • 너무 긴 wait 값은 UX 지연을 유발하므로 사용자 기대에 맞게 튜닝 필요.
  • rAF는 화면 재페인트 주기와 동기화되어 레이아웃 스로틀에 유리하지만 서버 호출 제어에는 부적합.

보안/운영:

  • 클라이언트에서의 throttle 만으로 서버 과부하를 완전히 방지할 수 없음. 서버 측 rate-limit을 병행하세요.
  • 민감한 액션(결제/인증)은 클라이언트 throttle뿐 아니라 서버에서 중복 방지 로직(아이디empotency token 등)을 구현하세요.
  • 로그/모니터링: throttle 도입 후 이벤트 빈도와 실패율 변화를 모니터링해 이상 징후를 감지하세요.

자주 묻는 질문

Q1: throttle과 debounce 중 언제 선택해야 하나요? A1: debounce는 마지막 이벤트 이후 일정 시간 무활동 시 한 번 실행이 필요할 때(검색 입력 등)에 적합합니다. throttle은 주기적 업데이트가 필요한 경우(스크롤, 윈도우 리사이즈) 적합합니다.

Q2: React에서 throttle을 사용할 때 주의할 점은 무엇인가요? A2: 컴포넌트 리렌더링마다 throttle 함수가 재생성되면 이벤트 리스너가 누적될 수 있습니다. useCallback이나 ref로 함수 참조를 고정하고 useEffect cleanup에서 removeEventListener를 호출하세요.

Q3: trailing 옵션만 켜두면 마지막 호출이 무조건 실행되나요? A3: trailing이 true이면 마지막 호출을 예약하지만, 컴포넌트가 언마운트되거나 타이머가 클리어되면 실행되지 않을 수 있습니다. 필요 시 언마운트 시 즉시 처리 로직을 추가하세요.

Q4: 서버 요청을 throttle로 막으면 완전 안전한가요? A4: 아니요. 클라이언트 우회나 여러 클라이언트로 인한 동시 요청은 서버에 영향을 줄 수 있습니다. 서버 측 rate limiting/검증을 병행해야 합니다.

정리

  • javascript throttle 구현은 이벤트 빈도 제어로 성능 개선과 중복 요청 감소에 유용합니다.
  • leading/trailing, rAF vs setTimeout 선택은 사용 사례에 따라 달라집니다.
  • React 등 프레임워크에서는 함수 참조 유지와 cleanup이 필수입니다.
  • 서버 측 보호와 모니터링을 병행해 운영 위험을 줄이세요.
  • 체크리스트를 통해 도입 전후 행동을 점검하세요.