react-shop-web/front & back

카트에 담긴 상품 정보 가져오는 부분 코드 수정

tjdvyzl 2023. 2. 13. 14:35

강사님께서 product.js의 products_by_id 라우터 부분을 구현하실 때 클라이언트에다가 json 형식으로 {success: true, product} 이렇게 보내도록 구현하셨었는데 이렇게되면 redux-dev tools에서 볼 때 cartDetail > product > 이런식으로 한 단계 더 들어가야 product 모델 데이터에 접근할 수 있다. 후에 계속 이 부분을 활용할 텐데 이렇게 한 단계 더 깊이 들어가면 코드가 더러워 질 것 같다고 하셔서 코드를 수정하셨다.

 

json 부분을 send로 바꿔주고 product만 보내주자.

 

router.get('/products_by_id', (req, res) => {
    // /api/product/products_by_id?id=${productId}&type=single

    // 지금은 req.query.id가 여러 개일 수 있어서 type이 array로 올 수 있다. 
    let type = req.query.type
    let productIds = req.query.id


    // ex) req.query.id = '123,456,789' 이런식으로 올 수 있음 
    // 위의 형식으로 온 id들을 , 단위로 끊어서 productIds에 배열 형식으로 바꿔서 넣어주는 과정이 밑의 부분임 
    if(type === 'array'){
        let ids = req.query.id.split(',');
        productIds = ids.map(item => {
            return item
        })
    }
    
    // query로 받아온 id가 여러 개일 때는 productIds가 배열 형식이므로 $in 문법을 사용하자. 
    Product.find({ _id: {$in: productIds} })
        .populate('writer')
        .exec((err, product) => {
            if (err) return res.status(400).send(err)
            return res.status(200).send(product)
        })

})

server/routes/product.js

 

 

더보기
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import ProductImage from './Sections/ProductImage'
import ProductInfo from './Sections/ProductInfo'
import {Row, Col} from 'antd'

function DetailProductPage(props) {

    const productId = props.match.params.productId

  const [Product, setProduct] = useState({})

    useEffect(() => {
      
        axios.get(`/api/product/products_by_id?id=${productId}&type=single`)
            .then(response => {
                setProduct(response.data.product[0])
            })
        
    }, [])
    

  return (
    <div style={{ width: '100%', padding: '3rem 4rem' }}>
      <div style={{display: 'flex', justifyContent: 'center'}}>
        <h1>{Product.title}</h1>
      </div>

      <br />

      <Row gutter={[16,16]}>
        <Col lg={12} sm={24}><ProductImage detail={Product} /></Col>
        <Col lg={12} sm={24}><ProductInfo detail={Product}/></Col>
      </Row>

    </div>
  )
}

export default DetailProductPage

DetailProductPage.js

 

 

export function getCartItems(cartItems, userCart){

    // 상세보기 페이지 만들 때 상품을 products_by_id 엔드포인트에서 갖고왔었다. 
    // 이때는 상품을 하나만 갖고와서 single로 type을 줬었지만 
    // 매개변수로 받은 cartItems에 여러 개의 cart id가 존재할 수 있기 때문에
    // 이번엔 type을 array로 줘야한다. 

    const request = axios.get(`/api/product/products_by_id?id=${cartItems}&type=array`)
        .then(response => {
        
            // CartItem들에 해당하는 정보들을 Product Collection에서 가져온 후에 
            // Quantity 정보를 넣어준다. 

            userCart.forEach(cartItem => {
                response.data.forEach((productDetail, index) => {
                    if(cartItem.id === productDetail._id){
                        response.data[index].quantity = cartItem.quantity 
                    }
                })
            })
        
        return response.data;
        
    });

    return {
        type: GET_CART_ITEMS,
        payload: request
    }
}

user_actions.js

 

 

더보기
import React, {useEffect} from 'react'
import {useDispatch} from 'react-redux';
import {getCartItems} from '../../../_actions/user_actions';
import UserCardBlock from './Sections/UserCardBlock';

function CartPage(props) {
  const dispatch = useDispatch();

  useEffect(() => {

    let cartItems = []
    
    // 리덕스 user state에서 cart안에 상품이 들어있는지 확인 
    if(props.user.userData && props.user.userData.cart) {
      // 카트 안에 상품이 하나 이상 들어있다면 
      if(props.user.userData.cart.length > 0){
        props.user.userData.cart.forEach(item => {
          cartItems.push(item.id)
        })

        // 밑에 액션에 매개변수로 방금 props로부터 받은 user의 userData에서 cart들을 담은 변수와,
        // userData의 cart 정보도 주자. (이때 두번째 매개변수를 넣어주는 이유는 quantity는 user의 cart 부분에만 존재하기 때문)
        dispatch(getCartItems(cartItems, props.user.userData.cart))
      }
    }
  
    return () => {
      
    }
  }, [props.user.userData])
  
  return (
    <div style={{ width: '85%', margin: '3rem auto'}}>
      <h1>My Cart</h1>
      {/* 밑에 props 주는 과정에서도 
      페이지가 렌더링 될 때는 return값을 먼저 렌더링되고 그 후에 props에 값이 들어올 때도 있다. 이때도 useEffect의
      빈칸에 props.user.userData를 넣어주지 않으면 useEffect 안에 있는 조건문이 통과되지 않기 때문에 cartItems에는 아무것도 담기지 않는다.
      UserCardBlock 컴포넌트에 props를 줄 때도 현재 컴포넌트 props에 값이 담겨있을 때를 조건으로 줘야 오류가 나지 않는다. */}
      <UserCardBlock products={props.user.cartDetail && props.user.cartDetail}/>  
    </div>
  )
}

export default CartPage

CartPage.js