[React] Debounce 와 Throttle

이원찬·2024년 11월 5일

React

목록 보기
16/17

리액트에서는 성능 최적화를 위해 최소한의 상태 변경과 최소한의 콜백 호출을 필요로 한다.

예를들어 보자

예시: 실시간 검색 기능 구현 시

검색 입력창에서 사용자가 키보드 입력마다 서버에 API요청을 보내게 구현하였다면?

<input type="text" onChange={(e) => fetchSearchResults(e.target.value)} />

사용자가 타이핑을 빠르게 한다면 서버에 과부하가 걸릴 위험이 있다…

예시: 윈도우 리사이즈 이벤트 처리 시

브라우저의 창 크기를 알기 위해 브라우저의 resize 이벤트 마다 계산을 하는 함수가 있다고 해보자

useEffect(() => {
  window.addEventListener('resize', handleResize);
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

resize이벤트가 호출될때마다 resize함수가 호출되는 것은 성능저하를 일으킬 위험이 있다.

해결 방법

위 같은 불필요한 상태변화와 콜백호출을 줄이는 방법중 대표적인 두가지 방법이 있다.

아래 두가지는 연속된 이벤트호출을 어떻게 해결하는 지에 대한 스킬이다.

Debounce

이벤트가 연속으로 일어난다면 호출하지 않고 있다가 잠잠해 졌을때 (일정시간동안 이벤트가 안일어날때) 마지막 이벤트를 처리한다.

  • 동작원리 마지막 이벤트 이후 일정 시간이 지나야 이벤트가 호출됨 (연속된 이벤트 중 1번만 실행된다.)

Throttle

이벤트가 연속으로 일어난다면 모든 이벤트를 처리 하지 않고 일정 간격으로 이벤트를 처리한다.

  • 동작원리 이벤트를 일정 간격으로 호출 ( 1번 이상의 이벤트가 실행된다. )

글로만 봐서는 이해가 잘 가지 않을수 있다.

실제 코드와 구현 방식을 보며 확인해보자

useDebounce 커스텀 훅

export default function useDebounce<T>(value: T, delay: number) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

간단하다 value 상태가 변경될때마다 delay 뒤에 상태가 변경되게 한다.

만약 delay 가 지나기 전에 value의 상태가 변경된다면

delay를 초기화 한다.

function App() {
  const [value, setValue] = useState("");
  const debouncedValue = useDebounce(value, 500);
  return (
    <div>
      <input
        onChange={(e) => setValue(e.target.value)}
        className="outline-none border-2 w-full"
        type="text"
      />
      <div>그냥 값 : {value}</div>
      <div>debounce 된 값 : {debouncedValue}</div>
    </div>
  );
}

useThrottle 커스텀 훅

export default function useThrottle<T>(value: T, limit: number): T {
  const [throttledValue, setThrottledValue] = useState(value);
  const lastRan = useRef(Date.now());

  useEffect(() => {
    const handle = setTimeout(() => {
      setThrottledValue(value);
      lastRan.current = Date.now();
    }, limit - (Date.now() - lastRan.current));

    return () => {
      clearTimeout(handle);
    };
  }, [limit, value]);

  return throttledValue;
}

나도 처음에 코드를 이해하는데 시간이 걸렸다.

하지만 일정 시간이 흐를때마다 handle함수가 호출될 뿐이라는것…

function App() {
  const [value, setValue] = useState("");
  const throttledValue = useThrottle(value, 500);
  return (
    <div>
      <input
        onChange={(e) => setValue(e.target.value)}
        className="outline-none border-2 w-full"
        type="text"
      />
      <div>그냥 값 : {value}</div>
      <div>throttle 된 값 : {throttledValue}</div>
    </div>
  );
}

profile
소통과 기록이 무기(Weapon)인 개발자

0개의 댓글