[JavaScript] debounce & throttle

tamagoyakii·2023년 9월 14일
1

Tamagoyaki Learning

목록 보기
1/3
post-thumbnail

닥터퐁에는 게임 중 상대에게 이모지를 보낼 수 있는 기능이 있다.
체험해 보고 싶다면??? -> 바로 가기

아무튼 닥터퐁에는 게임 중 상대에게 이모지를 보낼 수 있는 기능이 있다. 이모지를 클릭하면 상단의 프로필 이미지가 1.5초간 해당 유저가 사용한 이모지로 대체된다.

위 이미지에서 crab은 현재 메롱 이미지를 클릭한 상태인 것!

const handleEmojiClick = (imgUrl: string) => {
	socket.emit('myEmoji', imgUrl);
};

원래는 이모지 클릭 이벤트에 이런 간단한 핸들러를 넣어놨었다. 하지만 한창 게임 테스팅을 하던 도중 "1.5초간 하나의 이모지가 보일 텐데, 그 사이에 클릭되는 이모지들에 대한 요청은 어떻게 처리하지...?"라는 의문이 들었다. 그러다 예전에 42gg에서 검색 기능을 만들 때 debounce를 사용했던 경험이 떠올랐다. 비슷한 방법으로 처리할 수 있지 않을까? 하다가 throttle을 발견하게 되는데...

debounce vs. throttle

debounce와 throttle은 함수의 호출 횟수를 조정하여 웹 성능이 저하되는 것을 방지하기 위해 사용된다. 실제로 스크롤 이벤트, 클릭 이벤트, 검색 요청 이벤트 등 연속되는 이벤트에서 api 요청이나 DOM 이벤트 실행을 제어하는데 많이 쓰인다고 한다.

debounce와 throttle 모두 이벤트가 발생하면 설정한 대기시간이 지난 후 함수가 실행된다. 둘의 차이는 대기시간 동안 발생하는 새로운 이벤트를 어떻게 처리하는지에 있다.

debounce의 경우 대기시간 동안 새로운 이벤트가 발생하면 대기시간이 초기화되어 처음부터 다시 대기하고, 대기시간이 끝날 때까지 새로운 이벤트가 발생하지 않는다면 함수가 실행된다. 하지만 throttle의 경우 대기시간 중 새로운 이벤트가 발생하면 해당 이벤트를 무시하고 예정된 대기시간이 지난 후 함수를 실행한다.

다음은 내가 이해한 둘의 처리 방식을 그림으로 그려본 것이다.

42gg에서 검색 요청 api에 debounce를 붙였을 때는 키보드 입력이 멈추길 기다렸다가 api 요청을 보내는 형식이었기 때문에 debounce를 사용했다. 하지만 이번에는 연속하여 클릭했을 때 발생하는 이벤트를 무시하기 위한 제어 함수가 필요해 throttle을 사용했다.

현업에서는 lodash와 같은 라이브러리를 사용한다고 한다. 하지만 나는 아직 아기 개발자기 때문에 utils 폴더에 직접 파일을 만들어서 사용해 보기로 했다. 위 그림에 있는 throttle 함수를 구현하면 아래와 같은 형태일 것이다.

export const throttler = <T extends Function>(func: T, delay: number) => {
  let isThrottled = false;

  return (...args: any[]) => {
    if (isThrottled) return;
    isThrottled = true;
    setTimeout(() => {
      func(...args);
      isThrottled = false;
    }, delay);
  };
};

하지만 대기시간이 지난 뒤 이모지를 보내는 것이 아니라, 이모지를 보낸 뒤 대기시간 동안 새로운 요청을 무시하기를 원했기 때문에, 함수 실행 위치를 조금 바꿨다.

export const throttler = <T extends Function>(func: T, delay: number) => {
  let isThrottled = false;

  return (...args: any[]) => {
    if (isThrottled) return;
    isThrottled = true;
    func(...args);			// 함수를 실행하고
    setTimeout(() => {
      isThrottled = false;
    }, delay);				// delay만큼 대기
  };
};

즉, 내가 만든 throttle은 아래와 같은 순서로 작동한다.

tsx 파일에서 throttler 함수를 import 하여 사용해 줬다.

  const handleEmojiClick = (imgUrl: string) =>
    throttler(() => socket.emit('myEmoji', imgUrl), 1500);

이제 이모지를 보내면 1.5초 동안 다른 이모지를 클릭해도 요청이 보내지지 않는다. 후후후... 좀 헷갈리던 개념이었는데, 이번 기회에 완벽하게 알아버렸다. 기분이 좋아~! (^o^)/~~

0개의 댓글