드디어 검색 기능 구현이다. 유저가 입력한 값에 따라 DB에 있는 값과 일치하는지 여부를 따져 일치한다면 노출시켜주자.
src/components/views/LandingPage/LandingPage/Sections/SearchFeature.js 생성
import React from "react";
const SearchFeature = () => {
return <div>SearchFeature</div>;
};
export default SearchFeature;
src/components/views/LandingPage/LandingPage.js 수정
import SearchFeature from "./Sections/SearchFeature";
function LandingPage() {
//...
return (
<div>
//...
{/* Search */}
<SearchFeature />
</div>
)
}
우선은 검색과 관련된 기능을 구현해 줄 검색 기능 관련 컴포넌트를 하나 생성해준 뒤 랜딩 페이지에 컴포넌트를 넣어준다.
src/components/views/LandingPage/LandingPage/Sections/SearchFeature.js 수정
import React from "react";
import { Input } from "antd";
const { Search } = Input;
const SearchFeature = () => {
return (
<div>
<Search placeholder="input search text" onSearch={(value) => console.log(value)} style={{ width: 200 }} />
</div>
);
};
export default SearchFeature;
디자인은 ant Design의 search를 이용했고, onSearch 함수는 우선 임의로 구현해 위치만 잡아두고 추후 구현하도록 하자.
src/components/views/LandingPage/LandingPage.js 수정
import SearchFeature from "./Sections/SearchFeature";
function LandingPage() {
//...
return (
<div>
//...
{/* Search */}
<div style={{ display: "flex", justifyContent: "flex-end", margin: "1rem auto" }}>
<SearchFeature />
</div>
</div>
)
}
SearchFeature 컴포넌트를 감싸서 해당 컴포넌트의 위치를 조정해준다.
src/components/views/LandingPage/LandingPage/Sections/SearchFeature.js 수정
import React, { useState } from "react";
import { Input } from "antd";
const { Search } = Input;
const SearchFeature = () => {
const [SearchTerm, setSearchTerm] = useState("");
const searchHandler = (event) => {
setSearchTerm(event.currentTarget.value);
};
return (
<div>
<Search placeholder="input search text" onChange={searchHandler} style={{ width: 200 }} value={SearchTerm} />
</div>
);
};
export default SearchFeature;
SearchTerm이라는 state를 만들어주고 유저가 입력할 때 마다 setState를 통해 해당 state의 값을 갱신해주는 onChange Function을 만들어주자!
src/components/views/LandingPage/LandingPage.js 수정
const [SearchTerm, setSearchTerm] = useState("");
const updateSearchTerm = (newSearchTerm) => {
setSearchTerm(newSearchTerm);
};
return <SearchFeature refreshFunction={updateSearchTerm} />;
src/components/views/LandingPage/LandingPage/Sections/SearchFeature.js 수정
import React, { useState } from "react";
import { Input } from "antd";
const { Search } = Input;
const SearchFeature = (props) => {
const [SearchTerm, setSearchTerm] = useState("");
const searchHandler = (event) => {
setSearchTerm(event.currentTarget.value);
props.refreshFunction(event.currentTarget.value);
};
return (
<div>
<Search placeholder="input search text" onChange={searchHandler} style={{ width: 200 }} value={SearchTerm} />
</div>
);
};
export default SearchFeature;
props로 부모에게 searchTerm이 바뀌는 부분에 대한 것들을 갱신해주고 부모단에서도 state를 통해 SearchTerm을 관리해준다.
src/components/views/LandingPage/LandingPage.js 수정
function LandingPage() {
const [SearchTerm, setSearchTerm] = useState("");
//...
const updateSearchTerm = (newSearchTerm) => {
let body = {
skip: 0,
limit: Limit,
filters: Filters,
searchTerm: newSearchTerm,
};
setSkip(0);
setSearchTerm(newSearchTerm);
getProducts(body);
};
//...
return (
//...
)
export default LandingPage;
유저가 입력할 때 마다 props를 이용해 LandingPage로 searchTerm이 계속 갱신되는데 이를 이용해서 유저가 입력할 때 마다 getProducts를 수행해준다.
getProducts Function은 가장 처음에 페이지가 랜더링 될 때 한번 작동해주고, 더보기 버튼, 필터등의 변화를 줄 때에도 작동이 되어서 새롭게 아이템을 가져다준다. 마찬가지로 검색을 할때에도 입력마다 getProduct를 작동시켜서 검색에 맞는 상품을 가져와주는 과정이 필요하다.
server/routes/product.js 수정
router.post("/products", (req, res) => {
let term = req.body.searchTerm;
if (term) {
Product.find(findArgs)
.find({ $text: { $search: term } })
.populate("writer")
.skip(skip)
.limit(limit)
.exec((err, productInfo) => {
if (err) return res.status(400).json({ success: false, err });
return res.status(200).json({
success: true,
productInfo,
postSize: productInfo.length,
});
});
} else {
Product.find(findArgs)
.populate("writer")
.skip(skip)
.limit(limit)
.exec((err, productInfo) => {
if (err) return res.status(400).json({ success: false, err });
return res.status(200).json({
success: true,
productInfo,
postSize: productInfo.length,
});
});
}
});
필터 때 find를 추가시켜 해당 필터에 해당하는 값들을 찾는 find를 추가시켰듯 검색하는 term에 대한 find 조건을 하나 더 추가해준다.
.find({ $text: { $search: term } })
find 내부에는 요렇게 적혀있는데 $ 표시가 붙은 것들은 mongoDB에서 제공하는 기능으로 text와 search를 사용하여 구현해주자.
server/models/Product.js 수정
productSchema.index(
{
title: "text",
description: "text",
},
{
weights: {
title: 5,
description: 1,
},
}
);
검색을 했을 시 title과 description에 검색이 걸리는데 weight를 주어서 title이 조금 더 중요하게 걸리도록 설정해준다. 키워드 별로 weight를 주는 것은 mongoDB에서 제공하는 기능이다.
src/components/views/LandingPage/LandingPage/Sections/RadioBox.js 수정
const RadioBox = (props) => {
return (
<Collapse defaultActiveKey={["0"]}>
<Panel header="Price" key="1">
<Radio.Group onChange={handleChange} value={Value}>
{renderRadioBoxLists()}
</Radio.Group>
</Panel>
</Collapse>
);
};
src/components/views/LandingPage/LandingPage/Sections/CheckBox.js 생성 및 수정
const CheckBox = (props) => {
return (
<div>
<Collapse defaultActiveKey={["0"]}>
<Panel header="Continents" key="1">
{renderCheckboxLists()}
</Panel>
</Collapse>
</div>
);
};
해당 필터 박스 기능을 구현할 때 Panel(접히는 부분)에 대한 이름을 적어두지 않았는데 header 부분 안에서 어떻게 해당 Panel이 표시될지 적어주자.
defaultActiveKey 같은 경우는 default로 해당 panel이 펴져있을지 접혀져있을지를 정해주는 부분인데 우리는 "0"으로 세팅해서 페이지가 랜더링되었을 때 접혀있을 수 있도록 세팅해주자.
따라하며 배우는 노드, 리액트 시리즈 - 쇼핑몰 사이트 만들기 를 공부하며 작성한 글입니다.