Debounce로 성능 최적화하기

Debounce로 성능 최적화하기

·

2 min read

오늘도 어김없이 회사에서 열심히 코드를 보고 또 보다가... Slider 컴포넌트 코드가 눈에 띄어 열심히 톺아보았다. 그런데 한 가지 불편한 점을 발견하게 되었다 👀

바로 슬라이더를 움직일 때마다 함수가 계속 호출되는 것이다. 예를 들어 현재 값이 1인데 100으로 이동한다면 함수가 100번이나 호출되어야 한다는 점!

지금부터 이 부분을 개선하는 방법을 함께 알아보자.

🤔 Slider 움직일 때마다 리소스 소비.. 최선인가

슬라이더 컴포넌트에서 포인터를 좌우로 움직이면 값이 변하는 코드가 있다. 현재 값에서 다음 값으로 이동할 때 그 사이 트랙을 모두 지나는 것이 슬라이더의 핵심이다. 하지만 여기서 치명적인 단점을 발견할 수 있다.

export default function SliderExample() {
  const [value, setValue] = useState(30);

  const handleChange = (event, newValue) => {
    console.log(newValue);
    setValue(newValue);
  };

  return (
    <Box sx={{ width: 300 }}>
      <Slider value={value} onChange={handleChange} />
    </Box>
  );
}

숫자를 하나 지나갈 때마다 handleChange 함수가 호출되어 불필요한 리소스를 소비하는 것이다. 솔직히 중간 값은 자연스럽게 빼먹어도 될 것 같은데... 어떻게 해결할 수 있을까?

🤩 debounce로 최적화 해보자!

debounce를 사용하면 해결할 수 있다. 디바운스란 연속해서 같은 행동을 취해도 오직 한 번만 함수를 실행하는 것이다. 디바운스는 다음과 같이 작성할 수 있다.

  1. 실행 함수인 func와 디바운스 시간 간격인 timeout을 매개변수로 받는 디바운스 함수를 생성한다.

  2. 새로운 호출을 예약하는 데 사용할 timer를 초기화한다.

  3. timer에 값이 존재하는 경우, 즉 이전에 예약된 함수 호출이 있으면 해당 timer를 삭제한다.

  4. timeout이 경과한 후 함수를 실행한다.

const debounce = (func, timeout = 300) => {
  let timer;
  return (...args) => {
    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      func.apply(this, args);
    }, timeout);
  };
};

onChangedebounce 함수를 적용하여 결과를 확인해보자.

  return (
    <Box sx={{ width: 300 }}>
      <Slider value={value} onChange={debounce(handleChange, 20)} />
    </Box>
  );

20ms 사이에 이동한 구간에서는 onChange 함수가 호출되지 않는 것을 확인할 수 있다.

사용자 입장에서 슬라이드가 20ms만큼 느리게 움직여 보이는 단점이 있지만, 성능을 개선하는 목표는 달성하였다. 😋

🛠️ 직접 구현하기 귀찮으니 라이브러리를 써보자

디바운스를 자바스크립트로 구현할 수도 있지만 라이브러리를 사용하여 더욱 쉽게 구현할 수 있다.

npm i --save lodash

import { debounce } from "lodash";

export default function SliderExample() {
  const [value, setValue] = useState(30);

  const handleChange = (event, newValue) => {
    console.log(newValue);
    setValue(newValue);
  };

  return (
    <Box sx={{ width: 300 }}>
      <Slider value={value} onChange={debounce(handleChange, 20)} />
    </Box>
  );
}

정말 간단하다... 💨💨💨

마치며

가벼운 프로젝트에서는 고려할 필요가 없었지만, 인터렉티브한 요소가 많은 페이지에서는 불필요한 리소스를 최소화하는 방법을 고려할 필요가 있다.

나는 슬라이더에만 사용해보았지만 나중에 디바운스가 필요한 경우가 또 발견되면 반가울 것 같다!

레퍼런스

https://www.freecodecamp.org/news/javascript-debounce-example/
mui.com/material-ui/react-slider
lodash.com