일단 중간에 강사님이 실수하신 부분이 있다.
Product Model을 만들 때 continents도 넣어줬어야 됐는데 이걸 깜박하고 안넣으셨다고 한다.
그래서 필터링 된 continents들이 랜딩 페이지에 뜨질 않는다.
Product Model 코드를 수정하고 파일들을 다시 업로드 해주자.
server/models/Product.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const productSchema = mongoose.Schema({
writer: {
type: Schema.Types.ObjectId,
ref: 'User'
},
name: {
type: String,
maxlength: 50
},
description: {
type:String
},
price: {
type: Number,
default: 0
},
images: {
type: Array,
default:[]
},
sold: {
type: Number,
maxlength: 100,
default: 0
},
continents: {
type: Number,
default: 1
},
views: {
type: Number,
default: 0
}
}, {timestamps: true})
const Product = mongoose.model('Product', productSchema);
module.exports = { Product }
LandingPage.js
import axios from 'axios';
import React, { useEffect, useState } from 'react'
import {Icon, Col, Row, Card, Carousel} from 'antd'
import Meta from 'antd/lib/card/Meta';
import ImageSlider from '../../utils/ImageSlider'
import CheckBox from '../LandingPage/Sections/CheckBox';
import { continents } from '../LandingPage/Sections/Datas';
function LandingPage() {
const [Products, setProducts] = useState([])
const [Skip, setSkip] = useState(0)
const [Limit, SetLimit] = useState(8)
const [PostSize, setPostSize] = useState(0)
const [Filters, setFilters] = useState({
continents:[],
price:[]
})
useEffect(() => {
let body = {
skip:Skip,
limit:Limit
}
getProducts(body)
},[])
const loadMoreHandler = () => {
let new_skip = Skip + Limit
let body = {
skip:new_skip,
limit: Limit,
// 더보기 버튼을 눌렀을 때 가는 request라는 정보를 넣어줌
loadMore:true
}
getProducts(body)
setSkip(new_skip)
}
const getProducts = (body) => {
axios.post('/api/product/products', body)
.then(response => {
if(response.data.success){
if (body.loadMore) {
setProducts([...Products, ...response.data.productInfo])
} else {
setProducts(response.data.productInfo)
}
setPostSize(response.data.postSize)
}else {
alert('상품들을 갖고오는데 실패')
}
})
}
const renderCards = Products.map((product, index) => {
return (
<Col lg={6} md={8} xs={24} key={index}>
<Card
cover={<ImageSlider images={product.images} />}>
<Meta
title={product.title}
description={`${product.price}`}
/>
</Card>
</Col>
)
})
// 체크박스에서 체크를 눌렀을 때 필터링 된 아이템들을 보여줘야 하므로
// 누를때 마다 getProducts 함수를 호출한다.
const showFilteredResults = (filters) => {
let body = {
// 누를때 마다 새로 필터링을 하여 DB에서 갖고오기 때문에 skip 또한 0으로 해줘야한다.
skip: 0,
limit: Limit,
filters: filters
}
getProducts(body)
setSkip(0)
}
const handleFilters = (filters, category) => {
const newFilters = { ...Filters }
newFilters[category] = filters
showFilteredResults(newFilters)
}
return (
<div style={{ width: '75%', margin: '3rem auto' }}>
<div style={{textAlign: 'center'}}>
<h2>Let's Travel Anywhere <Icon type="rocket"/></h2>
</div>
{/* Filter */}
{/* CheckBox */}
{/* 밑에 매개변수로 filters와 continents를 받았는데 이때 continents는 카테고리이다.
필터를 구현할 때 continent 뿐만 아니라 가격 필터도 구현해야 되기 때문에 구분해주기 위해 카테고리를 매개변수로 넣어준다.
*/}
<CheckBox list={continents} handleFilters={filters => handleFilters(filters, 'continents')} />
{/* RadioBox */}
{/* Search */}
<Row gutter={[16, 16]}>
{renderCards}
</Row>
{PostSize >= Limit &&
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button onClick={loadMoreHandler}>더보기</button>
</div>
}
</div>
)
}
export default LandingPage
server/routes/product.js
const express = require('express');
const router = express.Router();
const multer = require('multer')
const {Product} = require('../models/Product')
//=================================
// Product
//=================================
const storage = multer.diskStorage({
destination: function (req, file, cb) {
// 밑에처럼 설정해주면 모든 파일이 uploads 폴더에 저장된다.
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, `${Date.now()}_${file.originalname}`)
}
})
const upload = multer({ storage: storage }).single("file")
router.post('/image', (req, res) => {
// 가져온 이미지를 저장 해주는 부분
upload(req, res, err => {
if(err){
return res.json({success: false, err})
}
// 백엔드에서 파일 저장과 파일 저장 정보를 전달해주는데
// 파일을 어디다가 저장했는지, 파일 이름은 무엇으로 정했는지 이 두가지를 client에 전달한다.
return res.json({success:true, filePath:res.req.file.path, fileName:res.req.filename})
})
})
// UploadProductPage.js에서 api endPoint를 /api/product 로 요청을 보냈었다.
// index.js에서 product라는 라우트를 따로 만들어줬기 때문에 여기에서 end point는 '/'로 주면 된다.
router.post('/', (req, res) => {
// 받아온 정보들을 DB에 넣어준다.
// 넣어주기 전에 Product 모델을 갖고와야 한다.
const product = new Product(req.body)
product.save((err) => {
if (err) return res.status(400).json({ success: false })
return res.status(200).json({success:true})
})
})
router.post('/products', (req, res) => {
// product collection에 들어있는 모든 상품 정보를 갖고오기
let limit = req.body.limit ? parseInt(req.body.limit) : 20;
let skip = req.body.skip ? parseInt(req.body.skip) : 0;
let findArgs = {};
// 여기서 key는 continent나 price가 됨
for (let key in req.body.filters) {
if (req.body.filters[key].length > 0) {
findArgs[key] = req.body.filters[key];
}
}
// console.log('findArgs', findArgs);
// find 안에 findArgs를 넣어줬을 때
// console에 찍어본 것 처럼 continents : [1,2,3] 이런식으로 되어있을 때
// continents가 1, 2, 3인 것들만 찾으라는 의미임
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})
})
})
module.exports = router;
이 글에선 getProducts 함수를 호출 했을 때 요청하게 되는 /products 라우트 부분 위주로 설명하면 될 것 같다.
filter의 category는 현재 continents, price 두 가지이다.
LandingPage.js에서 자식 컴포넌트인 CheckBox에서 필터링된 continents들을 filters 변수로 받아와서 이것을 매개변수로handleFilter 함수를 호출한다.
handleFilters 함수에선 스프레드 오퍼레이터를 이용해서 Filters state를 모두 갖고 온 다음, 매개 변수로 받은 category 즉,
newFIlters['continents'] = filters로 바꾸고 이것을 매개변수로 showFilteredResults 함수를 호출한다.
새로 갱신된 filters를 getProducts 함수의 매개변수로 넣고 호출한다.
여기까지가 front에서 back으로 요청하는 과정이다.
body객체에 담긴 filters를 받은 /products 라우트에서 반복문을 이용하여 findArgs 객체를 정의한다.
let findArgs = {};
// 여기서 key는 continent나 price가 됨
for (let key in req.body.filters) {
if (req.body.filters[key].length > 0) {
findArgs[key] = req.body.filters[key];
}
}
현재 req.body.filters에는 continents와 price가 담겨있다. (아직 price 필터는 구현하지 않았으므로 이 필터 배열에는 아무것도 들어있지 않다.) 즉, 반복문을 통해 key에 담기는 변수는 continents와 price이다.
만약 CheckBox에서 1,2,3번 contients를 선택했다고 가정하자.
이렇게 되면 필터링된 filters['continents'] 배열에는 1 2 3이 들어있다.
이렇게 되면 req.body.filters['continents'] 배열의 크기는 3이다.
따라서 조건문에 걸리므로 findArgs['continents'] = {1,2,3}이 들어가게 된다.
이것을 Product.find(findArgs) 이렇게 넣어주고 실행을 하면
continents 카테고리에서 필터링된 continents는 1 2 3이므로 이것들만 찾으라는 의미이다.
'react-shop-web > front & back' 카테고리의 다른 글
검색 기능 만들기 (0) | 2023.02.04 |
---|---|
라디오 박스 필터 만들기 (0) | 2023.02.04 |
체크 박스 필터 만들기 (0) | 2023.01.28 |
더보기 버튼 만들기 (0) | 2023.01.28 |
이미지 슬라이더 만들기 (0) | 2023.01.28 |