짧은 시간 연속으로 발생하는 이벤트(scroll, resize, input, mousemove 등)는
과도하게 호출되어 성능에 문제를 일으킬 수 있다.
이럴 때 성능 최적화를 위해 사용되는 기술인 Debounce와 Throttle을 사용할 수 있다.
Debounce와 Throttle은
짧은 시간 간격으로 연속으로 발생하는 이벤트를 그룹화하여 과도한 이벤트 호출을 방지하는 프로그래밍 기법이다.
이를 구현하기 위해서는 타이머 함수가 사용된다.
1. Debounce:
특정 시간동안 이벤트가 발생하지 않을 때, 딱 한 번만 함수를 실행하는 것이다.
이벤트가 연속적으로 발생하면 이전의 실헹 대기 중인 함수 호출을 취소하고,
가장 마지막 이벤트 발생 시점부터 설정된 시간(delay) 이후에 함수를 실행한다.
주로 사용하는 곳은,
1) 검색어 자동 완성:
사용자가 검색어를 입력할 때마다 API 호출을 하는 대신,
입력이 멈춘 후 잠시 뒤에 API를 호출하여 불필요한 네트워크 요청을 줄일 수 있다.
2) 창 크기 조절 이벤트:
창 크기가 계속 변경되는 동안에는 함수를 실행하지 않고,
크기 조절이 완료된 후 한 번만 함수를 실행하여 성능을 개선할 수 있다.
3) 버튼 중복 클릭 방지 처리
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Debounce</title>
</head>
<body>
<input type="text" />
<div class="msg"></div>
<script>
const $input = document.querySelector("input");
const $msg = document.querySelector(".msg");
const debounce = (callback, delay) => {
let timerId;
return (event) => {
if (timerId) clearTimeout(timerId);
// 만약 timerId가 존재하면 이전 타이머를 취소한다.
timerId = setTimeout(callback, delay, event);
// delay 시간 후에 callback 함수를 실행하도록 예약한다.
// 결과적으로, 이벤트가 delay 시간 내에 다시 발생하면 이전 타이머가 취소되고 새로운 타이머가 설정되므로,
// callback 함수는 마지막 이벤트 발생 후 delay 시간 이후에 딱 한 번만 실행된다.
};
};
$input.oninput = debounce((e) => {
$msg.textContent = e.target.value;
}, 300);
</script>
</body>
</html>
2. Throttle:
스로틀은 설정된 시간 간격(delay) 동안 함수가 최대 한 번만 실행되도록 제한하는 기법이다.
이벤트가 연속적으로 발생하더라도, 지정된 시간 간격이 지나야만 함수를 다시 실행할 수 있다.
주로 사용하는 곳은,
1) 스크롤 이벤트:
스크롤 이벤트가 너무 자주 발생하면 성능에 영향을 줄 수 있다.
스로틀링을 사용하여 스크롤 이벤트 처리 함수가 일정 시간 간격으로만 실행되도록 제한할 수 있다.
2) 마우스 이동 이벤트:
마우스가 빠르게 움직일 때마다 함수를 실행하는 대신, 스로틀링을 사용하여 함수 호출 빈도를 줄일 수 있다.
3) 무한 스크롤 UI 구현
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Throttle</title>
<style>
.container {
width: 300px;
height: 300px;
background-color: rebeccapurple;
overflow: scroll;
}
.content {
width: 300px;
height: 1000vh;
}
</style>
</head>
<body>
<div class="container">
<div class="content"></div>
</div>
<div>
일반 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
<span class="normal-count">0</span>
</div>
<div>
스로틀 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
<span class="throttle-count">0</span>
</div>
<script>
const $container = document.querySelector(".container");
const $normalCount = document.querySelector(".normal-count");
const $throttleCount = document.querySelector(".throttle-count");
const throttle = (callback, delay) => {
let timerId;
return (event) => {
if (timerId) return;
// timerId가 존재할시 함수를 즉시 종료한다.(이미 타이머가 실행 중이므로)
timerId = setTimeout(
() => {
callback(event);
timerId = null;
},
delay,
event
);
// delay 시간 후에 callback 함수를 실행하도록 예약한다.
// callback 함수가 실행되면 timerId를 null로 설정하여, 다음 이벤트 발생시 함수를 실행할 수 있도록 한다.
// 결과적으로, 이벤트가 연속적으로 발생하더라도 callback 함수는 delay 시간 간격으로 최대 한 번만 실행된다.
};
};
let normalCount = 0;
$container.addEventListener("scroll", () => {
$normalCount.textContent = ++normalCount;
});
let throttleCount = 0;
$container.addEventListener(
"scroll",
throttle(() => {
$throttleCount.textContent = ++throttleCount;
}, 100)
);
</script>
</body>
</html>
'Javascript > Concept' 카테고리의 다른 글
이터러블, 이터레이터, 제너레이터, async/await (0) | 2025.04.28 |
---|---|
비동기 함수의 단점과 프로미스. (0) | 2025.04.28 |
동기 함수와 비동기 함수 (0) | 2025.04.28 |
slice()와 splice()의 차이점 (0) | 2025.02.17 |
github : https://github.com/dnjfht
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!