1. 소개
- 최근에 수많은 이벤트를 발생시킬 수 밖에 없는 부분을 개발하면서 고민에 빠졌었다. 이 정도로 많은 요청이 있을때마다 이벤트를 발생시키는 것이 옳은건지, 이것을 최적화 할 수는 없는지 고민을 했다.
- 예를 들면 사용자가 스크롤을 내릴 때마다 특정 함수를 발생시킨다고 가정하자. 스크롤 좌표가 바뀔때마다 함수가 실행될텐데 정말 이것이 올바른 방법일까?
- 이러한 의문에 쓰로틀링과 디바운스에 대해 공부해보라는 조언을 받았고, 이렇게 정리해보려 한다.
- 참고로 제목에서는 프론트엔드의 기술이라고 썼지만, 이는 순전히 필자가 웹 프론트엔드 개발자이기 때문이다. 그렇기 때문에 아래에서도 웹을 예시로 들 예정이다. 디바운싱과 쓰로틀링은 프로그래밍 기법 중 하나임을 명심하길 바란다.
2. 쓰로틀링
* 의미
- 쓰로틀링(throttling), 또는 쓰로틀(throttle)은 전자-컴퓨터 공학에서 하드웨어에 과부하를 줄 경우 성능을 낮추고 억제하는 것을 의미한다.
- 쓰로틀은 조절판이라는 의미를 갖고 있으며, 쓰로틀링은 (목을)조르다 의 의미를 갖고있다. 즉 과도한 요청이 있을 때, 마치 수도꼭지를 조으듯이 잠시 억제 또는 무시하는 것이다.
- 위 둘을 정리하여 프론트엔드 관점에서 보자면, 연속적으로 발생하는 이벤트에 대해, 일정 시간을 묶어 그 시간이 지나기 전까지는 무시하는 방식을 의미한다.
* 예시
- 쓰로틀링이 사용될 수 있는 예시를 보자.
<section>
<div class="top">TOP</div>
<div class="middle">MIDDLE</div>
<div class="bottom">BOTTOM</div>
</section>
- 간단하게 스크롤 이벤트를 구성하기 위해 html 파일을 위와같이 작성하였다.
let number = 0;
const onScroll = () => {
number++;
console.log(number);
};
window.addEventListener("scroll", onScroll);
- 자바스크립트파일은 위와 같이 작성하였으며, window에 스크롤 이벤트를 걸어서 스크롤마다 숫자를 1씩 증가시키며 출력하도록 하였다.
- 이를 브라우저에서 실행해보자.
- 위의 영상을 보면 알 수 있듯이 스크롤 이벤트가 무수히 많이 발생하여 출력이 과도하게 됨을 알 수 있다.
- 만일 스크롤을 이용한 더 무거운 함수를 만들어 실행한다면 의도치않게 함수가 많이 실행될 것이다.
- 이럴 때 필요한 것이 쓰로틀링이다. 한 번 실행되고 나서 일정시간동안 이벤트를 무시하게된다면 원하는 만큼 실행할 수 있게 된다.
* 적용
- 자바스크립트에서 쓰로틀링은 아래와 같은 코드로 적용해 볼 수 있다.
let number = 0;
let throttler;
const onScroll = () => {
if (throttler) return;
throttler = setTimeout(() => {
number++;
console.log(number);
throttler = null;
}, 100);
};
window.addEventListener("scroll", onScroll);
- setTimeout을 이용하여 타이머 객체를 통째로 임의의 변수 throttler에 넣었다.
- 위 코드의 경우 0.1초동안은 이벤트를 잠궈 한번만 실행되도록 하였고, 0.1초 후에 함수를 실행한 후 비로소 잠굼을 해제하고 있다.
- 따라서 0.1초간은 임의의 변수 throttler에 타이머객체가 있기 때문에 스크롤 이벤트가 발생하더라도 무시된다.
- 쓰로틀링을 적용하여 함수발생을 위처럼 조절할 수 있게되었다.
3. 디바운스
* 의미
- 디바운싱(debouncing) 또는 디바운스(debounce)는 전자 컴퓨터 공학의 bouncing과 관련된 용어이다. bouncing은 스위치들이 접점에서 떨어지거나 붙는 시점에 물리적으로 미세하게 여러번 on/off가 되는 현상이다.
- 즉 바운싱은 의도는 한번의 on/off이나 원하지않는 수많은 on/off가 발생하는 것이다. 이를 막으려는 방법이 debouncing이다.
- 디바운싱은 여러 이벤트가 발생할 때, 이들을 하나의 그룹으로 묶어서 처리한다.
- 이 때, 주로 그룹에서 마지막에 처리된 함수를 처리한다.
* 예시
- 이번엔 스크롤 이벤트가 아닌 form의 제출이벤트를 예시로 보자.
<form>
<input id="input" type="text" />
<button type="submit">submit</button>
</form>
- input에는 텍스트를 입력할 수 있으며, 엔터 혹은 버튼을 클릭하면 submit이벤트가 발생한다.
const form = document.querySelector("form");
const onSubmit = (e) => {
e.preventDefault();
const input = document.querySelector("#input");
console.log("제출완료 :", input.value);
};
form.addEventListener("submit", onSubmit);
- submit이벤트가 발생할 시에는 input의 value값을 출력하도록 하였다.
- 한 글자씩 입력할때마다 엔터를 눌렀으며, 마지막에는 제출 버튼을 연속적으로 클릭하는 영상이다.
- 당연히 엔터 혹은 버튼 클릭마다 submit이벤트가 발생하고, 함수가 실행된다.
- 하지만 위 상황이 api요청이라면 어떨까. 유저가 검색을 실수해서 얼른 고쳐서 재요청할 수도있고, 장난삼아 제출버튼을 여러번 누를 수도 있다. 그렇게되면 서버에 수많은 요청이 가서 과부하가 걸릴 수도 있고, 만일 외부 api를 이용중이어서 한 번 요청에 일정금액이 청구된다면 생각만 해도 끔찍하다.
- 이럴 때 필요한 방식이 디바운싱이다. 모든 요청을 하나로 보고 한 번만 요청하는 것이다. 위의 사례에서도 적용할 수 있도록 가장 마지막 요청에만 실행하는 방식을 적용해보자.
* 적용
- 코드는 다음과 같다.
const form = document.querySelector("form");
let debouncer;
const onSubmit = (e) => {
e.preventDefault();
const input = document.querySelector("#input");
if (debouncer) {
clearTimeout(debouncer);
}
debouncer = setTimeout(() => {
console.log("제출완료 :", input.value);
}, 1000);
};
form.addEventListener("submit", onSubmit);
- 쓰로틀링과 달리 타이머 객체 바깥에서 타이머 객체가 있으면 클리어해주는 것을 볼 수 있다.
- 위를 통해 시간(예시에서는 1초)안에 다시 요청이 발생하면 앞의 요청은 제거하고, 최신 요청을 1초후에 실행하도록 할 수 있다.
- 위에서 봤던 영상과 동일하게 한 글자 입력마다 엔터를 눌렀다. 사에서 잠시 멈추었기때문에 함수가 실행되었다.
- submit버튼을 아무리 여러번 누르더라도 가장 마지막 요청만 처리되도록 했음을 알 수 있다.
참고
'Client > Front-end' 카테고리의 다른 글
<JS> 클로저에 대한 고찰 (소개 / 활용 / 단점 / 메모리) (0) | 2022.01.20 |
---|---|
<React> React Router Dom V6 도입기 (2) | 2022.01.18 |
<FE> 리액트 프로젝트에 무한스크롤 도입하기 (0) | 2021.12.19 |
<FE> Recoil로의 마이그레이션 (4) | 2021.12.07 |
<FE프레임워크> 리액트 vs 뷰 vs 앵귤러 vs 스벨트 (0) | 2021.11.08 |