HighLights 검색기능 구현

김태영·2023년 7월 5일
0
post-thumbnail

프로젝트 초기에는 상품정보 전체를 받아와 해당값들에서 검색값과 일치하는 항목을
highlights 처리해주고 리스트로 뿌려주기만 했었다. 별로 효율적인 방법은 아니었고
백엔드에서 만들어둔 상품필터 기능을 통해 개선해야지 생각하고 있었다.

{
  "filter": [
    {
      "key": "string",
      "operation": "string",
      "value": "string"
    }
  ],
  "page": 0,
  "query": "모자",
  "size": 5,
  "sort": 0
}

여기 들어가는 query 값이 검색기능이다. 예를들어 위처럼 모자를 넣고 api 요청을 보내면
모자 관련 항목이 response로 오는 방식인데 이 방법만으로는 매끄러운 검색창 기능을 구현하지 못할것 같아 고민하던 찰나 백엔드에서 검색기능을 만들어줬다.

검색기능

type SearchValue = {
  keyword: string;
  hlKeyword: string;
};

const ProductListli = styled.li`
  margin-left: 10px;
  position: relative;

  padding: 10px 25px 10px 11px;
  line-height: 25px;
  overflow: hidden;
  text-overflow: ellipsis;
  color: #111;
  border-top: 1px solid #d3d3d3;
  a {
    text-decoration: none;
    color: inherit;
  }

  .highlight {
    color: red;
    font-weight: 600;
  }
  
  
const SearchpopUp = () => {
  // const [productData, setProductData] =
  //   useRecoilState<fakeProduct[]>(ProductListAtom);
  const [searchValue, setSearchValue] = useState<string>("");

  const [productData, setProductData] = useState<SearchValue[]>([]);
  const navigate = useNavigate();
  const getAuto = async (query: string) => {
    const response = await api.get(`cal/v1/autocomplete?query=${query}`);

    setProductData(response.data.body.item);
  };

  const onChange = (e: ChangeEvent<HTMLInputElement>) => {
    const inputValue = e.target.value;
    setSearchValue(inputValue);

    if (inputValue.length >= 1) {
      getAuto(inputValue);
    }
  };

  const highlightMatchingText = (text: string) => {
    if (!searchValue) return text;

    const regex = new RegExp(
      `(${searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")})`,
      "gi"
    );
    const parts = text.split(regex);

    return parts.map((part, index) => {
      if (part.toLowerCase() === searchValue.toLowerCase()) {
        return (
          <span key={index} className="highlight">
            {part}
          </span>
        );
      }
      return part;
    });
  };
  const onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      navigate(`/search/${searchValue}`);
    }
  };
  • span > highlight 클래스를 추가, 해당하는 글자는 빨간색으로 하이라이팅 CSS 생성
  • useEffect를 사용해 입력된 글자값이 바뀔때마다 api 요청을 보냄
  • api response를 useState에 저장, response와 입력한 글자를 비교하여 하이라이팅 처리
  • 표출된 리스트 클릭시 입력된 검색어에 해당하는 페이지로 라우팅과,
      onKeypress에 enter키 지정하여 검색어 입력후 enter키 입력시 페이지 라우팅처리
  • 검색 리스트 페이지

    //App.tsx// route path 설정
    
    <Route path="/search/:keyword" element={<SearchPage />} />
    ///SearchPage.tsx//
    
    const SearchPage = () => {
      const { keyword } = useParams<{ keyword: string }>();
      const [page, setPage] = useState(0);
    
      const getSearchData = async (currentPage: number) => {
        const response = await api.post(`/cal/v1/product`, {
          filter: [],
          page: currentPage,
          query: keyword,
          size: 20,
          sort: [],
        });
        return response.data.body.product;
      };
    
      const { data, isLoading } = useQuery<SearchData>(
        ["getSearch", page, keyword],
        () => getSearchData(page),
        {
          enabled: !!keyword,
          refetchOnWindowFocus: false,
        }
      );
      useEffect(() => {
        setPage(0); // Reset current page when the keyword changes
      }, [keyword]);
    
      const handlePageChange = (page: number) => {
        setPage(page);
      };
      
      return isLoading ? (
        <Loading />
      ) : (
      {... 생략 ...}

    Params를 이용해 구현하는게 맞는건가 싶었지만 마땅히 떠오르는 방법이 없었고
    다른사람의 코드를 보지 않고도 코드를 작성하는 습관을 들이기위해 내가 생각해낸 방법으로 구현했다.

    문제해결

    구현을 다 하고나서 테스트를 해보는데, 최초 검색시에는 검색 리스트가 잘 불러와졌는데,
    검색리스트 페이지에서 재검색을 하니 새로운 데이터를 받아오는데 너무 긴시간이 걸렸다

    나름 최적화 해본다고 enabled, refetchOnWindowFocus 옵션을 추가했지만 결과는 같았고
    useParams로 값을 받아와 해당값으로 요청하고 받아와서 느린건가? 생각도 해보았는데
    useEffect로 keyword가 바뀔때마다 page값을 변경 => 다시 데이터 fetching
    이라 문제 없어보였다.

    다시 테스트해보니 Loading 컴포넌트가 출력이 안됨. 여기서 데이터 refetching이 안되는것을 발견, useQuery 의존성배열에 keyword를 추가해주니 정상 작동하였고 로딩시간도 정상적으로 돌아왔다.

    구현 완료 !!

    회고

    항상 느끼는거지만 당연하다고 생각했던 부분에서 의외로 오류가 많이난다
    이번 검색기능도 useEffect로 충분히 refetching 처리를 해주고 있다고 생각했으나, 작동하지 않았음
    평소에도 코드를 꼼꼼히 봐야겠지만 내가 원하는대로 동작하는데 ? 문제 없어보이는데? 할 때
    더욱 꼼꼼히 봐야겠구나 느낀 하루 컴퓨터는 거짓말 하지 않는다..

    profile
    구렁이

    0개의 댓글