카트에 담긴 상품 정보들을 데이터베이스에서 가져오기
위 이미지는 현재 mongoDB에 있는 products 모델과 users 모델의 내용이다.
현재 상품 상세페이지를 들어가보면 나오는 내용은 위 이미지에서 오른쪽 내용이다.
보면 상품의 이미지나 설명 등등은 있지만 Cart 내용이 존재하지 않는다. 이렇게 되면 products 모델만으로는 cart 페이지를 만들 수 없다. 그러므로 users 모델과 products 모델을 합쳐줘야한다.
import React, {useEffect} from 'react'
import {useDispatch} from 'react-redux';
import {getCartItems} from '../../../_actions/user_actions';
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>CartPage</div>
)
}
export default CartPage
CartPage.js
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).json({success:true, product})
})
})
server/routes/product.js
export const LOGIN_USER = 'login_user';
export const REGISTER_USER = 'register_user';
export const AUTH_USER = 'auth_user';
export const LOGOUT_USER = 'logout_user';
export const ADD_TO_CART = 'add_to_cart';
export const GET_CART_ITEMS = 'get_cart_items';
types.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.product.forEach((productDetail, index) => {
if(cartItem.id === productDetail._id){
response.data.product[index].quantity = cartItem.quantity
}
})
})
return response.data;
});
return {
type: GET_CART_ITEMS,
payload: request
}
}
user_actions.js에서 getCartItems 함수 구현부분. import로 GET_CART_ITEMS 타입을 type.js에서 갖고오자.
위 코드를 간략하게 설명해놔야 복습할 때 편할 것 같다.
cartItems : user.userData.cart의 id값들
cart.id : 상세 페이지에서 Add To Cart 버튼을 눌렀을 때 해당하는 상품의 id값
products_by_id 라우터로부터 받은 response.data : cartItems에 들어있는 productId와 일치하는 product들
userCart : 유저의 모든 Cart 값들
지금 하고 있는 과정이 뭐냐면
product를 업로드 할 때 writer, title, description 등의 필드들이 있었지만 quantity 필드는 없다.
카트 페이지를 만들면 product들을 갖고올텐데 이때 quantity 필드가 없기 때문에 개수 정보를 화면에 보여줄 수 없다.
그래서 userCart를 모두 탐색하고, 탐색하는 과정에서 response.data를 탐색해준다.
이렇게 되면 userCart의 forEach 원소인 cartItem의 id값이랑 같은 id가 response.data안에 있을 것이다.
ㄴresponse.data 안에는 product 모델들이 들어있고, cart를 만들 땐 이 product 모델들의 id값을 기반으로 만들어줬기 때문에 cart에는 product 모델의 id값들이 들어있다.
같은 값을 찾았다면 product 모델에 새로운 quantity 필드를 추가하여 해당 cart의 quantity 값을 넣어준다.
결과로 product 모델에는 quantity 필드가 없었었지만 지금은 quantity 필드가 생기고 동시에 값까지 들어가는 과정이다.
import {
LOGIN_USER,
REGISTER_USER,
AUTH_USER,
LOGOUT_USER,
ADD_TO_CART,
GET_CART_ITEMS,
} from '../_actions/types';
export default function(state={},action){
switch(action.type){
case REGISTER_USER:
return {...state, register: action.payload }
case LOGIN_USER:
return { ...state, loginSucces: action.payload }
case AUTH_USER:
return {...state, userData: action.payload }
case LOGOUT_USER:
return {...state }
case ADD_TO_CART:
return {...state,
userData: {
...state.userData,
cart: action.payload
}
}
case GET_CART_ITEMS:
return {...state, cartDetail: action.payload }
default:
return state;
}
}
user_reducer.js