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