[TIL]9월 30일 검색(Lodash Debounce)

기록하며 공부하자·2021년 10월 2일
0

자유게시판, 물품판매게시판, 커뮤니티 대부분의 게시글을 사용하는 사이트들은 모두 검색기능을 지원하고 있다.

설정에 따라서 제목만 검색하거나, 내용만 검색하거나 제목 + 내용을 검색하거나 아니면 복잡한 필터를 사용해 검색하는 경우도 있다.

기본적인 검색기능

검색기능 전체코드

import { gql, useQuery } from "@apollo/client";
import { useState } from "react";
import styled from "@emotion/styled";
import { v4 as uuidv4 } from "uuid";
const FETCH_BOARDS = gql`
  query fetchBoards($search: String, $page: Int) {
    fetchBoards(search: $search, page: $page) {
      _id
      writer
      title
      createdAt
    }
  }
`;
const Column = styled.span`
  padding: 0px 50px;
`;
const Page = styled.span`
  padding: 0px 10px;
  cursor: pointer;
`;
export default function SearchPage() {
  const { data, refetch } = useQuery(FETCH_BOARDS);
  const [mySearch, setMySearch] = useState();
  const [myKeyWord, setMyKeyword] = useState();
  function onChangeSearch(event) {
    setMySearch(event.target.value);
  }
  function onClickSearch() {
    refetch({ search: mySearch, page: 1 });
    setMyKeyword(mySearch);
  }
  function onClickPage(event) {
    refetch({ search: myKeyWord, page: Number(event.target.id) });
  }
  return (
    <>
      <div>검색 페이지!!!</div>
      검색어 : <input type="text" onChange={onChangeSearch} />{" "}
      <button onClick={onClickSearch}>검색</button>
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <Column>{el.writer}</Column>
          <Column>{el.title}</Column>
          <Column>{el.createdAt}</Column>
        </div>
      ))}
      {new Array(10).fill(1).map((_, index) => (
        <Page key={uuidv4} onClick={onClickPage} id={String(index + 1)}>
          {index + 1}
        </Page>
      ))}
    </>
  );
}

검색기능 구현을 위해서는 물품 리스트가 필요하기 때문에 fetchBoards를 이용해서 가져온 물품을 뿌려준다.

그후 onChange와 state기능을 이용해서 검색기능을 구현한다.
검색창 부분에 mySearch라는 state를 이용해 검색어를 state에 담은 다음

해당 값을 myKeyword라는 값에 담아준후 mykeyword 값을이용해 refetch를 걸어준다.
그후 검색버튼을 누르면 title과 검색값이 일치하는 부분이 검색이 된다.

Lodash Debounce

버튼을 눌러서 검색을 할수도 있지만 검색어를 입력시 바로 해당검색어가 포함된 내용을 보여주는 경우도 많다.

그렇게 검색기능을 구현한다면 한가지 문제가 생긴다.
검색어를 입력할때마다 서버에 요청을 보낸다면 엄청난 트래픽 낭비가 될수 있다.

Debounce란 바로 반복적인 동작을 강제적으로 대기하게 하는 기능이다.

Debounce 적용 전체코드

import { useState } from "react";
import styled from "@emotion/styled";
import { useQuery, gql } from "@apollo/client";
import { v4 as uuidv4 } from "uuid";
import _ from "lodash";

const FETCH_BOARDS = gql`
  query fetchBoards($search: String, $page: Int) {
    fetchBoards(search: $search, page: $page) {
      _id
      writer
      title
      createdAt
    }
  }
`;
const Column = styled.span`
  padding: 0px 50px;
`;
const Page = styled.span`
  padding: 0px 10px;
  cursor: pointer;
`;
interface Iprops {
  isMatched: boolean;
}
const MyWord = styled.span`
  color: ${(props: Iprops) => (props.isMatched ? "red" : "black")};
`;
export default function SearchDebouncePage() {
  const { data, refetch } = useQuery(FETCH_BOARDS);

  //   const [mySearch, setMySearch] = useState();
  const [myKeyword, setMyKeyword] = useState();

  const getDebounce = _.debounce((data) => {
    refetch({ search: data, page: 1 });
    setMyKeyword(data);
  }, 500);
  function onChangeSearch(event) {
    // setMySearch(event.target.value);
    getDebounce(event.target.value);
  }
  //   function onClickSearch() {
  //     refetch({ search: mySearch, page: 1 });
  //     setMyKeyword(mySearch);
  //   }
  function onClickPage(event) {
    refetch({ search: myKeyword, page: Number(event.target.id) });
  }
  return (
    <>
      <div>검색어 입력</div>
      검색하기 : <input type="text" onChange={onChangeSearch} />{" "}
      {/* <button onClick={onClickSearch}>검색</button> */}
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <Column>{el.writer}</Column>
          <Column>
            {el.title
              .replaceAll(myKeyword, `#$#${myKeyword}#$#`)
              .split("#$#")
              .map((el) => (
                <MyWord key={uuidv4()} isMatched={myKeyword === el}>
                  {el}
                </MyWord>
              ))}
          </Column>
          <Column>{el.createdAt}</Column>
        </div>
      ))}
      {new Array(10).fill(1).map((_, index) => (
        <Page key={uuidv4} onClick={onClickPage} id={String(index + 1)}>
          {index + 1}
        </Page>
      ))}
    </>
  );
}

Debounce 기능을 사용하기 위해서 먼저 lodash를 import 해온다.

import _ from "lodash";

lodash를 import 해온후 _.을 작성하면 사용할수 있는 모든 기능들이 나온다.

getDebounce라는 변수를 하나 만들어준 후 그 속에서 리패치를 진행한다.

const getDebounce = _.debounce((data) => {
    refetch({ search: data, page: 1 });
    setMyKeyword(data);
  }, 500);

,500이라고 적은 부분이 시간인데 해당 시간만큼 추가적인 입력이 없다면, 서버에 state값을 요청한다는 의미이다.

이렇게 되면 검색창에 검색을 할때마다 요청을 보내는것이 아니라 입력을 멈춘후 일정시간이 지날때만 요청을 보내기에 훨씬더 효율적으로 서버에 데이터를 요청할수 있다.

profile
프론트엔드 개발자 입니다.

0개의 댓글