블로그 이름 뭐로 하지

[JavaScript] 디바운싱(Debouncing) 본문

프론트엔드

[JavaScript] 디바운싱(Debouncing)

발등이 따뜻한 사람 2024. 1. 28. 19:35

최근 벨로그 인기글에서 검색 api 사용을 할 때 계속해서 api를 호출하는 것에 대해 js의 디바운스 개념을 활용해서 최적화하는게 좋다는 글을 본 적 있었다. 그 때도 '나중에 ^^' 다시 공부해야겠다하고 넘겼는데, 지금 하고 있는 프로젝트도 그렇고 예전에 했던 프로젝트나 앞으로 할 개발에 있어서도 알아두고 있어야할 개념이라고 생각해서 공부한 내용을 정리하고자 한다.

 

 

디바운스. 어떨 때 써야할까?

스크롤이나 마우스 클릭, api호출 관련한 함수가 불필요하게 너무 많이 실행되는 경우가 있다. 예를 한 번 살펴보자.

사진은 현재 내가 개발 중인 프로젝트이다. 검색어에 따라 지하철 목록을 호출해서 보여줘야 하는데, 현재는 검색어가 ㅅ,서,성,성ㅅ,성수 로 바뀌는 일련의 과정동안 계속해서 api가 호출되고 있다.

당연한 말이지만 몇 초도 안 되는 시간동안 몇십, 몇백개의 함수를 호출하는 것은 작업을 무겁게 만들고, 웹과 서버 모두에게 부담을 줄 수 있다.

 

이를 해결하기 위해 디바운싱을 활용한다. 이 개념은 Javascript 개념이라기 보다는 프로그래밍 기법 중에 하나이다. 따라서 자바스크립트에서 직접적으로 디바운스라는 이름으로 제공하는 함수같은건 없고, web api인 clearTimeoutsetTimeout 를 사용해서 디바운스의 기능을 하는 함수를 작성해야 한다. 즉 일종의 최적화 기법이라고 보면 된다.

 

 

디바운싱 (Debouncing)

💡 연속해서 호출되는 함수들 중 마지막 함수만 호출하도록 하는 것

 

 

searchText가 변경될때마다 지하철목록을 불러오는 api를 호출하는 useGetStations이 실행되기 때문에 사용자가 실수로 길게 검색어를 작성할 때도, 그렇지 않을 때에도 글자를 입력할 때마다 API 호출이 진행되고 있다. 

const data = useGetStations(searchText);

onChange={(e) => setSearchText(e.target.value)}

 

 

디바운스 적용 후 실행 과정

디바운스를 지정하면 다음과 같이 실행 된다.

  • 지정된 딜레이 안에 호출이 여러번 발생 돼도 마지막 1번만 실행한다.
    • ex) 딜레이를 1초로 잡는다면 0.9초 뒤에 버튼을 클릭하고, 0.9초 뒤에 또 버튼을 클릭하고, 또 0.9초 뒤에 버튼을 클릭해도 마지막 클릭만 함수를 호출한다.
  • 기본적으로 함수 호출이 지연되는 것이다. (setTimeout함수의 특징을 생각하자!)
    • ex) 디바운스 딜레이가 1초라면, 1번만 클릭해도 1초 후에 실행된다. (마지막 클릭 이후 1초 뒤에 함수가 실행 된다.)

 

디바운스 적용 코드

 const debounce = <T extends (...args: any[]) => any>(fn: T, delay: number) => {
    let timeout: ReturnType<typeof setTimeout>;

    return (...args: Parameters<T>): ReturnType<T> => {
      let result: any;
      if (timeout) clearTimeout(timeout);
      timeout = setTimeout(() => {
        result = fn(...args);
      }, delay);
      return result;
    };
  };
  
  const onChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchText(e.target.value);
  };
  
  const debouncedOnChange = debounce<typeof onChange>(onChange, 500);
 
.
.
.
return (

 <SearchBar
        ref={inputRef}
        placeholder={cntPage == 'search' ? '지하철 역 이름을 입력해주세요.' : '지하철역'}
        onClick={cntPage == 'home' ? () => router.push('/search') : undefined}
        onChange={debouncedOnChange}
      ></SearchBar>
     .
     .
     .
);

 

위에서 debounce라는 이름의 함수를 훅으로 만들어두면 프로젝트 내에서 디바운스가 필요한 부분이 있을때마다 import해와서 사용할 수 있을 것이다. 이 이에도 이미 디바운스를 구현해둔 라이브러리들이 있어서 불러와서 써줄 수도 있다.

 

 

마무리

이번 썸네일 프로젝트에서 검색어를 구현하는 부분과 무한스크롤 부분을 모두 내가 맡았는데 마침 그 두 부분에서 적용할 수 있는 개념이라고 생각해서 지금이라도 공부하게 되어 참 좋았다! 무한스크롤에는 디바운스보다 쓰로틀링이 적합하다고 하는데 쓰로틀링도 공부하고 적용해본 후 가져와봐야겠다.
또 예전에 했던 티타임 프로젝트에서 setTimeout함수를 사용해서 채팅을 구현했었는데(채팅 로직에는 스크롤도 연관되어 있고... 굉장히 복잡하다) 여기에 디바운스를 적용할 수 있다면 더욱 성능을 최적화를 시킬 수 있을까?하는 생각이 들었다. 티타임 코드도 살펴보고 고칠 수 있는 부분이 있다면 고쳐보고 싶다!