본문 바로가기

카테고리 없음

바닐라 자바스크립트와 리액트 훅으로 디바운스(Debounce) 구현하기

디바운스란 어떤 함수를 연속적으로 호출할 때, 실제 호출되기 전까지 지연 시간을 두어 마지막 함수만 호출되도록 하는 방법입니다. 짧은 시간 안에 너무 많은 함수 호출이 발생하면 성능에 영향을 미칠 수 있기 때문에, 이러한 방법을 이용하여 함수가 호출되는 속도를 제한할 필요가 있습니다. 

 

이번 글에서는 디바운스를 구현하는 두 가지 사례를 알아보겠습니다. 첫 번째로 바닐라 자바스크립트를 이용해서 디바운스 함수를 작성해보겠습니다. 다음으로는 리액트 훅으로 디바운스를 구현해보겠습니다.

 

 

Debouncing width JavaScript

예를 들어 검색어를 입력하면 검색 결과가 자동으로 화면에 출력되는 기능이 있다고 하겠습니다. 간단하게 생각하면 입력 이벤트가 발생할 때마다 서버로 검색 조건에 맞는 데이터를 요청하고 받아오는 것입니다. 아래 사진의 console.log 메서드를 ajax 요청이라 생각하면 "자바스크립트"라는 단어를 검색하기 위해서 매 타자마다 서버로 요청을 보낸 셈입니다. 이런 방식은 쓸모없는 부하를 발생시킵니다.

 

 

document.querySelector(".search-box").addEventListener("input", (event) => {
  // Api Call
  console.log(event.target.value);
});

 

 

디바운스 함수를 이용하면 위의 방식을 개선할 수 있습니다. 스코프와 클로저에 대해서 알고 있다면 디바운스 함수를 작성할 수 있습니다. 디바운스 함수는 함수와 지연 시간을 인자로 받아 익명 함수를 반환합니다. 지연 대기 시간 내에 새로 함수를 호출하지 않으면 마지막으로 호출된 함수의 결과가 나타나게 됩니다.

 

마지막 호출의 결과만 나타난 것을 확인할 수 있습니다.

 

 

document.querySelector(".search-box").addEventListener(
  "input",
  debounce((event) => {
    // Api Call
    console.log(event.target.value);
  }, 1000)
);

function debounce(func, delay) {

  // timeoutID를 할당할 변수를 선언합니다.
  let timer = null;
  
  // timer 변수에 접근 가능한 클로저 함수를 반환합니다.
  return function (...args) {
  
    // 만약 타이머가 존재한다면 타이머를 취소시킵니다.
    if (timer) {
      clearTimeout(timer);
    }
    
    // 타이머를 새로 만듭니다.
    timer = setTimeout(() => func(...args), delay);
  }
}

 

 

Debouncing with React Hook

useState와 useEffect를 이용하면 useDebounce라는 훅을 만들 수 있습니다. 이 훅은 값과 지연 시간을 인자로 받아 일정 시간 후에 입력받은 값을 반환하는 기능을 합니다. 해당 훅의 원문은 usehooks.com/useDebounce/입니다.

 

import { useState, useEffect } from "react";

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(timer);
    };
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;

 

 

Api 호출에 해당 훅을 다음과 같이 사용할 수 있습니다. 

 

import React, { useState, useEffect } from "react";
import useDebounce from "Hooks/useDebounce";

function App() {
  const [searchTerm, setSearchTerm] = useState("");

  const debouncedSearchTerm = useDebounce(searchTerm, 1000);

  const searchItems = async (search) => {
    // API Call
  };

  useEffect(() => {
    searchItems(debouncedSearchTerm);
    // debouncedSearchTerm가 바뀔 때만 재실행됩니다.
  }, [debouncedSearchTerm]);

  return (
    <input onChange={event => setSearchTerm(event.target.value)}/>
  );
};

export default Search;

 

위의 훅을 간단히 설명하면 이렇습니다. 지연 시간 1초 안에 새로운 검색어가 입력되지 않으면 마지막으로 입력된 검색어를 기준으로 debouncedSearchTerm 변수의 값이 갱신됩니다. 컴포넌트가 리렌더링된 이후 debouncedSearchTerm의 값이 변했다면 effect를 재실행합니다.