-
이전 글
IceCraft - search검색부분(리팩토링), 디바운싱 적용하기
검색하는 페이지에서 서버 부담을 줄이기 위해 디바운싱을 적용시켜 검색기능 리팩토링을 진행하였다.훅으로 만들어 빼두기import { useState, useEffect } from "react";const useDebounce = (value: string, delay: num
seokachu.tistory.com
이전 글에서 프로젝트를 진행하면서 검색기능에 Debounce를 적용해보았다.
조금 더 개념정리를 하기위해 적어본다!
✨ Debounce 와 Throttleing
`Debounce` : 연속된 이벤트에서 마지막 이벤트만 처리하도록 제어
`Throttleing` : 일정 간격으로 요청을 처리함. 정기적으로 이벤트를 주거나 실행을 해야한다면 Throttleing으로 처리하는것이 좋다.
그래서 두개 차이점이 뭔데?
`throotle`은 정해진 시간에 한번만 요청한다
하지만 `debounce`는 정해진 시간으로 그룹화 된 요청들 중 마지막 요청에 주목하는 것
정기적으로 실행해야한다? = `throotle` = ex) `스크롤 이벤트`
마지막 요청만 남겨도 됨! = `debounce` = ex) `검색창`
요 이미지로 한번에 이해할 수 있다!
우리가 잘 알고있는 일반적인 검색코드! (IceCraft의 프로젝트 검색 영역 부분이다.)
//NOTE - 방 목록 검색 const searchHandler = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!search.trim()) return; try { const rooms = await getRoomsWithKeyword(search); setRooms(rooms); } catch (error) { toast.error("검색 중 오류가 발생했습니다."); } }; return ( <form onSubmit={searchHandler}> <div className={S.roomSearch}> <label htmlFor="search">검색하기</label> <input type="text" id="search" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="방 이름을 입력해 주세요." /> <button> <Image src={SearchIcon} alt="search Icon" /> </button> </div> </form>
이렇게 하면 사용자가 검색어를 한글자씩 쓰게되면
상태변화가 일어날때마다 계속 찍히는 내용을 확인할 수 있다.
ex) 사용자가 안녕 이라는 단어를 입력했을때
ㅇㅏㄴㄴㅕㅇ 총 6번 상태변화가 일어남
프로젝트 적용기(이전 포스팅 참고)
IceCraft 프로젝트에서 검색기능을 구현할때 Debounce를 적용했다.
useEffect를 사용하여 customHook으로 만들어서 사용했다.
import { useState, useEffect } from "react"; const useDebounce = (value: string, delay: number) => { const [debounced, setDebounced] = useState(value); useEffect(() => { const handler = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(handler); }, [value, delay]); return debounced; }; export default useDebounce;
useEffect로 커스텀 훅을 만들어주었고
인자로 value, delay를 넘겨서 value는 값, delay는 시간을 지정해주었다.
value와 delay에 값이 들어오면 setTimeout을 실행하게 된다.
지정한 delay값에 따라 시간이 되면 debounced 상태값을 변경해준다.
setTimeout이 끝나지 않은 handler가 있다고 가정하면 clearTimeout으로 제거해주고, 마지막 요청만 실행된다.
useDebounce 사용해보기
const RoomSearch = () => { const { setRooms } = useGetRoomsSocket(); const [search, setSearch] = useState<string>(""); const debouncedValue = useDebounce(search, 500); //NOTE - 방 목록 검색 const searchHandler = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!search.trim()) return; try { const rooms = await getRoomsWithKeyword(debouncedValue); setRooms(rooms); } catch (error) { toast.error("검색 중 오류가 발생했습니다."); } }; return ( <form onSubmit={searchHandler}> <div className={S.roomSearch}> <label htmlFor="search">검색하기</label> <input type="text" id="search" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="방 이름을 입력해 주세요." /> <button> <Image src={SearchIcon} alt="search Icon" /> </button> </div> </form> ); };
사용방법도 커스텀 훅으로 빼놨기 때문에 간단하게 사용할 수 있다.
useDebounce훅을 import로 불러온다.
searchHandler 함수가 호출될때마다
const debouncedValue = useDebounce(search);
여기 부분에서
input value값을 인자로 넘겨 마지막 요청에 값을 업데이트 해서 debouncedValue에 저장한다.
이렇게 진행하면 debouncedValue값은 업데이트 될 때만 실행된다.
디바운싱 제대로 적용해보기
"use client"; import React, { useEffect, useState } from "react"; import { getRoomsWithKeyword, getRooms } from "@/utils/supabase/roomAPI"; import SearchIcon from "@/assets/images/icon_search.svg"; import S from "@/style/mainpage/main.module.css"; import Image from "next/image"; import { toast } from "react-toastify"; import useGetRoomsSocket from "@/hooks/useGetRoomsSocket"; import useDebounce from "@/hooks/useSearchDebounce"; import { FormSearchProps } from "@/types"; const FormSearch = ({ placeholder }: FormSearchProps) => { const { setRooms } = useGetRoomsSocket(); const [search, setSearch] = useState<string>(""); const debouncedValue = useDebounce(search, 500); //NOTE - 메인페이지 방 목록 검색 const searchHandler = (e: React.ChangeEvent<HTMLInputElement>) => { setSearch(e.target.value); }; useEffect(() => { const fetchRooms = async () => { try { if (!debouncedValue.trim()) { const allRooms = await getRooms(0, 20); setRooms(allRooms); return; } const roomKeyword = await getRoomsWithKeyword(debouncedValue); setRooms(roomKeyword); } catch (error) { toast.error("검색 중 오류가 발생했습니다."); } }; fetchRooms(); }, [debouncedValue]); return ( <> <div className={S.roomSearch}> <label htmlFor="search">검색하기</label> <input type="text" id="search" value={search} onChange={searchHandler} placeholder={placeholder} /> <button type="button"> <Image src={SearchIcon} alt="search Icon" /> </button> </div> </> ); }; export default FormSearch;
0.5초 뒤에 검색결과를 반영하도록 했고, 사용자가 다시 검색창에 내용을 비우면 전체 데이터를 불러오게 리팩토링을 진행하였다.
정리는 여기 링크 참고하여 작성하였음.
Debounce vs Throttle: Definitive Visual Guide
A complete guide to learn the difference between debounce and throttle using visual examples. Never confuse the two again.
kettanaito.com
Debounce? Throttle requests? · Issue #110 · vercel/swr
I'd like to debounce the request for the auto-suggestion example in this repository. So when a user types a search term, it waits 3000 ms before it fires a network request on the search term. I'm n...
github.com
'📚 이론정리 > React & Next.js' 카테고리의 다른 글
✏️ Framer-motion 애니메이션 라이브러리 (0) 2024.08.10 ✏️ EmailJS 라이브러리를 활용하여 문의사항 받기 (1) 2024.06.30 ✏️ 사용자 경험에 맞춘 skelecton 라이브러리 (0) 2024.06.23 ✏️ 커스텀 훅(Custom Hook)과 유틸함수(util function)의 차이점 (0) 2024.05.19 ✏️ 제어 컴포넌트와 비제어 컴포넌트 (0) 2024.05.13 댓글