Debounce
와 Throttle
는 JavaScript에서 자주 사용되는 최적화 기법으로, 자주 발생하는 이벤트(ex> 스크롤, 입력 등)의 호출 빈도를 줄여 성능을 개선하는 방법. 개념은 비슷하지만 동작 방식이 다름
Debounce는 여러 번 발생하는 이벤트 중 마지막 이벤트만 실행.
이벤트가 발생하면 타이머를 설정하고, 일정 시간이 지나기 전에 동일한 이벤트가 다시 발생하면 타이머를 리셋. 설정된 시간이 지나면 마지막 이벤트가 실행.
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;
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은 주기적으로 자주 발생하는 이벤트를 제한하는 데 적합.