게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌52 - JavaScript - 7년 전 등록

쓰로틀링과 디바운싱

안녕하세요. 이번 시간에는 쓰로틀링(throttling)과 디바운싱(debouncing)에 대해 알아보겠습니다. 원래 예정에 없던 강좌이지만 요청을 받았기 때문에 써봅니다. 프로그래밍 기법 중 하나입니다(아니, 둘 인가요...).

용어가 생소하신 분들을 위해 간단히 설명해보겠습니다.

  • 쓰로틀링: 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것
  • 디바운싱: 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것

위 두 개는 underscore(_)에도 있는 기능입니다. underscore나 lodash를 쓰고 계신 분들이라면 그 라이브러리의 메소드를 쓰시면 편합니다.

사용처가 궁금하실 텐데요. 디바운싱은 주로 ajax 검색에 자주 쓰입니다. 쓰로틀링은 스크롤을 올리거나 내릴 때 보통 사용합니다. 어디까지나 제 경험에 바탕한 사용처입니다.

디바운싱

요즘 서비스들은 검색어 치자마자 엔터 없이도 결과가 바로바로 나오죠? 만약 '제로초'를 검색창에 친다고 합시다. 엔터 없이도 결과를 즉시 보여주려면 항상 input 이벤트에 대기하고 있어야 합니다.

<input id="input" />
document.querySelector('#input').addEventListener('input', function(e) {
  console.log('여기에 ajax 요청', e.target.value);
});

실제 ajax 요청을 보내기는 힘드니 콘솔 로그로 대체합니다. 로그가 콘솔에 찍힐 때마다 ajax 요청이 실행된다고 생각하시면
됩니다. 문제는 한 글자 칠 때마다 ajax 요청이 실행된다는 것입니다. 'ㅈ', '제', '젤', '제로', '제롳', '제로초' 모두 요청이 실행됩니다. 6번이나 요청을 했습니다(한글같은 조합형 언어는 사진처럼 6번보다 더 많이 이벤트가 발생할 수도 있습니다). 거기에 'ㅈ', '젤', '제롳'는 제대로 된 검색 결과가 나오지 않을 것 같은 검색어입니다. 

undefined

이와 같은 낭비는 유료 API를 사용했을 때 큰 문제가 됩니다. 만약 구글지도 API같은 것을 사용할 때 위와 같이 쿼리를 10번 날리면 어마어마한 손해입니다. 쿼리 하나가 다 돈이거든요. 따라서 디바운싱은 비용적인 문제와도 관련이 있습니다. 우리는 마지막 '제로초'를 다 쳤을 때 ajax 요청을 보내고 싶습니다.

먼저 어떻게 구현할지 생각해봅시다. 보통 사람들은 타자를 연달아칩니다. 중간에 잠시 생각하느라 몇 초 쉴 수는 있겠지만 대부분 한번에 검색어를 입력합니다. 따라서 입력이 다 끝난 후에 요청을 보내면 됩니다. 즉 타자를 칠 때(input 이벤트 발생)마다 타이머를 설정합니다. 200ms동안 입력이 없으면 입력이 끝난 것으로 칩니다(시간은 적당히 설정하면 됩니다). 200ms 이전에 타자 입력이 발생하면 이전 타이머는 취소하고 새로운 타이머를 다시 설정하는 겁니다.

var timer;
document.querySelector('#input').addEventListener('input', function(e) {
  if (timer) {
    clearTimeout(timer);
  }
  timer = setTimeout(function() {
    console.log('여기에 ajax 요청', e.target.value);
  }, 200);
});

이제 더는 여러 번 호출되지 않습니다. 이게 바로 디바운싱입니다. 한글 특성상 마지막에 두 번 호출되는 경우도 있습니다.

undefined

쓰로틀링

쓰로틀링은 보통 성능 문제 때문에 많이 사용합니다. 특성 자체가 실행 횟수에 제한을 거는 것이기도 하고요.

스크롤을 올리거나 내릴 때 scroll 이벤트가 매우 많이 발생합니다. scroll 이벤트가 발생할 때 뭔가 복잡한 작업을 하도록 설정했다면 매우 빈번하게 실행되기 때문에 엄청 렉이 걸릴 것입니다. 그럴 때 쓰로틀링을 걸어줍니다. 몇 초에 한 번, 또는 몇 밀리초에 한 번씩만 실행되게 제한을 두는 것이죠.

디바운싱으로 구현했던 ajax 검색을 쓰로틀링으로 대체해도 됩니다. 물론 쿼리는 조금 더 날리겠지만요. 한 번 구현해보겠습니다. 똑같이 200ms초 제한을 걸어두었습니다. 타이머가 설정되어 있으면 아무 동작도 하지 않고, 타이머가 없다면 타이머를 설정합니다. 타이머는 일정 시간 후에 스스로를 해제하고, ajax 요청을 날리게 하면 됩니다.

var timer;
document.querySelector('#input').addEventListener('input', function (e) {
  if (!timer) {
    timer = setTimeout(function() {
      timer = null;
      console.log('여기에 ajax 요청', e.target.value);
    }, 200);
  }
});

이제 최소 200밀리초마다 요청을 보냅니다. 물론 ajax 검색은 디바운싱으로 처리하는 게 더 나아보입니다. 하지만 중간 중간 검색 결과도 보여주고 싶다면 쓰로틀링도 괜찮은 옵션인 것 같습니다.

undefined

물론 매우 빠르게 구현한 것이기 때문에 이 코드로는 예외 사항들을 처리하지 못 할 수도 있습니다. underscore의 _.debounce_.throttle을 추천합니다.

이렇게 디바운싱과 쓰로틀링에 대해 알아보았습니다. 코드는 잊어버리셔도 좋습니다. 하지만 용어는 기억해두세요. 그래야 나중에 다시 검색할 수 있습니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

12개의 댓글이 있습니다.
일 년 전
안녕하세요 제로초님 underscore를 활용하는 대신 리덕스 사가의 takeLastest랑 delay를 이용해서 구현하는 방식도 디바운스 서치로 활용할수 있나요? 아니면 _.debounce를 활용하는게 가장 확실한 방법인가요?
일 년 전

리덕스 사가에 아예 debounce라는 이펙트가 있습니다.
3년 전
안녕하세요 제로초님~ 쓰로틀링에서 timer = null 하기 전에 clearTimeout(timer)를 해주는 게 좋을까요???
3년 전
안 해도 됩니다. 이미 해제된 상태입니다.
3년 전
감사합니다
3년 전
안녕하세요 제로초님. var timer 부분이 addEventListener안쪽에 들어와 있으면 제대로 작동을 안하는데 이유를 알 수 있을까요?
3년 전
변수의 스코프 문제입니다. 안쪽에 변수를 선언하면 addEventListener가 끝나면 timer 변수가 사라지겠죠?
3년 전
감사합니다!
3년 전
오늘도 좋은 글 감사합니다.
3년 전
로초님 궁금한 점이 있습니다! 'Debounce'는 아무리 많은 이벤트가 발생해도 무시하고 딱 한번만 마지막 이벤트만 실행하는 것으로 알고있는데 , 이는 자바스크립트 엔진이 알아서 파악하고 무시하는 건가요 ??
3년 전
아뇨. 그 기능을 위 코드처럼 직접 구현한 겁니다.
3년 전
엇 underscore의 _.debounce 가 그러한지 물어보려고 했는데.. 질문 할때 생략헸네요 ㅜ.ㅜ
3년 전
이 글을 왜 이제야 봤을까 흑 ㅠ
3년 전
/
4년 전
와우 딴글아무리 찾아봐도 이해안됐는데 이 글 보고 한방에 이해 됨 굳굳
4년 전
디바운싱 쓰로쓸링 관련 작업할때마다 들어오고있습니다 정말감사합니다.
5년 전
안녕하세요 . 쓰로틀링에서 timer = null; 을 해주는 부분이 이해가 가지 않아 질문드립니다.
Q. 조건문에서 !undefinded 도 true 이고 !null도 true로 되는데 setTimeOut에서 null을 할당해주는 이유가 무었인가요? throttle 코드가 어떤방식으로 작동되는건가요?
5년 전
!undefined하셔도 됩니다. 저는 undefined를 안 쓰기 때문에 null을 한 것 뿐입니다.
5년 전
슬라이딩되는 보고서를 구현하는데 다음 보고서로 넘어가는 버튼을 여러번 클릭할 때 문제가 되어서 구글링하다가 이 강좌를 찾았습니다.
바로 해결이 되었고 새로운 기술도 배웠네요 감사합니다 ^^
그런데 질문이 있는데요, 이벤트 리스너 안에있는 조건문에서 200ms 이후에 timer가 다시 없어지는? 이유가 무엇일까요?
맨 처음 화면이 로드될때 timer가 선언만 되었기 때문에 undefinded상태라서 처음 조건문은 false로 빠지겠지만, 이후에는 어떻게 조건문을 피해가는 것인지 이해가 잘 안되어서요..
5년 전
조건문을 피해가지 않습니다. 처음을 제외하고는 매번 조건문이 실행됩니다.
5년 전
앗..! 그렇네요. 게시하신지 오래돼서 바로 확인하실줄 몰랐는데 감사합니다ㅎㅎ
7년 전
오랜만에 들어와서 새로운거 배우고 갑니다.