연속으로 발생하는 이벤트를 제어하는 방법
Event Controls
HTML의 DOM 요소를 조작하면서 발생하는 이벤트 중 input, mousemove, resize, scroll 등의 이벤트는 짧은 시간 간격으로 연속해서 발생합니다. 이러한 이벤트들에 함수를 등록해서 사용하는 경우, 너무나 잦은 호출로 인해 성능 저하를 불러올 수 있습니다. 그렇기 때문에 연속적으로 발생하는 이벤트들을 그룹화하여, 원하는 순간에만 호출될 수 있도록 하는 기법을 소개하려 합니다.
본문에서는 간단한 예제만 소개하기에, 프로젝트에서는 Lodash와 같은 라이브러리 사용을 추천합니다.
디바운스
디바운스(Debounce)는 연속으로 발생하는 이벤트의 마지막 이벤트만 호출할 수 있도록 하는 기법입니다. 만약 우리가 외부의 API를 호출해서 사용한다 하면 보통 요청 제한 횟수(Limit)가 걸려있을텐데, 이를 짧은 시간 안에 연속으로 호출하게 된다면 할당량을 금방 소모하게 되어 큰일입니다. 그렇기 때문에 호출이 필요한 부분에 디바운스를 적용한다면, 이와 같은 문제를 사전에 방지할 수 있습니다. 이 외에도 디바운스는 input의 값이 변경될 때, 창의 크기가 변경되어 resize가 발생할 때 적용해볼 수 있습니다.
다음은 input 이벤트에 디바운스를 적용한 예시입니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Debounce</title>
<style>
body {
padding-top: 2rem;
text-align: center;
}
input {
width: 600px;
padding: 0.625rem;
font-size: 1.2rem;
}
</style>
</head>
<body>
<input type="text" />
<script>
const text = document.querySelector('input');
const debounce = (callback, delay) => {
let timer;
return (event) => {
// 실행 중인 타이머가 있다면, 제거 후 재생성
if (timer) clearTimeout(timer);
timer = setTimeout(callback, delay, event);
};
};
text.addEventListener(
'input',
debounce((e) => {
console.log(e.target.value);
}, 1000)
);
</script>
</body>
</html>
콘솔에서 결과를 확인해 보면, 사용자가 입력을 마친 시점에만 함수가 호출되는 것을 확인할 수 있습니다.
스로틀
디바운스가 바인딩된 함수를 마지막에만 호출하는 것이라면, 스로틀(Throttle)은 일정한 간격에 맞춰 함수를 호출하는 기법입니다. 스로틀은 보통 scroll과 관련된 이벤트에 적용하거나, 무한 스크롤을 구현할 때 발생하는 성능 저하를 막기 위해 사용합니다.
아래는 스크롤 이벤트에 바인딩된 함수가 얼마나 호출되는지 비교해보는 코드입니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Throttle</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 300px;
height: 300px;
margin: 0 auto;
overflow-y: scroll;
}
.box {
width: 100%;
height: 2000px;
background-color: grey;
overflow: hidden;
}
.text {
padding-top: 2rem;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="box"></div>
</div>
<p class="text">
스로틀 적용 X: <span>0</span>회 호출<br />
스로틀 적용 O: <span>0</span>회 호출
</p>
<script>
const container = document.querySelector('.container');
const text = document.querySelector('.text');
const throttle = (callback, delay) => {
let timer;
return (event) => {
// 타이머가 호출되면, 함수를 실행하고 타이머 제거
if (timer) return;
timer = setTimeout(() => {
callback(event);
timer = null;
}, delay);
};
};
container.addEventListener('scroll', (e) => {
text.children[0].innerText = +text.children[0].innerText + 1;
});
container.addEventListener(
'scroll',
throttle((e) => {
text.children[2].innerText = +text.children[2].innerText + 1;
}, 300)
);
</script>
</body>
</html>
디바운스는 새로운 요청이 들어올 때 타이머를 새롭게 초기화해주는 반면, 스로틀은 타이머가 이미 존재하면 아무런 동작을 하지 않습니다. 타이머가 실행되어서야 내부에서 타이머를 제거하기 때문에, 일정한 간격으로 이벤트 함수를 호출할 수 있게 됩니다.
'🌈 기술스택 > JavaScript' 카테고리의 다른 글
비동기 처리를 위한 문법 (Promise) (0) | 2021.10.08 |
---|---|
자바스크립트에 동시성을 부여하는 이벤트 루프 (0) | 2021.10.07 |
코드 실행에 필요한 정보를 담은 실행 컨텍스트 (0) | 2021.10.05 |
자기 자신을 가리키는 값 this (0) | 2021.10.03 |
자바스크립트 코드의 유효 범위 Scope (0) | 2021.10.03 |
댓글
이 글 공유하기
다른 글
-
비동기 처리를 위한 문법 (Promise)
비동기 처리를 위한 문법 (Promise)
2021.10.08 -
자바스크립트에 동시성을 부여하는 이벤트 루프
자바스크립트에 동시성을 부여하는 이벤트 루프
2021.10.07 -
코드 실행에 필요한 정보를 담은 실행 컨텍스트
코드 실행에 필요한 정보를 담은 실행 컨텍스트
2021.10.05 -
자기 자신을 가리키는 값 this
자기 자신을 가리키는 값 this
2021.10.03