의도: 성능 최적화 및 각 개념을 어디에 적용하면 좋을지 확인하는 질문
팁: 사용한 경험에 대해서도 언급하면 좋다.
나의 답안
쓰로틀링과 디바운싱은 주로 이벤트 처리 성능을 최적화하기 위해 사용되는 기술로, 특히 브라우저에서 발생하는 빈번한 이벤트를 효율적으로 처리하는 데 유용합니다.
두 기법 모두 이벤트 호출 빈도를 제어하는 데 사용되지만, 동작 방식이 다릅니다.
먼저, 쓰로틀링은 특정 간격마다 이벤트를 실행하도록 제한하는 기술입니다.
이벤트가 여러 번 발생하더라도, 주어진 간격 내에서는 한 번만 실행됩니다.
쓰로틀링은 이벤트가 발생하면 현재 시간을 확인하고, 이전에 실행된 시간을 설정된 간격과 비교합니다.
이때 간격이 지나지 않았으면 이벤트를 실행하지 않고, 간격이 지났으면 이벤트를 실행합니다.
사용 예시로는 스크롤 이벤트가 있습니다.
스크롤 이벤트처럼 순식간에 여러 번 실행되는 이벤트는 성능 저하를 일으키기 쉽습니다.
여기에 쓰로틀링을 적용해 주면 스크롤할 때마다가 아닌 설정한 간격마다 함수를 실행하기 때문에 불필요한 호출을 줄여 성능을 향상시킬 수 있습니다.
디바운싱은 이벤트가 연속으로 발생할 때 마지막 이벤트가 발생한 후 일정 시간 이후에만 실행되는 기술입니다.
디바운싱은 이벤트가 발생하면 기존 타이머를 취소하고, 새 타이머를 설정합니다.
이때 일정 시간이 지나도 추가 이벤트가 발생하지 않으면 실행됩니다.
사용 예시로는 검색어 자동 완성이 있습니다.
유저의 인풋 중간 결과는 중요하지 않기 때문에 전부 건너뛰고, 마지막 결과 값에 대해서만 자동 완성 결과를 보여주면 불필요한 검색 요청을 줄여 성능을 향상시킬 수 있습니다.
정리하자면, 지속된 이벤트의 호출에서 중간 호출도 중요하면 쓰로틀링을, 마지막 호출만이 중요하면 디바운싱을 사용하는 것이 좋습니다.
주어진 답안 (모범 답안)
두 개념 모두 특정 함수의 실행 빈도를 조절하는 기술이지만 서로의 차이점 때문에 사용처가 다릅니다.
먼저 쓰로틀링의 경우에는 일정 간격마다 함수를 실행하도록 횟수를 제한하는 기술입니다.
예를 들어 스크롤 이벤트처럼 순식간에 여러 번 실행되는 이벤트는 성능 저하를 일으키기 쉽습니다.
여기에 쓰로틀링을 적용해 주면 스크롤할 때마다가 아닌 설정한 간격마다 함수를 실행하기에 불필요한 호출을 줄여 성능을 향상시킬 수 있습니다.
그리고 디바운싱은 연속된 함수의 호출이 들어올 경우 무시하고 있다가 제일 마지막에 호출된 함수만을 실행하도록 하는 기술입니다.
대표적인 사용처는 검색어 자동 완성입니다.
유저의 인풋 중간 결과는 중요하지 않기에 전부 스킵해버리고, 마지막 결과 값에 대해서만 자동 완성 결과를 보여주면 불필요한 검색 요청을 줄여 성능을 향상시킬 수 있습니다.
다시 말해 지속된 이벤트의 호출에서 중간 호출도 중요하면 쓰로틀링을, 마지막 호출만이 중요하면 디바운싱을 사용하는 것이 좋습니다.
쓰로틀링(Throttling)과 디바운싱(Debouncing)은 주로 이벤트 처리 성능을 최적화하기 위해 사용되는 기술로, 특히 브라우저에서 발생하는 빈번한 이벤트(스크롤, 입력, 리사이트 등)를 효율적으로 처리하는 데 유용하다.
두 기법 모두 이벤트 호출 빈도를 제어하는 데 사용되지만, 동작 방식이 다르다.
예제 코드
function throttle(func, delay) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
func.apply(this, args);
}
};
}
// 사용 예시
window.addEventListener(
"resize",
throttle(() => {
console.log("윈도우 리사이즈");
}, 1000)
);
throttle 함수의 구성요소
func(매개변수)
- 쓰로틀링을 적용할 실제 실행할 함수
- 예제에서 전달된 함수는
() => { console.log("윈도우 리사이즈"); }이다.
delay(매개변수)
- 함수 실행 간 최소 시간 간격을 밀리초 단위로 설정한다.
- 예제에서는
1000밀리초(1초)로 설정되었다.
lastTime(상태 변수)
- 마지막으로
func가 실행된 시간을 저장한다.- 처음에는
0으로 초기화된다.
return function (...args)(익명 함수)
- 실제로 이벤트가 발생했을 때 호출되는 함수
- 이 함수는 클로저로서
lastTime과func에 접근할 수 있다....args는 가변 인자 문법으로, 이벤트 핸들러에 전달된 모든 인수를 배열 형태로 받아온다.
Date.now()(현재 시간)
- 현재 시간을 밀리초 단위로 반환한다.
- 이 값과
lastTime을 비교하여 함수 실행 여부를 결정한다.
if(now - lastTime >= delay)(조건문)
- 현재 시간(
now)과 마지막 실행 시간(lastTime)의 차이가delay이상이면 함수를 실행한다.
func.apply(this, args)(함수 실행)
- 전달받은
func를 현재 실행 컨텍스트(this)와 인자(args)를 사용해 실행한다.apply를 사용하면 가변 인자를 함수에 쉽게 전달할 수 있다.
lastTime = now
- 함수가 실행된 시간을 갱신한다. 이후 호출 시 이 시간을 기준으로 계산된다.
사용 예시 동작 과정
window.addEventListener("resize", ...);
resize이벤트가 발생할 때마다 쓰로틀링 함수가 실행된다.
throttle함수 호출
throttle함수는 내부적으로 클로저를 생성하여 반환한다. 반환된 함수는 이벤트 발생 시 호출된다.
- 첫 번째
resize이벤트
lastTime은 초기값0이므로, 현재 시간(now)과의 차이는delay(1000ms) 이상이다.- 조건을 만족하여
func(여기서는console.log("윈도우 리사이즈"))가 실행된다.
- 1000ms 이내에 발생한 추가
resize이벤트
- 현재 시간(
now)과lastTime의 차이가delay(1000ms) 미만이므로,func는 실행되지 않는다.
- 1000ms 이후의
resize이벤트
- 현재 시간(
now)과lastTime의 차이가delay이상이므로func가 실행된다.lastTime은 다시 현재 시간으로 업데이트된다.
작동 방식
1) 이벤트가 발생하면 기존 타이머를 취소한다.
2) 새 타이머를 설정한다.
3) 일정 시간이 지나도 추가 이벤트가 발생하지 않으면 실행된다.
예제 코드
function debounce(func, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 사용 예시
const input = document.querySelector("input");
input.addEventListener(
"input",
debounce(() => {
console.log("입력값 변화 후 실행");
}, 500)
);
debounce 함수의 구성요소
func(매개변수)
- 디바운싱을 적용할 실제 실행할 함수
- 예제에서는
() => { console.log("입력값 변화 후 실행"); }이다.
delay(매개변수)
- 함수가 실행되기까지 기다릴 시간 간격을 밀리초 단위로 설정한다.
- 예젱서는
500밀리초(0.5초)로 설정되었다.
timer(상태 변수)
- 타이머를 저장하는 변수
clearTimeout을 사용하여 이전 타이머를 취소하고, 새로운 타이머를 설정하는 데 사용된다.
return function (...args)(익명 함수)
- 이벤트가 발생할 때 호출되는 함수
...args는 이벤트 핸들러에 전달된 인자를 배열 형태로 받는다.
clearTimeout(timer)(기존 타이머 취소)
- 이 코드가 실행될 때마다 이전에 설정된 타이머를 취소하여, 이전 이벤트에 의해 설정된 함수 실행을 막는다.
- 즉, 이전 이벤트로 인해 발생할 함수 실행을 취소하고 새로운 타이머를 설정한다.
timer = setTimeout(...)(새 타이머 설정)
setTimeout을 사용하여 지정된delay(여기서는500ms)후에func를 실행한다.func.apply(this.args)는func를 실행할 때, 이벤트 핸들러가 전달한this와 인자(args)를 그대로 넘겨준다.
사용 예시 동작 과정
- 이벤트가 발생하면
- 사용자가
input필드에 입력을 할 때마다input이벤트가 발생한다.- 이벤트 핸들러로
debounce가 감싸진func가 호출된다.
- 첫 번째 호출 시
clearTimeout(timer)가 실행되어, 이전에 설정된 타이머가 취소된다.- 새로운 타이머가
setTimeout으로 설정되고,delay(500ms) 후에func가 실행되도록 예약된다.
- 추가적인 호출들
input이벤트가 계속해서 발생하면, 그때마다 이전에 설정된 타이머를clearTimeout(timer)로 취소하고 새 타이머를 설정한다.- 즉, 사용자가 계속해서 입력하는 동안
func는 실행되지 않는다.
delay시간 동안 더 이상 이벤트가 발생하지 않으면
- 마지막 이벤트가 발생한 후,
delay(500ms)가 지나면func가 실행된다.
| 특성 | 쓰로틀링 | 디바운싱 |
|---|---|---|
| 실행 주기 | 일정한 시간 간격으로 실행 | 마지막 이벤트 발생 후 일정 시간 이후 실행 |
| 즉시 실행 여부 | 즉시 실행 가능(옵션에 따라 다름) | 즉시 실행되지 않음 |
| 주요 사용 사례 | 스크롤, 리사이즈 등 지속적 이벤트 | 검색창 입력, 자동 저장, 버튼 중복 클릭 방지 |
| 타이머 초기화 | 타이머 초기화 없음 | 이벤트 발생 시 타이머 초기화 |
JavaScript 라이브러리인 Lodash는 쓰로틀링과 디바운싱을 쉽게 구현할 수 있는 유틸리티 메서드(_.throttle, _.debounce)를 제공한다.
Lodash 예제
// Throttling
const throttledFunc = _.throttle(() => {
console.log("Throttled 실행");
}, 1000);
// Debouncing
const debouncedFunc = _.debounce(() => {
console.log("Debounced 실행");
}, 1000);
window.addEventListener("scroll", throttledFunc);
input.addEventListener("input", debouncedFunc);