ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -16) placeorder Screen 만들기
    NODE.JS 2021. 5. 28. 03:36

     

    우선 screen안에 PlaceOrderScreen.js를 만든다. 

    import React from 'react'
    import { useSelector } from 'react-redux'
    import CheckoutSteps from '../components/CheckoutSteps'
    
    function PlaceOrderScreen(props) {
        const cart = useSelector(state=>state.cart);
        if(!cart.paymentMethod){
            props.history.push('/payment');
        }
        return (
            <div>
                <CheckoutSteps step1 step2 step3 step4></CheckoutSteps>
                <div className="row top">
                    <div className="col-2">
                        <ul>
                            <li>
                                <div className="card card-body">
                                    <h2>Shipping</h2>
                                    <p>
                                        <strong>Name:</strong>{cart.shippingAddress.fullName}<br/>
                                        <strong>Address:</strong>{cart.shippingAddress.address},{cart.shippingAddress.city},{cart.shippingAddress.postalCode},{cart.shippingAddress.country}
                                    </p>
                                </div>
                            </li>
                            <li>
                                <div className="card card-body">
                                    <h2>Payment</h2>
                                    <p>
                                        <strong>Method:</strong>{cart.paymentMethod}<br/>
                                    </p>
                                </div>
                            </li>
                            <li>
                                <div className="card card-body">
                                    <h2>Order Items</h2>
                                    
                                </div>
                            </li>
                        </ul>
                    </div>
                    <div className="col-1">
    
                    </div>
                </div>
            </div>
        )
    }
    
    export default PlaceOrderScreen
    

     

     

    여기서 안에있는 주문된 상품을 보여주는 것은 카트에서 카트내의 아이템을 보여주는 것과 비슷하다. 

    가져와서 필요없는 부분은 삭제하고 아이템양과 총가격을 보여주는 부분만 추가하자. 

    import React from 'react'
    import { useSelector } from 'react-redux'
    import CheckoutSteps from '../components/CheckoutSteps'
    import {Link} from 'react-router-dom';
    
    function PlaceOrderScreen(props) {
        const cart = useSelector(state=>state.cart);
        if(!cart.paymentMethod){
            props.history.push('/payment');
        }
        return (
            <div>
                <CheckoutSteps step1 step2 step3 step4></CheckoutSteps>
                <div className="row top">
                    <div className="col-2">
                        <ul>
                            <li>
                                <div className="card card-body">
                                    <h2>Shipping</h2>
                                    <p>
                                        <strong>Name:</strong>{cart.shippingAddress.fullName}<br/>
                                        <strong>Address:</strong>{cart.shippingAddress.address},{cart.shippingAddress.city},{cart.shippingAddress.postalCode},{cart.shippingAddress.country}
                                    </p>
                                </div>
                            </li>
                            <li>
                                <div className="card card-body">
                                    <h2>Payment</h2>
                                    <p>
                                        <strong>Method:</strong>{cart.paymentMethod}<br/>
                                    </p>
                                </div>
                            </li>
                            <li>
                                <div className="card card-body">
                                    <h2>Order Items</h2>
                                    <ul>
                                        {cart.cartItems.map(item=>(
                                                <li key = {item.product}>
                                                    <div className="row">
                                                        <div>
                                                            <img src={item.image} alt={item.name} className="small"/>
                                                        </div>
                                                        <div className="min-30">
                                                            <Link to={`/product/${item.product}`}>{item.name}</Link>
                                                        </div>
                                                        <div>
                                                             {item.qty} x ${item.price} = ${item.qty * item.price}
                                                        </div>
                                                    </div>
                                                </li>
                                            ))
                                        }
                                    </ul>
                                </div>
                            </li>
                        </ul>
                    </div>
                    <div className="col-1">
    
                    </div>
                </div>
            </div>
        )
    }
    
    export default PlaceOrderScreen
    

     

    app.js에 추가하자. 

    import PlaceOrderScreen from './screens/PlaceOrderScreen';
                  <Route path="/placeorder" component={PlaceOrderScreen} exact></Route>
    

     

     

     

    이제 스크린에 총량과 계산 버튼을 선택하는 것을 만들자. 

    여기서 toPrice 함수는 5.123->"5.12"->5.12의 과정으로 만드는 것이다. 

        const toPrice = (num)=>Number(num.toFixed(2));
    
        cart.itemsPrice = toPrice(cart.cartItems.reduce((a,c)=> a+c.qty*c.price,0))    
    
        cart.shippingPrice = cart.itemsPrice >100 ? toPrice(0):toPrice(10);
        cart.taxPrice = toPrice(0.15*cart.itemsPrice);
        cart.totalPrice = cart.itemsPrice + cart.shippingPrice+cart.taxPrice;
    
        const placeOrderHandler= () =>{
            
        }
        
        
         <div className="col-1">
                        <div className="card card-body">
                            <ul>
                                <li>
                                    <h2>Order Summary</h2>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>Items</div>
                                        <div>${cart.itemsPrice}</div>
                                    </div>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>Shipping</div>
                                        <div>${cart.shippingPrice}</div>
                                    </div>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>Tax</div>
                                        <div>${cart.taxPrice}</div>
                                    </div>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>
                                            <strong>Order Total</strong>
                                        </div>
                                        <div>
                                            <strong>${cart.totalPrice}</strong>
                                        </div>
                                    </div>
                                </li>
                                <li>
                                    <button className="primary block" type="button" onClick={placeOrderHandler} disabled={cart.cartItems.length===0}>Place Order</button>
                                </li>
                            </ul>
                        </div>
                    </div>

     

    모든 가격을 소숫점 2자리 까지 나오도록 설정해보자. 

                            <ul>
                                <li>
                                    <h2>Order Summary</h2>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>Items</div>
                                        <div>${cart.itemsPrice.toFixed(2)}</div>
                                    </div>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>Shipping</div>
                                        <div>${cart.shippingPrice.toFixed(2)}</div>
                                    </div>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>Tax</div>
                                        <div>${cart.taxPrice.toFixed(2)}</div>
                                    </div>
                                </li>
                                <li>
                                    <div className="row">
                                        <div>
                                            <strong>Order Total</strong>
                                        </div>
                                        <div>
                                            <strong>${cart.totalPrice.toFixed(2)}</strong>
                                        </div>
                                    </div>
                                </li>

     

     

    이제 placeorder의  백엔드 부분을 작성하자. 

    db에 주문을 올릴 것이다. 

    backend의 order모델을 만들자. 

    import mongoose from 'mongoose';
    
    const orderSchema = new mongoose.Schema({
        orderItems:[{
            name:{
                type:String,
                required:true
            },
            qty:{
                type:Number,
                required:true
            },
            image:{
                type:String,
                required:true
            },
            price:{
                type:Number,
                required:true
            },
            product:{
                type:mongoose.Schema.Types.ObjectId,
                ref:'Product',
                required:true
            }, 
        }],
        shippingAddress:{
            fullName:{
                type:String,
                required:true
            },
            address:{
                type:String,
                required:true
            },
            city:{
                type:String,
                required:true
            },
            postalCode:{
                type:String,
                required:true
            },
            country:{
                type:String,
                required:true
            }
        },
        paymentMethod:{
            type:String,
            required:true
        },
        itemsPrice:{
            type:Number,
            required:true
        },
        shippingPrice:{
            type:Number,
            required:true
        },
        taxPrice:{
            type:Number,
            required:true
        },
        totalPrice:{
            type:Number,
            required:true
        },
        user:{
            type:mongoose.Schema.Types.ObjectId,
            ref:'User',
            required:true
        },
        isPaid:{
            type:Boolean,
            default:false
        },
        paidAt:{
            type:Date
        },
        isDelivered:{
            type:Boolean,
            default:false
        },
        deliveredAt:{
            type:Date
        }
    }, {timestamps:true})
    
    const Order = mongoose.model('Order', orderSchema);
    
    export default Order;

     

     

    이제 라우터를 만들자. 

    import express from 'express';
    import expressAsyncHandler from 'express-async-handler';
    import Order from '../models/orderModel';
    
    const orderRouter = express.Router();
    
    
    orderRouter.post('/', expressAsyncHandler(async(req, res) => {
        if(req.body.orderItems.length===0){
            res.status(400).send({message:'Cart is empty'})
        }else{
            const order = new Order({
                orderItems: req.body.orderItems,
                shippingAddress: req.body.shippingAddress,
                paymentMethod: req.body.paymentMethod,
                itemsPrice: req.body.itemsPrice,
                shippingPrice: req.body.shippingPrice,
                taxPrice: req.body.taxPrice,
                totalPrice: req.body.totalPrice,
                
            })
        }
    }))
    
    export default orderRouter;

     

    라우터에서 유저의 정보를 가져와야 하는데, 이는 미들웨어를 이용해서 isAuth정보를 가져오는 것으로 할 것이다. 

    utils.js로 간다. 

    여기서 slice(7, authorization.length)의 의미는 {bearer xxx}에서 'bearer '를 제외한 다음의 토큰만 가져와서 저장하겠다는 것이다. 

    export const isAuth = (req, res, next)=>{
        const authorization = req.headers.authorization;
        if(authorization){
            const token = authorization.slice(7, authorization.length);
            jwt.verify(token, process.env.JWT_SECRET || 'somethingsecret', (err, decode)=>{
                if(err){
                    req.status(401).send({message:'Invalid Token'})
                }else{
                    req.user = decode;
                    next();
                }
            })
        }else{
            req.status(401).send({message:'No Token'})
        }
    } 

     

     

    다음과 같이 라우터에 미들웨어를 추가해서 작성해준다. 

    import express from 'express';
    import expressAsyncHandler from 'express-async-handler';
    import Order from '../models/orderModel.js';
    import { isAuth } from '../utils.js';
    
    const orderRouter = express.Router();
    
    
    orderRouter.post('/', isAuth, expressAsyncHandler(async(req, res) => {
        if(req.body.orderItems.length===0){
            res.status(400).send({message:'Cart is empty'})
        }else{
            const order = new Order({
                orderItems: req.body.orderItems,
                shippingaddress: req.body.shippingaddress,
                paymentMethod: req.body.paymentMethod,
                itemPrice: req.body.itemPrice,
                shippingPrice: req.body.shippingPrice,
                totalPrice: req.body.totalPrice,
                user:req.user._id
            })
            const createdOrder = await order.save();
            res.status(201).send({message:'New Order reated', order:createdOrder})
        }
    }))
    
    export default orderRouter;

     

    server.js에 order 주소도 추가해준다. 

    import orderRouter from './routes/orderRouter.js';
    
    app.use('/api/orders', orderRouter);
    

     

    이제 placeorder버튼을 활성화 시키자. 

     

     

    placeOrderScreen.js로 가자. 

        import { useDispatch, useSelector } from 'react-redux'
    
    	const dispatch = useDispatch()
        const placeOrderHandler= () =>{
            dispatch(createOrder({...cart, orderItems:cart.cartItems}))
        }

     

    constants를 만들자 

    orderConstants.js

    export const ORDER_CREATE_REQUEST= 'ORDER_CREATE_REQUEST'
    export const ORDER_CREATE_SUCCESS= 'ORDER_CREATE_SUCCESS'
    export const ORDER_CREATE_FAIL= 'ORDER_CREATE_FAIL'
    export const ORDER_CREATE_RESET= 'ORDER_CREATE_RESET'
    
    

     

     

    actions을 만들자. 

    orderActions.js

    여기서 우리는 리덕스의 userSignin안의 userInfo를 가지고 올것이다 (getState())

    그리고 헤더안에서 Authorization 을 'Bearer '+ token으로 설정한다. 

    import axios from "axios"
    import {  ORDER_CREATE_FAIL, ORDER_CREATE_REQUEST, ORDER_CREATE_SUCCESS } from "../constants/orderConstants"
    import { CART_EMPTY } from "../constants/cartConstants";
    
    export const createOrder = (order)=> async (dispatch, getState)=>{
        dispatch({
            type:ORDER_CREATE_REQUEST,
            payload: order
        })
        try{
            const {userSignin : {userInfo}} = getState();
            const {data} = await axios.post('/api/orders', order, {
                headers:{
                    Authorization:`Bearer ${userInfo.token}`
                }
            })
            dispatch({
                type:ORDER_CREATE_SUCCESS,
                payload:data.order
            })
            dispatch({
                type:CART_EMPTY
            })
            localStorage.removeItem("cartItems");
    
        }catch(error){
            dispatch({
                type:ORDER_CREATE_FAIL,
                payload:error.response && error.response.data.message
                    ? error.response.data.message
                    : error.message
            })
        }
    }

     

    여기서 order를 생성하고 나면 카트를 비워줄것이기 때문에 cart_empty상수를 cartConstants안에 생성하고 리듀서액션을 만든다. 

    export const CART_EMPTY = 'CART_EMPTY'
            case CART_EMPTY:
                return {...state, cartItems:[]}
            default: 
                return state;
        }

     

     

     

     

    OrderReducers.js를 만들자. 

    import { ORDER_CREATE_FAIL, ORDER_CREATE_REQUEST, ORDER_CREATE_RESET, ORDER_CREATE_SUCCESS } from "../constants/orderConstants";
    
    export const orderCreateReducer = (state = {}, action) =>{
        switch(action.type){
            case ORDER_CREATE_REQUEST:
                return {loading:true}
            case ORDER_CREATE_SUCCESS:
                return {loading:false, success:true, order:action.payload}
            case ORDER_CREATE_FAIL:
                return {loading:false, error:action.payload}
            case ORDER_CREATE_RESET:
                return {};
            default:
                return state 
        }
    }

    store.js에 추가해준다. 

    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        userSignin: userSigninReducer,
        userRegister: userRegisterReducer,
        orderCreate:orderCreateReducer
        
    
    })

     

     

    placeOrder스크린에 임포트도 해준다. 

    import { createOrder } from '../actions/orderActions';
    import { ORDER_CREATE_RESET } from '../constants/orderConstants';
    import LoadingBox from '../components/LoadingBox'
    import MessageBox from '../components/MessageBox'
    
        const orderCreate  = useSelector(state => state.orderCreate);
        const {loading, success, error, order} = orderCreate;
    
        const dispatch = useDispatch()
        const placeOrderHandler= () =>{
            dispatch(createOrder({...cart, orderItems:cart.cartItems}))
        }
    
        useEffect(() => {
            if(success){
                props.history.push(`/order/${order._id}`)
                dispatch({
                    type:ORDER_CREATE_RESET
                })
            }
        }, [success, dispatch, order, props.history])
        
        
        
        
        
        
                                        <div className="row">
                                        <div>
                                            <strong>Order Total</strong>
                                        </div>
                                        <div>
                                            <strong>${cart.totalPrice.toFixed(2)}</strong>
                                        </div>
                                    </div>
                                </li>
                                <li>
                                    <button className="primary block" type="button" onClick={placeOrderHandler} disabled={cart.cartItems.length===0}>Place Order</button>
                                </li>
                                {
                                    loading && <LoadingBox ></LoadingBox>
                                }
                                {
                                    error && <MessageBox variant = 'danger'>{error}</MessageBox>
                                }
                            </ul>
                        </div>
        
        
        
    

    placeorder 버튼을 누르면 다음과 같이 주문 아이디로 넘어간다. 

    주문하고나서 카트를 확인하면 빈칸임을 확인할 수 있다. 

    db에 주문모델도 잘 들어왔음을 확인할 수 있다. 

     

    리덕스에서도 주문이 성공하면

    카트가 비워지고 

    orderCreate도 리셋된다. 

     

    payload도 확인 가능하다. 

     

     

     

     

     

     

Designed by Tistory.