본문 바로가기

페이팔기반, e커머스 플랫폼 제작하기 [8편. 제품상세 & 장바구니 구현]

by Recstasy 2021. 11. 9.

 

 

1 제품 상세페이지

장바구니 구현에 앞서, 제품의 상세 페이지를 위한 라우터가 필요하다. 제품 상세페이지의 URL은 ('/:category/:product')이며, 카테고리와 프로덕트의 id는 URL에서 params.id값으로 추출할 수 있다.

 


  //Get product Detail

    router.get('/:category/:product', (req, res)=>{
        let galleryImages = null;

        Product.findOne({slug : req.params.product}, (err, product)=>{
            if(err){
                console.log(err);
            }else{
                let galleryDir = 'public/product_image/'+product._id + '/gallery';
                fs.readdir(galleryDir, (err, files)=>{
                    if(err){
                        console.log(err);
                    }else{
                        galleryImage = files;
                        res.render('product', {
                            title : product.title,
                            p : product,
                            galleryImages : galleryImages
                        })
                    }
                })
            }
        })
    })

[ products.js ]

 

 

제품 상세 페이지의 랜더링 뷰는 'product.ejs'파일이다.

 


  <%-
include('./layouts/header.ejs') %>


    <div class="row">

        <h1 class="page-header"><%= p.title %></h1>
        <div class="col-xs-12 col-md-5">
            <img class="spi" src="/product_images/<%= p.id %>/<%= p.image %>" alt="">
            <br>
        </div>
        <div class="col-xs-12 col-md-7">
            <p><%= p.desc %></p>
            <p>$<%= parseFloat(p.price).toFixed(2) %></p>
            <p><a href="/cart/add/<%= p.slug %>">Add to cart</a></p>
        </div>
        <div class="col-xs-12">
            <ul class="gallery">
            <% galleryImages.forEach(function(image){ %>
               <% if (image != "thumbs") { %>
                <li>
                    <a data-fancybox="gallery" href="/product_images/<%= p.id %>/gallery/<%= image %>">
                        <img src="/product_images/<%= p.id %>/gallery/thumbs/<%= image %>" alt="">
                    </a>
                </li>
               <% } %>
          <% }); %>
           </ul>
        </div>

    </div>

  <%- include('./layouts/footer.ejs') %>

[ product.ejs ]

 

css를 사용해서 이미지, ul리스트의 간격과 위치 그리고 라인을 정리한다.

 


  img.pimage
{

    width: 150px;
  }

  a.pa{
    display:inline-block;
    height:160px;
  }

  .row.products .p{
    margin-top:20px;
    margin-bottom:20px;
  }

  img.spi {
    width: 100%;
  }

  ul.gallery li {
    display: inline-block;
    margin: 20px;
    list-style: none;
  }

  img.cpi {
    width: 100px;
  }

  table.alignmiddle td {
    vertical-align: middle !important;
  }

  .pp {
    position: absolute;
    left: -3000px;
  }

[ style.css ]

 

 

 

 

 

 


2 카트 제작하기

카트에서는 제품 개수, 총합, 장바구니 담기, 장바구니 제거, 전체 제거와 같은 기능이 필요하다.

 

 

2-1 라우팅 

index.js에서 카트를 위한 라우팅('/cart')을 추가한다.

 


  //중략...


    app.get('*', (req, res, next)=>{
        res.locals.cart = req.session.cart;
        next();
    })

  //라우팅
    let pages = require('./routes/pages.js');
    let products = require('./routes/products.js');
    let cart = require('./routes/cart.js');
    let adminPages = require('./routes/admin_pages.js');
    let adminCategories = require('./routes/admin_categories.js');
    let adminProducts = require('./routes/admin_products.js');

    app.use('/admin/pages', adminPages);
    app.use('/admin/categories', adminCategories);
    app.use('/admin/products', adminProducts);
    app.use('/products', products);
    app.use('/cart', cart);
    app.use('/', pages);


  //https 서버연결
    app.listen(3005);

[index.js]

 

 

2-2 물품 추가 :: /add/:product 

카트에 물품 추가하는 코드는 크게 2개의 조건문으로 나눠진다. DB쿼리를 통해 세션유무를 파악하고, 세션의 카트값 유무를 통해 새롭게 추가하는 물품인지 기존의 것인지 분류한다.

 


    let express = require('express');
    let router = express.Router();

  //Get Product Model
    let Product = require('../models/productSchema');

  // Get add Product to cart
    router.get('/add/:product', (req, res)=>{

        let slug = req.params.product;

        Product.findOne({slug:slug}, (err, p)=>{
            if(err) console.log(err);
            if(typeof req.session.cart == 'undefined'){
                req.session.cart = [];
                req.session.cart.push({
                    title:slug,
                    qty : 1,
                    price : parseFloat(p.price).toFixed(2),
                    image : '/product_images/'+ p._id +'/' + p.image
                });
            }else{
                let cart = req.session.cart;
                let newItem = true;

                for(let i=0; i<cart.length; i++){
                    if(cart[i].title == slug){
                        cart[i].qty++;
                        newItem = false;
                        break;
                    }
                }

                if(newItem){
                    cart.push({
                        title:slug,
                        qty : 1,
                        price : parseFloat(p.price).toFixed(2),
                        image : '/product_images/'+ p._id +'/' + p.image
                    });
                }
            }

            console.log(req.session.cart);

            req.flash('success', 'Product added!');
            res.redirect('back');
            })
        });

  //Exports
    module.exports = router;

[cart.js]

 

 

물품이 추가되었을 때, 이를 알리는 장바구니 표시가 필요하다. header.ejs파일을 아래와 같이 수정하자.

 

       <!-- 중략... -->
                  <% }) %>
                  </ul>

                 
<ul class="nav navbar-nav navbar-right">
                       <li>
                           <a href="/cart/checkout"> My Cart
                                <% if(typeof cart !== "undefined"){ %>
                                    <%= cart.length %>
                                <% } else{ %>
                                    0
                                <% } %>
                            </a>
                        </li>
                    </ul>
                </div><!--/.nav-collapse -->
            </div>
        </nav>

<div class="container">
<%- messages('messages', locals) %>
<!-- 중략... -->

[ header.ejs ]

 

 

 

 

2-3 물품 업데이트 :: /update/:product 

'/update/:product'경로는 '?action=add', '?action=remove', '?action=clear'과 같은 방식으로 query값이 함께 들어온다. update라우터는 action의 값에 따라 switch문을 통해 세션의 cart값을 아래와 같이 관리한다.

 


  // 중략...

  //Get update product
    router.get('/update/:product', (req, res)=>{
        let slug = req.params.product;
        let cart = req.session.cart;
        let action = req.query.action;

        for(let i=0; i<cart.length; i++){
            if(cart[i].title == slug){
                switch(action){
                    case "add":
                        cart[i].qty++;
                        break;
                    case "remove":
                        cart[i].qty--;
                        if(cart[i].qty < 1) cart.splice(i, 1);
                        break;
                    case "clear":
                        cart.splice(i, 1);
                        if(cart.lenth == 0) delete req.session.cart
                        break;
                    default:
                        console.log('update problem');
                        break;
                }
                break;
            }
        }

        req.flash('success', 'Cart updated!!');
        res.redirect('/cart/checkout');
    })

  //Exports
    module.exports = router;

[ cart.js ]

 

카트의 랜더링 뷰 파일은 아래와 같다. 카트에서 제품을 추가, 수정, 제거할 수 있는 링크와 제품 전체값을(total) 계산하는 부분을 프론트에서 해결하는 부분이 핵심이다.

 


  <%-
include('./layouts/header.ejs') %>

  <% if(typeof cart !== "undefined"){ %>

        <h1 class="page-title">My Cart</h1>
        <br><br>
        <table class="table table-striped alignmiddle">
            <tr>
                <th>Image</th>
                <th>Title</th>
                <th>Price</th>
                <th>Quantity</th>
                <th></th>
                <th>Subtotal</th>
            </tr>

            <%
let total=0; %>
            <% cart.forEach((product)=>{ %>
                <% let sub = parseFloat(product.qty * product.price).toFixed(2) %>
                <% total += +sub %>
            <tr>
                <td><img src="<%=product.image%>" class="cpi" alt=""></td>
                <td><%=product.title%></td>
                <td>$<%=product.price%></td>
                <td><%=product.qty%></td>
                <td>
                    <a href="/cart/update/<%=product.title %>?action=add">+</a>&nbsp;
                    <a href="/cart/update/<%=product.title %>?action=remove">-</a>&nbsp;
                    <a href="/cart/update/<%=product.title %>?action=clear">Clear</a>&nbsp;
                </td>
                <td>$<%= sub %></td>
            </tr>
            <% }) %>
            <tr>
                <td colspan="6" align="right"> <b>Total: </b> $<%= parseFloat(total).toFixed(2) %></td>
            </tr>
            <tr>
                <td colspan="5" align="right">
                    <a class="clearcart btn btn-danger" href="/cart/clear">Clear cart</a>
                </td>
                <td colspan="5" align="right">
                    <a class="btn btn-primary buynow" href="#">Buy now</a>
                </td>
            </tr>
        </table>

        //페이팔 결제폼 부분

  <% } else{ %>
    <h3 class="text-center"> Your cart is empty. </h3>
  <% } %>

  <%- include('./layouts/footer.ejs') %>

[ checkout.ejs ]

 

 

 

 

 

2-4 카트 삭제 :: /delete/:product 

checkout.ejs에서 카트 전체를 삭제하는 코드를 아래와 같이 추가한다.

 


  <%-
include('./layouts/header.ejs') %>


    <!-- 중략... -->

    <script>
    $(()=>{
        $('a.clearcart').on('click', function () {
            if (!confirm('Confirm clear cart?'))
                return false;
            });
        })
    </script>

  <%- include('./layouts/footer.ejs') %>

[ checkout.ejs ]

 

카트를 삭제하는 라우팅도 추가한다.

 


  //Get clear cart

    router.get('/clear', (req, res)=>{
        delete req.session.cart;
        req.flash('success', 'Cart cleared!!');
        res.redirect('/cart/checkout');
    });

  //Exports
    module.exports = router;

[ cart.js ]

 

 

 

 

 

 

 


3 정리

카트 CRUD구현이 끝났다면, 이제 남은 부분은 페이팔 결제 모듈과 폼을 추가하는 일이다. 최근 페이팔보다 Stripe가 많이 사용되는 추세지만, 페이팔은 여전히 많은 E커머스에서 사용되고 있다. 다음 포스팅에서는 기본이라 할 수 있는 페이팔을 정리해보자. 

댓글

최신글 전체

이미지
제목
글쓴이
등록일