[쇼핑몰 사이트 만들기] #2-6. 검색 기능 만들기

ppmyor·2022년 8월 31일
0
post-thumbnail

드디어 검색 기능 구현이다. 유저가 입력한 값에 따라 DB에 있는 값과 일치하는지 여부를 따져 일치한다면 노출시켜주자.

목차

  1. SearchFeature Component 만들기
  2. Search 기능을 위한 UI 만들기
  3. onChange Function 만들기
  4. search Data를 부모 컴포넌트에 업데이트 하기
  5. 검색값을 이용한 getProductFunction을 작동시키기
  6. Search 기능을 위해서 getProduct Route 수정하기
  7. Product Model 수정

1. 🆕 SearchFeature Component 만들기

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>
	)
}

우선은 검색과 관련된 기능을 구현해 줄 검색 기능 관련 컴포넌트를 하나 생성해준 뒤 랜딩 페이지에 컴포넌트를 넣어준다.

2. 🎨 Search 기능을 위한 UI 만들기

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 컴포넌트를 감싸서 해당 컴포넌트의 위치를 조정해준다.

3. ✨ onChange Function 만들기

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을 만들어주자!

🆙 4. search Data를 부모 컴포넌트에 업데이트 하기

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을 관리해준다.

✊ 5. 검색값을 이용한 getProduct Function을 작동시키기

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를 작동시켜서 검색에 맞는 상품을 가져와주는 과정이 필요하다.

🔥 6. Search 기능을 위해서 getProduct Route 수정하기

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를 사용하여 구현해주자.

🔧 7. Product Model 수정

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"으로 세팅해서 페이지가 랜더링되었을 때 접혀있을 수 있도록 세팅해주자.

➕ 참고

따라하며 배우는 노드, 리액트 시리즈 - 쇼핑몰 사이트 만들기 를 공부하며 작성한 글입니다.

profile
유영하는 개발자

0개의 댓글