React 한글 검색 - React hangeul geomsaeg

input박스에서 검색어를 치면 자동완성 기능을 만드려고 한다.

우리가 검색을 하려고 하는 기능은

1. 책검색

2. 사용자가 올린 태그 기반으로 컬렉션 검색


태그로 컬렉션을 검색하는 것은
태그가 별로 없기 때문에 
글자를 칠때마다 요청을 보내는 것보다는 한번에 받아와서 프론트에서 처리하는게 낫다고 생각했다.

그래서 먼저 태그를 배열로 한번에 받은 다음에 Hint라는 라이브러리를 사용해서 구현했다.

이것은 다음 게시물에 담겠다.


그런데 책 검색 같은경우에는 책이 워낙 방대하다보니 한번에 받아오는 것은 불가능했다.

그래서 2글자 이상 쳤을때 디바운스를 이용하여 일정 시간동안 이벤트가 없으면 서버에 검색요청이 가도록 했다.

그리고 서버에서 받아온 값을 리덕스에 저장하고 리덕스에 있는 값을 밑에 띄웠다.

React 한글 검색 - React hangeul geomsaeg
React 한글 검색 - React hangeul geomsaeg

여기서 클릭을 하면 클릭한 값이 input박스의 value로 들어가도록 만들었고 바로 검색이 되도록 하고싶다.

React 한글 검색 - React hangeul geomsaeg

여기서 같은 값이 나오기도 한다. 이게 보니까 체험판도 있고 무슨 한정판도 있고 그래서 저게 중복으로 나오는거다.

React 한글 검색 - React hangeul geomsaeg

한가지 문제가 더 생겼다.

제목을 클릭하면 저렇게 태그가 같이 들어갈때도 있다.

text =p?.title.replace(/<(\/)?([a-zA-Z]*)(\s[a-zA-Z]*=[^>]*)?(\s)*(\/)?>/ig, "");

이 식을써서 정규식으로 태그를 없애줬다.

React 한글 검색 - React hangeul geomsaeg

검색페이지

import React, { useEffect, useState, useRef, useCallback ,useMemo} from "react";
import styled from "styled-components";
import Color from "../../shared/Color";
import SearchIcon from "@material-ui/icons/Search";
import { useDispatch, useSelector } from "react-redux";
import { actionCreators as reviewActions } from "../../redux/modules/review";
import { actionCreators as bookActions} from "../../redux/modules/book";
import { actionCreators as searchActions } from "../../redux/modules/search";
import { Hint } from 'react-autocomplete-hint';
import SelectBookCard from "../../components/SelectBookCard";
import BookCard from "../../elements/BookCard";




const Search = (props)=>{
  const dispatch = useDispatch();
  const alltags = useSelector(state=> state.review.all_tags);
  const search_book_list = useSelector(state=> state.search.search_book_list);
  const [auto, setAutoComplete] = useState(false);
  const search_book_title = useSelector(state=> state.search.search_book_title);
  const text = useRef();
  
  useEffect(()=>{
    dispatch(reviewActions.getAllTagsSV());
    return(
      dispatch(searchActions.resetSelectedBook())
    )
  },[])

  const searchBook = ()=>{
    dispatch(searchActions.getSearchBooksSV(text.current.value))
  }

  
  let timer;
  const search = ()=>{
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(function() {
      dispatch(searchActions.getSearchBooksSV(text.current.value))
      setAutoComplete(true)
    }, 1000);
  
  }
    return(
        <Container>
            <SearchBarBox >
            <SearchIcon />
            <Hint options={alltags} allowTabFill>
                <input 
                style={{
                  width: "290px",
                  height: "48px",
                  color: `${Color.gray}`,
                  border: "none",
                  borderRadius: "12px",
                  backgroundColor: `${Color.mainColor}`,
                  padding: "0px 0px 0px 10px"
                }}
                placeholder="책이름, 저자명 등으로 검색해보세요" 
                onKeyPress ={(e)=>{
                  if(e.key === "Enter"){
                    searchBook()
                    setAutoComplete(false)
                  }
                }}
                onChange = {(e)=>{
                  if(text.current.value.length >=2){
                    search()
                  }
                }}
                ref={text}
                />
            </Hint>
               
            </SearchBarBox>
            <Wrapper>
              {/* 자동완성부분 */}
            {
             auto && <Autocomplete>
              
              {
                search_book_title?.map((p)=> {
                  return(<Title 
                    key= {p?.isbn} 
                    dangerouslySetInnerHTML={{__html: p?.title.split("(")[0]}}
                    onClick={()=>{
                      text.current.value =p?.title.replace(/<(\/)?([a-zA-Z]*)(\s[a-zA-Z]*=[^>]*)?(\s)*(\/)?>/ig, "").split("(")[0] 
                      searchBook()
                      setAutoComplete(false)
                    }}
                    ></Title>)
                })
              }
             </Autocomplete>
           }

        {/* 책 나오는 부분 */}
        <Grid>
           {
             !auto && search_book_title?.map((p,idx)=>{
               return(<BookCard key={idx} {...p}></BookCard>)
             })
           }
            </Grid>
           </Wrapper>
        </Container>
    )
}


const Container = styled.div`
background: ${Color.mainColor};
width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
`;


const SearchBarBox = styled.div`
  width: 90%;
  height: 50px;
  border-bottom: 2px solid ${Color.gray};
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  background-color: ${Color.mainColor};
  margin-top: 20px;
`;

const SearchBar = styled.input`
  width: 100%;
  height: 48px;
  color: ${Color.gray};
  border: none;
  border-radius: 12px;
  background-color: ${Color.mainColor};
  :focus {
    outline: none;
  }
  ::placeholder {
    color: ${Color.gray};
  }
  padding: 0px 0px 0px 10px;
`;

const Autocomplete = styled.div`
width: 100%;
height: 500px;
padding-left: 20px;
`;


const Title = styled.div`
`;
const Wrapper = styled.div`
background:  ${Color.mainColor};
width: 100%;
min-height: 90vh;
height: 100%;
margin-top: 20px;
`;
const Grid = styled.div`
width: 100%;
height: 100%;
display: grid;
flex-direction: row;
grid-template-columns: 1fr 1fr 1fr;
`;

export default Search;