본문 바로가기

[Dev] 🎯Self Study

[JavaScript] Debounce 함수 예제 및 풀이

JavaScript에서 debounce(디바운스)는

유저가 입력할 때마다 코드를 오직 한 번씩만 실행되도록 해주는 함수
검색 박스의 제안, 텍스트 필드의 자동 저장, 버튼의 더블 클릭의 제거가 모두 debounce를 이용하는 사례

 

출처와 예시 설명 

 

Debounce(디바운스) - JavaScript에서 함수를 지연시키는 방법 (JS ES6 예제)

JavaScript에서 debounce(디바운스)는 유저가 입력할 때마다 코드를 오직 한 번씩만 실행되도록 해주는 함수입니다. 검색 박스의 제안, 텍스트 필드의 자동 저장, 버튼의 더블 클릭의 제거가 모두 debou

www.freecodecamp.org

 

 

 

사용 예시


타이핑을 끝내고 난 뒤에만 검색 질의(query)에 대한 제안 옵션을 보여주고 싶을 때 사용

 

 


 

코드 이해하기

 

#HTML

<html>
    <head>
        <title>JS leading debounce</title>
        <link href="https://fonts.googleapis.com/css2?family=Jua&family=Roboto:wght@300&display=swap" rel="stylesheet"> 
        <link rel="stylesheet" href="./assets/css/main.css">
    </head>
    <body>
        <main>
            <div class="card blue">
                <h1>JS leading debounce</h1>
                <input type="text" onkeyup="processChanges()" />
                <button onclick="processChanges()">Click me</button>
            </div>
        </main>
    </body>
</html>

 

# JavaScript

function debounce(func, timeout = 300) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, args);
    }, timeout);
  };
}
function saveInput() {
  console.log('Saving data');
}
const processChange = debounce(() => saveInput());

 

1. 입력을 한다.

<input type="text" onkeyup="processChange()" />

 processChange = debounce(() => saveInput());가 실행된다. 

const processChange = debounce(() => saveInput());
즉 debounce 함수를 실행하는데 그 안에 인자로 콜백함수가 들어가며 이름은 func로 바뀐다. 
매개변수 : () => saveInput()
function debounce(func, timeout = 300) {


2. func가 실행되면서 비동기로 주르륵 실행된다. 

2-1. let timer; timer라는 변수를 선언한다.

  let timer;

 


2-2. 동시에 return이 돌면서 clearTimeout(timer); timer를 초기화한다.  

return (...args) => {
    clearTimeout(timer);


2-3. setTimeout이 timeout만큼 기다리고 그 안에 함수를 실행. (이때 timout은 초기값인 300ms)

    timer = setTimeout(() => {
      func.apply(this, args);
    }, timeout);
  };


timer = setTimeout(() => { func.apply(this, args); }, timeout);
이는 timer = setTimeout(() => { func.apply(this, args); }, 300); 이다. 

setTimeout을 호출하면 '타이머 식별자(timer identifier)'가 반환되어 timer에 저장.
 

setTimeout과 setInterval을 이용한 호출 스케줄링

 

ko.javascript.info

 


2-4. 만약 300ms 가 지나도록 더이상 입력이 없다면?

  func.apply(this, args);
{ func.apply(this, args); }가 실행되어 
미리 설정한 함수인 function saveInput() { console.log('Saving data'); 가 실행된다.
function saveInput() {
  console.log('Saving data');
}

따라서 콘솔에 Saving data가 출력된다.


3. 그러나 사용자가 300ms가 되기 전에 다다다 값을 입력하고 있다면?

3-1. processChange = debounce(() => saveInput()) 이 함수가 실행이 되어 다시 func부터 실행이 된다.

<input type="text" onkeyup="processChange()" />
const processChange = debounce(() => saveInput());
function debounce(func, timeout = 300) {

 


3-2. let timer; timer 변수 설정

  let timer;


return (...args) => { clearTimeout(timer); timer 초기화가 되어

    clearTimeout(timer);

그 아래의 setTimeout은 실행되지 않게 된다.

따라서 300ms 만큼의 시간이 될 때까지 입력값이 없다면, 그 값을 가지고 검색할 수 있는 것이다. 

 

 


 

반대의 경우 - 뒤이은 이벤트를 무시하는 방법

방금까지의 방법은 자동 저장이나 제안 옵션을 보여줄 때 좋다. 하지만 버튼 하나를 여러 번 클릭하는 사례에서는 어떨까?

 

 

: 마지막 클릭까지 기다리는 대신 첫 번째 클릭에서 이를 등록하고, 나머지를 무시

function debounce_leading(func, timeout = 300) {
  let timer;
  return (...args) => {
    if (!timer) {
      func.apply(this, args);
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = undefined;
    }, timeout);
  };
}

 

 

- debounce_leading라는 함수를 정의

 

- setTimeout 함수는 자바스크립트에서 제공하는 비동기 함수이며,

첫 번째 인자로 주어진 함수를 두 번째 인자로 주어진 시간(밀리초 단위)이 지난 후에 실행한다. 

여기서는 timeout이라는 시간이 경과하면 timer 변수를 undefined로 만드는 함수를 setTimeout에 전달한다.

 

 

1. debounce_leading 함수가 호출되면 timer 변수가 선언 

let timer;


2. 함수가 호출될 때마다, timer가 존재하지 않는지 확인

- 이때 첫 번째 호출에서는 timer가 아직 정의되지 않았으므로 (값은 undefined)이므로 (!timer) 조건이 참

 if (!timer) {
      func.apply(this, args);
    }

 


3. 이 경우, func 함수를 즉시 실행하고, timer에 setTimeout을 설정

- 이 setTimeout 함수는 timeout 시간이 지나면 timer를 undefined로 만든다.

 

3-1. func함수 실행결과는 콘솔에 값을 찍히게 한다.

function saveInput() {
  console.log('Saving data');
}

 

3-2. timer 설정

    timer = setTimeout(() => {
      timer = undefined;
    }, timeout);

 


4. 함수가 다시 호출되면, timer가 존재(timerID 반환후 저장)하므로 func 함수는 실행되지 않는다. 

 

이때 분기별 timer 값은 다음과 같다.

1. 초기 상태

 

- debounce_leading 함수가 처음 호출되면, timer는 아직 정의되지 않은 상태 

 


2. 첫 번째 함수 호출

 

- debounce_leading 함수가 처음 호출되면, (!timer) 조건식이 참이 되어 func 함수가 실행
그리고 timer에는 timeout 시간 후에 실행될 setTimeout 함수가 할당 

- 이 시점에서 timer에는 setTimeout의 반환값인 타이머 ID가 저장

🌟
따라서 이때 timer에 undefined나 null이 아닌 값이 저장되었기 때문에 (!timer) 조건식은 false가 되므로

다음 func 함수가 실행되지 않는 것이다.


3. 두 번째 이후 함수 호출

 

- debounce_leading 함수가 두 번째 이후로 호출되면, timer 값이 존재하므로 (!timer) 조건식이 거짓이 되고

func 함수는 실행되지 않음


- 그 대신 이전에 설정된 setTimeout은 clearTimeout에 의해 취소되고,
timer에는 다시 새로운 setTimeout 함수가 할당, 이 때도 timer에는 새로운 타이머 ID가 저장 

4. 이벤트 없는 timeout 시간 경과

만약 timeout 시간 동안 debounce_leading 함수가 다시 호출되지 않으면, setTimeout 함수 내부의 코드가 실행
이 코드에 의해 timer 값은 undefined로 설정되며, 다음 이벤트가 발생할 때 func 함수가 다시 실행


5. 대신, 이전에 설정한 setTimeout이 취소되고, 새로운 setTimeout이 다시 설정.

이것이 debounce의 핵심 동작

즉, 연속된 이벤트가 발생하면 이전 이벤트의 처리는 취소되고 새 이벤트의 처리만을 진행 


6. 이벤트가 timeout 시간(300ms) 동안 발생하지 않으면, 

setTimeout에 의해 timer가 undefined로 만들어지고, 다음 이벤트가 발생할 때 func 함수가 다시 실행

 

debounce 기법의 장점

debounce 기법을 사용하면, 이벤트가 너무 빈번하게 발생하여(저장 등) 시스템에 부담을 주는 것을 막을 수 있다.