Debouce로 알아보는 Closure

Debouce로 알아보는 Closure

자바스크립트로 Debounce를 구현하고 Closure에 대해 알아본다.

검색 기능을 구현 할때 불필요한 요청을 줄이기위해 debounce를 구현해서 요청을 줄인다. 보통은 라이브러리(Lodash)를 많이 사용하지만 직접 구현 해보기로 했다

💡
Debounce : 일정 시간안에 연속되어 발생되는 이벤트를 마지막 이벤트만 콜백 함수를 실행 시킨다.
function debounce(callback, delay = 500) {
    let timer = null
    return (...args) => {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            callback(...args)
        }, delay)
    }
}

debouce를 구현하면 위와같은 코드로 구현 할 수 있다.


동작원리를 간단하게 설명 하면

처음에 timer를 선언해주고 함수를 return 한다 →

만약 timer가 있으면 clearTimeout 해주어 setTimeout을 종료한다. →

timer에 새로운 setTimeout을 넣어준다.

이런 과정을 통해서 여러 요청이 들어와도 이전에 있던 요청은 clearTimeout으로 종료해주고 새로운 setTimeout을 실행한다.


🤖 "timer는 어떻게 동작하는거지?" 라는 의문이 생긴다.

바로 Closure개념을 이용한 것이다.

Closure : 생명주기가 끝난 렉시컬 스코프의 환경을 기억해서 내부함수가 외부함수에 접근할수있는 개념

자바스크립트는 Lexical scope를 따른다, 어디서 호출 되었는지 보다 어디서 선언 되었는지에 따라 상위스코프가 결정된다.

deboucetimer 또한 생명주기가 끝난 함수 이지만 반환된 함수를 통해서 접근 할수있는 것이다.


♻️ 직접 debounce를 사용해 보면

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input class="input" type="text" />
    <script>
        // 여기에 javascript code 가 작성된다.
    </script>
</body>

</html>
function debounce(callback, delay = 500) {
    let timer = null
    return (...args) => {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            callback(...args)
        }, delay)
    }
}       

const onClick = (e) => {
    console.log(e.target.value)
}
const debounceInstance = debounce(onClick)

const $input = document.querySelector('.input')

$input.addEventListener('input', debounceInstance)

다음과 같이 사용 할 수 있다.

🚨여기서 주의해야할 점은 함수의 호출이다.

addEventListener에 인자로 들어가 있는 debounceInstance 는 callback함수로 들어가 있는것이지 함수의 호출이 된것이 아니다.

(함수의 호출을 작성하려면 debounceInstance() 으로 인자로 넣어야한다)

input이벤트가 발생할때마다 tiemr 재선언 되어서 const timer = null 이 계속해서 실행되는것이 아닐까라는 의문이 들수도 있다.

그래서 이해를 돕기 위해 debounceInstance변수에 debounce를 넣어서 사용해주었다.

debounce는 한번만 호출되고 debounce가 반환한 함수 즉 아래 코드와 같이 함수만 반복적으로 실행된다고 생각된다

const debounceInstance = (...args) => {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            callback(...args)
        }, delay)
    }

이런식으로 동작하기에 timer는 재선언되지 않고, debounce에서 반환된 함수를 통해 timer 에 접근해서 debounce를 구현할수 있었던 것이다.

추가적으로 보면 좋은자료

poiema - scope

제로초님의 함수의 호출

우테코 엘라님 - Scope & Closure