# Debouce로 알아보는 Closure

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Debounce : 일정 시간안에 연속되어 발생되는 이벤트를 마지막 이벤트만 콜백 함수를 실행 시킨다.</div>
</div>

```javascript
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`를 따른다, 어디서 호출 되었는지 보다 어디서 선언 되었는지에 따라 상위스코프가 결정된다.

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

---

### ♻️ 직접 debounce를 사용해 보면

```xml
<!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>
```

```javascript
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`가 반환한 함수 즉 아래 코드와 같이 함수만 반복적으로 실행된다고 생각된다

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

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

추가적으로 보면 좋은자료

[poiema - scope](https://poiemaweb.com/js-scope)

[제로초님의 함수의 호출](https://www.youtube.com/watch?v=NS1cIsWlFGI&list=PLcqDmjxt30Rt9wmSlw1u6sBYr-aZmpNB3&index=2)

[우테코 엘라님 - Scope & Closure](https://www.youtube.com/watch?v=PVYjfrgZhtU&t=762s)
