첫 생각
- Search라는 파일을 만든 후 그 파일에서 modal과 searchProudctList를 관리해준다.
- 그럼 자연스럽게 useState로 관리한 state는 props로 내려 관리가 된다. 이게 첫 search 파일 구성이었다 이렇게 되어야 product 파일들을 한번에 모아 보기 편하다고 생각했다.
const [toggle, setToggle] = useState(false) // 이 state로 props를 넘겨 modal 유무를 관리해 주었다.
또한 Router에도 path를 지정해주었다.
그 결과, Router에 path만 지정해 준 url에서만 modal이 나타났다.스스로 피드백 후 생각
- Search modal 과 SearchProduct는 따로 생각해주어야 한다. 나는 Product만 모이게끔 파일을 정리하고 싶었기 때문이며, SearchModal은 어디서든 나와야 하기 때문이다.
- SearchModal을 nav와 footer처럼 공통 컴포넌트로 생각하자 그럼 어디서든 핸들링이 가능할 것이다.
- searchmodal에 대한 핸들링 스테이트는 redux로 관리해 전역으로 뿌리자 그러면 따로 분리가 가능하다. 이게 내가 다시 생각해낸 생각이다.
// Router <BrowserRouter> <Nav /> <Search /> <Routes> <Route path="/" element={<Main />} /> <Route path="/login" element={<AuthLogin />} /> <Route path="/create" element={<AuthCreate />} /> <Route path="/product" element={<Product />} /> <Route path="/product/:pageId" element={<Detail />} /> <Route path="/mypage" element={<MyPage />} /> <Route path="/cart" element={<Basket />} /> <Route path="/wishlist" element={<WishList />} /> </Routes> <Footer /> </BrowserRouter>
// redux 관련 코드 // redux-toolkit을 사용했다. const initialState = { toggleSearch: false, productSearch: "", }; export const productSlice = createSlice({ name: "productSlice", initialState, reducers: { setToggleSearch: ( state, action: PayloadAction<{ toggleSearch: boolean }> ) => { state.toggleSearch = action.payload.toggleSearch; }, setProductSearch: ( state, action: PayloadAction<{ productSearch: string }> ) => { state.productSearch = action.payload.productSearch; }, }, export const { setToggleSearch, setProductSearch } = productSlice.actions;
일부분만 가져온 코드이다. toggle에 관련된 state, search를 한 검색어에 대한 state에 관련한 코드이다. 이 state들은 전역으로 뿌려 관리해 줄 것이다.
// nav에 searchmodal에 대한 아이콘 코드 import { useDispatch } from "react-redux"; import { AppDispatch } from "../../store"; import { setToggleSearch } from "../../reducers/productSlice"; const navigateSearch = () => { dispatch(setToggleSearch({ toggleSearch: true })); };
dispath를 통해 state에 값을 true로 변경 modal이 on이 되게끔 설정.
//searchModal에 관련한 코드 const [search, setSearch] = useState(""); const toggleSearch = useSelector( ({ product }: RootState) => product.toggleSearch ); const dispatch: AppDispatch = useDispatch(); const navigate = useNavigate(); const onChangeSearch = (e: React.ChangeEvent<HTMLInputElement>) => { const { value } = e.target; setSearch(value); }; const onClickClose = () => { dispatch(setToggleSearch({ toggleSearch: false })); }; const onSubmitSearch = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); await Promise.all([ await dispatch(setProductSearch({ productSearch: search })), await dispatch(setToggleSearch({ toggleSearch: false })), ]); navigate("searchProdcut에 맞는 URL"); setSearch(""); };
useSelector로 toggleSearch인 전역 변수를 가져와 핸들링 해준다. 그리고 dispatch로 다시 productSearch에 관련한 state를 관리 productList에서 useSelector로 가져온다.
modal animaition
첫 생각
- toggleSearch 불리언 state에 따른 조건부 랜더링으로 모달이 생겼다 안생겼다로 구현하자.
- keyframes를 이용해 animation 구현.
그, 결과 false일때는 animation 무효화 이유는 애초에 조건부 랜더링으로 false가 되어버렸으니 아예 keyframes는 실행이 안된다.다시 생각
- 불리언 스테이트로 조건부 랜더링을 하면 안된다.
- 굳이 구간을 나누어야 할만한 animation이 아닌데 keyframes를 써야할까?
import styled, { css } from "styled-components"; return( <form onSubmit={onSubmitSearch}> <SearchComponent visible={toggleSearch}> <SearchTitle>SEARCH</SearchTitle> <SearchFieldSet> <SearchInput value={search} onChange={onChangeSearch} type="text" /> <SerchIcon /> </SearchFieldSet> <CloseIcon onClick={onClickClose} /> </SearchComponent> </form> <BackGround visible={toggleSearch} /> ) const SearchComponent = styled.div<{ visible: boolean }>` z-index: 1001; ${({ theme }) => theme.positionMixIn("fixed", 0, 0)}; left: 0; height: 389px; opacity: 0; visibility: hidden; transform: translate3d(0, -100%, 0); transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s linear 0.5s; background: #fff; ${(props) => props.visible && css` opacity: 1; visibility: visible; transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s linear 0s; transform: translateZ(0); `} `;
조건부 랜더링을 스타일드 컴포넌트에서 사용한다. 그리고 visible 속성을 이용해 나오게끔 안나오게끔을 구현 조건부 랜더링에 따른 다른 속성으로 위에서 아래로 아래에서 위로에 animation 구현.
BE SEARCH
첫 생각
1. 공부해보니 index를 이용해 db의 읽기 성능을 올리고 $text를 이용해 내가 원하는 값을 search 하게끔 가능하겠다.
2. 지금 새상품 추천상품 이렇게 나누어져있는데 새상품 추천상품 할 것 없이 공통된 검색어가 나오면 상품이 나와야 하는데 이걸 어떻게 해결하지
// model const { Schema, model } = require("mongoose"); const RecommendProductSchema = new Schema( { src: { type: String, required: true }, name: { type: String, index: true, required: true }, price: { type: Number, index: true, required: true }, }, { timestamps: true } ); RecommendProductSchema.index({ name: "text" }); const RecommendProduct = model("recommendproduct", RecommendProductSchema); module.exports = { RecommendProduct };
$text에 관련한 index를 생성한다. RecommendProductSchema.index({ name: "text" }) 라는 코드를 통해 $text는 name이 된다. 즉 , name에 관련으로 search를 할 수 있다.
productRouter.get("/search", async (req, res) => { try { const { search, page = 1, limit = 4 } = req.query; const [ searchProudctFirst, searchProductSecond, totalProductOne, totalProductTwo, ] = await Promise.all([ await RecommendProduct.find({ $text: { $search: search } }) .sort({ _id: 1, }) .limit(limit) .skip((page - 1) * limit), await NewProduct.find({ $text: { $search: search } }) .sort({ _id: 1, }) .limit(limit) .skip((page - 1) * limit), await RecommendProduct.count({ $text: { $search: search } }), await NewProduct.count({ $text: { $search: search } }), ]); const searchProduct = [...searchProudctFirst, ...searchProductSecond]; const totalProduct = totalProductOne + totalProductTwo; return res.json({ searchProduct, totalProduct, }); } catch (e) { res.status(500).json({ message: e.message }); } });
위의 코드는 인피니트 스크롤도 구현해야 하기 때문에 query에 page와 limit이 있다 search에 기능에는 관련이 없다. find({ $text: { $search: search } ) find로 $text에 걸려있는 index에서 search를 할 것이다. 어떤 것을 search 하냐 query에 들어온 search 값에 따른 값을 서치할 것이고 나는 한번에 모든 상품들을 나열해야 했기때문에 배열에 이터럴을 통해 새 배열을 만들어 client에 보내줬다.
정말 좋은 글 감사합니다!