본문 바로가기
Javascript

[JavaScript] lodash의 debounce: cancel / flush

by yuraming 2026. 3. 31.

 프론트엔드 개발자로서 실무를 하다 보면 수많은 상태를 관리하고, 사용자 입력에 실시간으로 반응해야 하는 상황을 마주합니다.

특히 검색어 입력, 무한 스크롤, 윈도우 리사이즈와 같은 이벤트들은 순간에도 수십, 수백 번의 함수 호출을 발생시키곤 합니다.

이런 성능 저하를 해결하기 위해 보통 디바운스(Debounce)를 도입합니다.

하지만 무분별한 디바운스 사용이나 제어되지 않은 설정은 오히려 의도치 않은 버그나 UX 저하를 불러오기도 합니다.

 

"분명 디바운스를 걸었는데 왜 데이터가 꼬이지?" "API가 호출 타이밍이 왜 이상하지"

 

이런 상황에 마주한다면, 디바운스를 제대로 '제어'해야 할 때라고 생각합니다.

오늘은 lodash의 debounce를 더욱 정교하게 만들어주는 cancelflush 메서드를 통해,

성능과 안정성을 모두 잡는 최적화 전략을 정리해 보겠습니다.

성능 최적화의 필수 요소인 Debounce, 그리고 그 실행을 효율적으로 제어할 수 있는 cancel 메서드에 대해 알아보겠습니다.

사용자의 입력을 처리하거나 윈도우 리사이즈 이벤트를 다룰 때, 너무 잦은 함수 호출로 인해 브라우저가 버벅거렸던 경험이 있으신가요? 이를 해결해주는 lodash의 강력한 도구들을 소개합니다.

 


 

1. Debounce란?

Debounce는 짧은 시간 간격으로 연속해서 이벤트가 발생할 때, 이벤트가 끝난 후 일정 시간이 지났을 때만 마지막 함수를 실행.

예를 들어, 실시간 검색 기능을 구현하기 위해 검색창에 글자를 입력할 때마다 끊임없이 API를 호출한다면 서버에 큰 부하가 생길 수 있습니다. 이때 디바운스를 사용하면 사용자가 입력을 멈춘 뒤에만 딱 한 번 API를 호출하게 됩니다.

 


 

2. cancel 메서드가 필요한 이유 ?

단순히 디바운스를 거는 것만으로는 부족할 때가 있습니다.

  • 페이지 전환:
    사용자가 입력을 마치기 전에 다른 페이지로 이동했다면,이미 예약된 디바운스 콜백은 더 이상 실행될 필요없음. (메모리 누수 방지)
  • 초기화:
    특정 액션이 발생했을 때 기존에 대기 중인 작업을 즉시 무효화해야 하는 경우.

이때 사용하는 것이 바로 lodash debounce 객체가 제공하는 .cancel() 메서드입니다.

 


 

3. cancel 예시

가장 흔한 사례인 검색 입력과 컴포넌트 언마운트 시점을 기준으로 예시를 작성해 보겠습니다.

import _ from 'lodash';

// 1. 디바운스 함수 생성 (300ms 대기)
const handleSearch = _.debounce((query) => {
  console.log(`Searching for: ${query}`);
  // 실제 API 호출 로직
}, 300);

// 2. 사용자가 입력을 할 때 호출
const onInputChange = (e) => {
  const value = e.target.value;
  handleSearch(value);
};

// 3. cancel 메서드 활용
// 만약 사용자가 검색 도중 '취소' 버튼을 누르거나 페이지를 나간다면?
const onCancelClick = () => {
  handleSearch.cancel();
  console.log('예약된 검색 호출이 취소되었습니다.');
};

 

 


 

4. React에서 주의할 점

React의 함수형 컴포넌트에서 lodash의 debounce를 사용할 때는

매 렌더링마다 함수 객체가 새로 생성되지 않도록 useMemo나 useCallback을 사용하여 함수를 메모이제이션하는 것이 정석.

import React, { useCallback, useEffect } from 'react';
import _ from 'lodash';

const SearchComponent = () => {
  // useCallback을 통해 디바운스 함수를 유지합니다.
  const delayedSearch = useCallback(
    _.debounce((q) => console.log('API 호출:', q), 500),
    []
  );

  useEffect(() => {
    // 컴포넌트가 언마운트(사라질 때) 예약된 호출을 취소합니다.
    // 이를 통해 불필요한 네트워크 요청이나 상태 업데이트를 막을 수 있습니다.
    return () => {
      delayedSearch.cancel();
    };
  }, [delayedSearch]);

  return (
    <input 
      type="text" 
      onChange={(e) => delayedSearch(e.target.value)} 
      placeholder="검색어를 입력하세요"
    />
  );
};

 

 

 


 

5.  flush 메서드가 필요한 이유 ?

디바운스는 보통 사용자의 행동이 끝날 때까지 기다리지만 가끔은 즉시 실행이 필요한 경우가 있습니다.

  • 저장 로직:
    사용자가 글을 쓰다가 갑자기 '창을 닫거나' '저장'을 눌렀을 때, 디바운스 대기 시간 때문에 마지막 수정 사항이 반영되지 않으면 안되기에, flush를 호출해 대기 중인 저장 로직을 즉시 실행.
  • 즉각적인 피드백:
    특정 조건이 만족되었을 때 남은 대기 시간을 무시하고 결과를 바로 보여줘야 할 때.

 


 

6. flush 예시

import _ from 'lodash';

// 5초마다 자동 저장을 시도하는 디바운스 함수
const saveContent = _.debounce((text) => {
  console.log(`서버에 저장 중: ${text}`);
}, 5000);

// 사용자가 글을 입력할 때마다 호출
const onEditorInput = (e) => {
  saveContent(e.target.value);
};

// [Case 1] 취소 버튼 클릭 시 (cancel)
const onCancelEdit = () => {
  saveContent.cancel(); 
  console.log('작업이 취소되어 자동 저장을 중단합니다.');
};

// [Case 2] 바로 저장 버튼 클릭 시 (flush)
const onSaveNow = () => {
  // 5초를 다 채우지 않았어도, 지금 대기 중인 내용이 있다면 즉시 실행!
  saveContent.flush();
  console.log('대기 중인 작업을 즉시 처리했습니다.');
};

 


 

7.  cancel과 flush의 활용

React 컴포넌트 내부에서 cancel과 flush를 함께 사용하면 리소스를 매우 효율적으로 관리할 수 있습니다.

import React, { useCallback, useEffect, useState } from 'react';
import _ from 'lodash';

const Editor = () => {
  const [text, setText] = useState("");

  const debouncedSave = useCallback(
    _.debounce((value) => {
      console.log("자동 저장 완료:", value);
    }, 3000),
    []
  );

  useEffect(() => {
    // 컴포넌트가 언마운트될 때!
    return () => {
      // 1. 만약 중요한 데이터라면? flush()로 마지막까지 저장
      debouncedSave.flush();
      
      // 2. 만약 불필요한 API 호출이라면? cancel()로 메모리 해제
      // debouncedSave.cancel();
    };
  }, [debouncedSave]);

  return (
    <textarea 
      value={text} 
      onChange={(e) => {
        setText(e.target.value);
        debouncedSave(e.target.value);
      }} 
    />
  );
};


성능 최적화는 단순히 함수 호출 횟수를 줄이는 것에서 끝나지 않고, 필요 없는 시점에는 멈추고(cancel), 결정적인 순간에는 실행(flush)하는 세밀한 제어가 함께 이루어질 때 UX와 사용성이 높은 서비스를 개발할 수 있는 것 같습니다.