관련지식
javascript, event, debouncing, throttling

혹시 마우스 이동이나 스크롤 이벤트를 사용해 봤습니까? 조그만 움직임에도 매우 많은 이벤트가 발생합니다. 이벤트 기반으로 동작하는 자바스크립트에서 과도한 이벤트의 발생은 성능 저하를 초래하기 때문에 이벤트를 제어하는 방법이 반드시 필요합니다.

아래의 상황을 보고 어떻게 동작하는게 맞을지 잠시 생각해보겠습니다.

  1. 네이버지도, 다음지도 등에서 스크롤 휠을 통해 지도를 확대/축소 하기
  2. Text 필드에서 검색어를 입력할때 자동완성이나 연관검색어 기능을 제공하기

먼저 지도 확대축소를 생각해보겠습니다. 네이버지도 등에서 스크롤 휠을 움직일 경우 지도가 확대/축소하게 됩니다. 그런데 만약 가장 확대된 지도 상태에서 중간단계로 축소 시키고 축소된 지도 정보로 UI가 업데이트 되는중에 더욱 축소시키면 이전에 진행중인 UI 업데이트를 취소하고 다시 UI를 갱신해야 할 것입니다.

입력중인 검색어에 대한 자동완성이나 연관검색어를 제공하기 위해선 키보드 입력시 입력되는 문자열을 서버로 전달해 결과를 받아와야 합니다. ‘제목입니다’ 라는 검색어를 입력할때 매 입력시 조회를 할 경우 ‘ㅈ’, ‘제모’, ‘제목ㅇ’ 등 의미없는 서버 호출이 반드시 일어나게 됩니다. 효율적으로 동작하려면 어떻게 할 수 있을까요?

디바운싱

디바운싱은 이벤트가 빈번하게 발생할때 마지막 이벤트만 실행하는 방법입니다.

아래 소스는 키보드 입력이 될때 ajax를 호출하는 소스입니다. setTimeout() 함수에서 0.3초 딜레이를 주었기 때문에 0.3초 이전에 키보드 입력이 이어질 경우 clearTimeout() 함수로 타이머를 취소시키고 새로운 타이머를 만들게 됩니다. 즉 0.3초라는 딜레이가 기준이 되어 마지막 타이머만 동작하는 구조입니다.

  1. $("#input1").on('keyup', function(e) {
  2. var $this = $(this);
  3. if($this.val() == '') { //검색어가 비어있을땐 조회 안함
  4. $search_results.html("");
  5. return;
  6. }
  7. var filter = '{"where":{"title":{"like":"^' + $this.val() + '"}}}';
  8. if(searchTimer) {
  9. clearTimeout(searchTimer);
  10. }
  11. searchTimer = setTimeout(function() {
  12. //입력된 글자로 시작하는 내용들 검색
  13. $.ajax({
  14. url : '/api/noraebooks?filter=' + encodeURIComponent(filter),
  15. dataType : 'json',
  16. async : true,
  17. success : function(rep) {
  18. var options = "";
  19. for(var i = 0; i < rep.length; i++) {
  20. options += "<option>" + rep[i].title + "</option>";
  21. }
  22. $search_results.html(options);
  23. }
  24. });
  25. }, 300);
  26. }).focus();

쓰로틀링

쓰로틀링은 이벤트가 발생하여 실행 중일때, 실행된 이벤트가 끝나기전에 발생하는 이벤트는 무시하는 방법입니다.

아래 소스 또한 키보드 입력이 될때 ajax를 호출하는 소스입니다. 디바운싱과 마찬가지로 setTimeout() 함수를 통해 실행주기를 제어하려고 합니다. 그러나 디바운싱과 다른점은 clearTimeout() 함수로 타이머를 취소 시키는 것이 아니라 실행되는 함수가 종료되기 전까지 이벤트 발생을 무시하는 것입니다. 따라서 이벤트가 연속으로 빠르게 발생할 경우 타이머가 만들어지고 타이머가 실행되는 중에 발생하는 모든 이벤트는 무시하게 됩니다. 타이머가 종료된 후에 이벤트가 발생하면 다시 타이머를 만들며 실행합니다.

  1. $("#input1").on('keyup', function(e) {
  2. var $this = $(this);
  3. if($this.val() == '') { //검색어가 비어있을땐 조회 안함
  4. $search_results.html("");
  5. return;
  6. }
  7. var filter = '{"where":{"title":{"like":"^' + $this.val() + '"}}}';
  8. if(searchTimer) {
  9. return;
  10. }
  11. searchTimer = setTimeout(function() {
  12. //입력된 글자로 시작하는 내용들 검색
  13. $.ajax({
  14. url : '/api/noraebooks?filter=' + encodeURIComponent(filter),
  15. dataType : 'json',
  16. async : true,
  17. success : function(rep) {
  18. console.log("called");
  19. var options = "";
  20. for(var i = 0; i < rep.length; i++) {
  21. options += "<option>" + rep[i].title + "</option>";
  22. }
  23. $search_results.html(options);
  24. searchTimer = null;
  25. }
  26. });
  27. }, 300);
  28. }).focus();

정리

디바운싱과 쓰로틀링은 각기 다른 특징을 가지고 있습니다.

디바운싱 -> 마지막 이벤트만 실행
쓰로틀링 -> 이벤트의 일정 주기로 실행

자 그럼 처음 언급했던 두가지 상황에 어떤식으로 적용하면 좋을지 보겠습니다.

  1. 네이버지도, 다음지도 등에서 스크롤 휠을 통해 지도를 확대/축소 하기
  2. Text 필드에서 검색어를 입력할때 자동완성이나 연관검색어 기능을 제공하기

지도의 경우 마우스를 빠르게 스크롤할 경우 중간의 축척을 굳이 처리할 필요 없이 마지막 스크롤에 대한 축척으로 지도를 그려주면 될것입니다. 디바운싱을 적용하면 되겠죠.

키보드 입력은 어느쪽으로 해도 상관없을것 같습니다. 디바운싱을 적용하면 입력이 끝난후에 검색을 하게 될 것이고 쓰로틀링을 적용하면 입력중에도 검색 결과를 계속 갱신해서 보여주겠지만 특별한 요건이 없다면 둘다 괜찮을것 같습니다. 화면 기능이 조금 달라질 뿐이죠.

디바운싱과 쓰로틀링은 특정한 함수를 지칭하는 것이 아닌 방법에 대한 지칭이지만 자바스크립트 라이브러리 underscore.jslodash 에서는 debouncethrottle 이름으로 함수를 제공하고 있으니 참고 바랍니다.