프로젝트 초기에는 상품정보 전체를 받아와 해당값들에서 검색값과 일치하는 항목을
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}`);
}
};
//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 처리를 해주고 있다고 생각했으나, 작동하지 않았음
평소에도 코드를 꼼꼼히 봐야겠지만 내가 원하는대로 동작하는데 ? 문제 없어보이는데? 할 때
더욱 꼼꼼히 봐야겠구나 느낀 하루 컴퓨터는 거짓말 하지 않는다..