DebounceThrottle는 JavaScript에서 자주 사용되는 최적화 기법으로, 자주 발생하는 이벤트(ex> 스크롤, 입력 등)의 호출 빈도를 줄여 성능을 개선하는 방법. 개념은 비슷하지만 동작 방식이 다름

1. Debounce (디바운스)

Debounce는 여러 번 발생하는 이벤트 중 마지막 이벤트만 실행.

이벤트가 발생하면 타이머를 설정하고, 일정 시간이 지나기 전에 동일한 이벤트가 다시 발생하면 타이머를 리셋. 설정된 시간이 지나면 마지막 이벤트가 실행.

사용 예시:

예시 코드(dessert-gallery 사용, lodash라이브러리 사용 시에도 구현 가능)

import { useEffect, useState } from "react";

export const useDebounce = (value: string, delay: number, setIsLoading?:React.Dispatch<React.SetStateAction<boolean>>) => {
  const [debounceValue, setDebounceValue] = useState(value);

  useEffect(() => {
    const debounceTimer = setTimeout(() => {
      setDebounceValue(value);
      try {
        if(setIsLoading!==undefined){
          setIsLoading(false);
        }
      }catch (error) {
      }
    }, delay);

    return () => {
      //useEffect 내부에서 return을 하게 되면 컴포넌트가 제거될 때 해당 코드들이 실행된다
      clearTimeout(debounceTimer);
    };
  }, [value, delay]);

  return debounceValue;
};

///////////////////////////////////////////// 사용 예시
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import ChatList from "./components/ChatList";
import ChatRoom from "./components/ChatRoom";
import { getChatRoom, getSearchChatRoom, getUserInfo } from "../../../apis/controller/chatPage";
import { loginPageApi } from "../../../apis/controller/loginPage";
import { StompClientProvider } from "./context/StompClientProvider";
import { useRoomInfoState } from "../../../recoil/chat/roomInfoStateAtom";
import { useDebounce } from "../../../hooks/useDebounce";

export type userInfoType = {
  nickname: string;
  loginType: "NORMAL" | "KAKAO";
  userRole: "USER" | "MANAGER";
};

export type roomInfoType = {
  roomId: number;
  storeName: string;
  customerName: string;
  lastChatDatetime: string;
  thumbnailMessage: string;
  storeId: number;
};

function ChatPage() {
  const [chatRoomList, setChatRoomList] = useState<roomInfoType[]>();
  const [userInfoState, setUserInfoState] = useState<userInfoType>();
  const [roomInfoState, setRoomInfoState] = useRoomInfoState();
  const [searchKeyword, setSearchKeyword] = useState<string>('');
  const debouncedSearchChatRoom = useDebounce(searchKeyword, 500);
  const [isLoading, setIsLoading] = useState<boolean>(false);

  const fetchChatRoom = async () => {
    const chatRoom = await getChatRoom();
    console.log(chatRoom);
    setChatRoomList(chatRoom.chatList);
    setChatRoomList(chatRoom.chatList);
    setIsLoading(false)
  };

  const fetchSearchChatRoom = async (keyword:string) => {
    const chatRoom = await getSearchChatRoom(keyword);
    setChatRoomList(chatRoom.chatList);
    setIsLoading(false)
  }

  const fetchUserInfo = async () => {
    const userInfo = await getUserInfo();
    console.log(userInfo);

    setUserInfoState(userInfo);
  };

  useEffect(() => {
    fetchUserInfo();
    if(searchKeyword.length !== 0 && debouncedSearchChatRoom){
      fetchSearchChatRoom(debouncedSearchChatRoom);
    }else{
      fetchChatRoom();
    }
    return () => {
      setRoomInfoState({
        roomId: 0,
        storeId: 0,
        partnerName: "",
      });
    };
  }, [debouncedSearchChatRoom, searchKeyword.length]);

  return (
    <Layout>
      <Wrapper>
        <ChatList chatRoomList={chatRoomList} isLoading={isLoading} loadingHandler={setIsLoading} chatSearchValue={searchKeyword} chatSearchHandler={setSearchKeyword} userInfo={userInfoState} />
        <StompClientProvider userInfo={userInfoState}>
          {roomInfoState.roomId === 0 ? (
            <NoItemAlert>선택된 채팅방이 없습니다.</NoItemAlert>
          ) : (
            <ChatRoom userInfo={userInfoState} />
          )}
        </StompClientProvider>
      </Wrapper>
    </Layout>
  );
}

export default ChatPage;

2. Throttle (쓰로틀)

Throttle은 이벤트가 여러 번 발생하더라도 일정 시간 간격으로만 실행되도록 제한. 지정된 시간 동안에는 한 번만 이벤트가 실행되며 그 시간 이후 다시 실행 가능.

사용 예시:

예시 코드:

function throttle(func, limit) {
  let inThrottle;
  // 함수 리턴 
  return function(...args) {
    // 넘겨받은 함수 func를 현재의 컨텍스트(this)와 함께 실행하며,
    // 함수에 전달된 인자(arg)도 그대로 전달
    if (!inThrottle) {
      func.apply(this, args);
      // 재 실행 방지를 위한 inThrottle값 변환(true)
      inThrottle = true;
      // 일정 시간이 지나고 재실행이 가능하도록 inThrottle 변환(false)
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

const handleScroll = throttle(() => {
  console.log('Scroll event 발생!');
}, 1000);

window.addEventListener('scroll', handleScroll);

차이점 요약:

따라서, Debounce는 사용자가 입력을 마쳤을 때 같은 작업을 반복하지 않도록 방지하는 데 유용하고, Throttle은 주기적으로 자주 발생하는 이벤트를 제한하는 데 적합.