React에서 디바운싱 구현하기 (검색어 입력과 추천 검색어 받아오기)

sean·2023년 2월 10일
0

Web

목록 보기
15/22

무수한 API 호출

얼마 전 인턴 채용 사전 과제로 추천 검색어를 띄우는 과제를 받았다.
추천 검색어는 사용자가 입력한 문자열을 토대로 서버에 GET 요청을 보내서 추천 검색어를 받아오고, 그걸 화면에 띄우는 식으로 구현된다.

그런데...!
단순히 input 태그의 onChange 이벤트에다가 추천 검색어를 받아오는 API를 호출하면... 과연 서버가 살아남을 수 있을까?

그래서 우리는 이런 엄청난 연속적인 입력들을 조금씩 묶어서 서버에 GET 요청을 보내야 할 것이다. 이러한 문제를 해결하기 위한 프로그래밍 기법으로써, 스로틀링(Throttling)디바운싱(Deboucing)이 있다.

  • 스로틀링: 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것
  • 디바운싱: 연이어 호출되는 함수들 중 마지막 함수 (또는 제일 처음)만 호출되도록 하는 것
    (cf. underscore.js에는 이미 이러한 기능을 편리하게 사용할 수 있도록 메소드로써 구현되어 있다고 함.)

검색어 입력은 보통 스로틀링보다는 디바운싱을 쓰기 때문에 이번 포스트에서는 디바운싱과, 내가 그걸 어떻게 구현했는지를 풀어보려고 한다.

반면, 스로틀링은 보통 스크롤을 올리거나 내리는 스크롤 이벤트에 사용한다고 한다.

디바운싱(Debouncing)

참고한 블로그: 제로초님의 쓰로틀링과 디바운싱 포스트

위에서 설명한대로, 만약 '에스파'를 검색할 때, input 태그의 onChange 이벤트에다가 API 요청을 보낸다면 한 글자를 칠 때마다 API 요청이 가게 된다.
ex) 'ㅇ', '에', '엣', '에스', '에슾', '에스파' 이렇게 총 6번이나 서버에 요청을 보내게 된다.

서버에 무리가 가는 것 뿐만이 아니라 만약에 우리가 외부의 유료 API를 사용한다고 했을 때, 쿼리를 한 번 날릴 때마다 비용을 지불해야하므로, 비용적인 측면에서도 우리는 이를 최적화할 필요가 있다.

따라서, 우리는 사용자의 매 입력마다 API를 호출하지 않기 위해 사용자의 입력이 다 끝났다고 판단되는 순간 API 요청을 보낼 것이다!! 즉, 타자를 칠 때마다 타이머를 생성하고, 거기에 설정한 제한시간이 다 끝나는 순간 우리는 API 호출을 한다. 물론 제한시간이 끝나기도 전에 새로운 입력이 들어온다면 지금까지의 타이머는 취소(초기화)하고 새로운 타이머를 다시 설정한다.

커스텀 Hooks 만들어서 해결하기

useDebounce

import { useState, useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { searchInputState } from '@/recoil/atoms/searchInputState';
import { recommendListState } from '@/recoil/atoms/recommendListState';

// useDebounce 커스텀 훅
const useDebounce = (value, delay) => {
  const [debouncedValue, setDebouncedValue] = useState(value);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    
    return () => clearTimeout(timer);
  }, [value, delay]);
  
  return debouncedValue;
}
  
// 컴포넌트
const myComponent = () => {
  const [keyword, setKeyword] = useRecoilState(searchInputState);
  const [recommendList, setRecommendList] = useRecoilState(recommendListState);
  const debouncedQuery = useDebounce(keyword, 250);
  
  // 추천 검색어 요청해서 받아오기
  useEffect(() => {
    async function fetchRecommend() {
      cons res = await axios.get(`?input=${debouncedQuery}`);
      setRecommendList(res.data);
    }
    
    if (debouncedQuery) fetchRecommend();
  }, [debouncedQuery]);
  
  return (<input onChange={(e) => setKeyword(e.target.value)} value={keyword} />)
}
profile
여러 프로젝트보다 하나라도 제대로, 깔끔하게.

0개의 댓글