본문 바로가기

react-shop-web/front & back

체크 박스 필터 만들기(2)

일단 중간에 강사님이 실수하신 부분이 있다.

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