프론트엔드 웹/React

검색할 때 debounce를 활용해서 API 호출 한 번만 하기

세리둥절 2022. 8. 17. 19:21
반응형

✔️ 필요성

검색어를 API에 호출해서 결과를 가져오고 싶다. 그런데 검색어를 한글자씩 바꿀때마다 API를 계속 호출하는 것은 원하지 않는다. 어느정도 글자가 정지했을 때 한 번만 API 호출하고 싶다.

 

debounce와 useMemo를 활용해서 아래와 같이 searchText를 변경한다.

 

검색창에 등장하는 단어인 query는 debounce를 적용하지 않아서 변경 즉시 값이 바뀌도록하고, searchText는 debounce를 적용한다.

 

import React, { useEffect, useRef, useState, useMemo } from "react";
import { debounce } from "lodash";

const DEBOUNCE_TIME = 500;

const SearchInput = ({
  placeholder,
  searchText,
  setSearchText,
  selectedItem,
  setSelectedItem,
  options,
  className,
}: Props) => {
  const [query, setQuery] = useState<string>(searchText); // 검색텍스트(즉각)
  
  /* 눈에보이는 query text는 즉각 변화 */
  const onChangeText = (query: string) => {
    setQuery(query);
  };

  /* 0.5초 debounce로 검색 API 호출 */
  const debouncedSearch = useMemo(
    () =>
      debounce((query: string) => {
        query.length > 0 && setSearchText(query); // 빈칸일 때는 검색하지 않는다
      }, DEBOUNCE_TIME),
    [searchText]
  );

  return (
    <Input
        type="text"
        value={query}
        onChange={(e) => {
          debouncedSearch(e.target.value);
          onChangeText(e.target.value);
        }}
      />
  );
};

export default SearchInput;​

 

 

✔️ 문제 확인

searchText가 바뀌면 데이터를 호출한다. 데이터를 호출하는 부분과 데이터를 보여주는 부분은 컴포넌트를 분리하는 것이 깔끔하다.

const [searchText, setSearchText] = useState<string>(''); // 검색 API에 호출하는 단어

  /* 데이터 호출 */
  const { options, error: optionsError } = useOptionsQuery(searchText);

  return (
    <SearchInput
      options={optionsError ? [] : options} // 옵션값이 없으면 빈칸을 내려보낸다
      searchText={searchText}
      setSearchText={setSearchText}
      selectedItem={selectedItem}
      setSelectedItem={setSelectedItem}
    />

 

useSWR이 써본 것 중에서 제일 깔끔한 것 같다.

import axios from "axios";
import useSWR from "swr";

export const useOptionsQuery = (searchText: string) => {
  const fetcher = (url: string) =>
    axios.get(encodeURI(url)).then((res) => res.data);

  // API주소에 검색어를 넣어서 호출
  const key = `api주소?search=${searchText}`;

  const { data, error } = useSWR(key, fetcher);
  return { data, error };
};

 

반응형