React/React 문법

[React]Debounce 함수 구현 및 사용

DevStory 2021. 11. 15.

이번 포스팅에서는 React에서 Debounce 기법을 사용한 함수를 구현하는 방법을 소개합니다.

 


Debounce란?

Debounce은 함수를 여러 번 호출하고 마지막 호출에서 일정 시간이 지난 후 해당 함수의 기능이 동작하는 기법입니다.

 

Debounce를 사용하는 경우

Debounce 기법은 검색창에서 자동 완성을 구현해야 하는 경우 또는 마우스를 빠르게 여러 번 클릭했을 때, 마지막 클릭에서 기능이 동작해야 하는 경우 사용할 수 있습니다.

naver 자동완성

네이버처럼 자동 완성 기능이 있는 검색창을 구현해야 한다고 가정합니다. 검색 키워드에 대한 자동완성 리스트를 보여주기 위해서는 키워드를 입력할 때마다 API 요청을 해야 합니다.

 

따라서 "naver"을 검색해야 하는 경우 5번의 API 요청을 합니다.

 

이러한 방법은 상당히 비효율적입니다.

 

Debounce 기법을 사용하면, 함수는 5번 호출되지만 일정 시간이 지난 후 마지막 호출에서 검색 키워드에 대한 API를 요청합니다.

 

API 호출 횟수가 5번에서 1번으로 감소되므로 코드가 좀 더 효율적으로 동작합니다.


Debounce 함수 사용

Debounce 함수는 lodash의 debounce 함수를 사용할 수 있지만, 이번 포스팅에서는 나만의 debounce 함수를 구현하고 사용합니다.

const debounceFunction = (callback, delay) => {
  let timer;
  return (...args) => {
    // 실행한 함수(setTimeout())를 취소
    clearTimeout(timer);
    // delay가 지나면 callback 함수를 실행
    timer = setTimeout(() => callback(...args), delay);
  };
};

debounceFunction 함수는 두 개의 인수를 가집니다.

 

첫 번째는 일정 시간이 지난 후 실행되는 함수이고 두 번째는 지연 시간입니다.

 

두 번째 인수인 delay(지연 시간)이 지나기 전에 함수가 이벤트 핸들러 함수가 실행되면, clearTimeout 함수를 통해 전달받은 첫 번째 인수(콜백 함수)를 실행하지 않습니다.

 

먼저, debounceFunction 함수를 사용하지 않은 클래스 컴포넌트입니다.

class App extends React.Component {
  state = {
    value: ""
  };
  
  printValue = (value) => console.log(value);

  handleChange = (e) => {
    this.printValue(e.target.value);
    this.setState({ value: e.target.value });
  };

  render() {
    const { value } = this.state;
    return (
      <div>
        <input type="text" value={value} onChange={this.handleChange} />
        <div>Value: {value}</div>
      </div>
    );
  }
}

실행 결과

5번의 onChange 이벤트가 발생했고 onChange 이벤트 핸들러 함수인 handleChange가 5번 실행되었습니다.

 

다음은 debouncFunction 함수를 적용한 클래스 컴포넌트입니다.

class App extends React.Component {
  state = {
    value: ""
  };

  printValue = debounceFunction((value) => console.log(value), 500);

  handleChange = (e) => {
    this.printValue(e.target.value);
    this.setState({ value: e.target.value });
  };

  render() {
    const { value } = this.state;
    return (
      <div>
        <input type="text" value={value} onChange={this.handleChange} />
        <div>Value: {value}</div>
      </div>
    );
  }
}

실행 결과


함수형 컴포넌트에서 Debounce 함수 사용

다음은 위에서 설명한 클래스 컴포넌트를 함수형으로 변경하였습니다.

 

하지만, 아래 코드를 실행하면 제대로 동작하지 않는 문제가 발생합니다.

const App = () => {
  const [value, setValue] = useState("");

  const printValue = debounceFunction((value) => console.log(value), 500);

  const handleChange = (e) => {
    printValue(e.target.value);
    setValue(e.target.value);
  };

  return (
    <div>
      <input type="text" value={value} onChange={handleChange} />
      <div>Value: {value}</div>
    </div>
  );
};

이러한 원인은 함수형 컴포넌트 내부의 함수는 컴포넌트가 re-render 되면, 재정의되기 때문입니다.

 

즉, 컴포넌트가 re-render 되면 printValue가 재정의됩니다.

 

printVlaue가 재정의되지 않도록 React Hooks API에서 지원하는 useCallback()을 사용합니다.

const App = () => {
  const [value, setValue] = useState("");

  const printValue = useCallback(
    debounceFunction((value) => console.log(value), 500),
    []
  );
  
  const handleChange = (e) => {
    printValue(e.target.value);
    setValue(e.target.value);
  };

  return (
    <div>
      <input type="text" value={value} onChange={handleChange} />
      <div>Value: {value}</div>
    </div>
  );
};

 

반응형

댓글