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> <a href="/cart/update/<%=product.title %>?action=remove">-</a> <a href="/cart/update/<%=product.title %>?action=clear">Clear</a> </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커머스에서 사용되고 있다. 다음 포스팅에서는 기본이라 할 수 있는 페이팔을 정리해보자.
'웹개발 자료실 > node & Express 쇼핑몰제작Code' 카테고리의 다른 글
페이팔기반, e커머스 플랫폼 제작하기 [9편. 페이팔 모듈 추가] (2) | 2021.11.09 |
---|---|
페이팔기반, e커머스 플랫폼 제작하기 [7편. 메인 페이지 구현] (0) | 2021.11.08 |
페이팔기반, e커머스 플랫폼 제작하기 [6편. 전역객체 카테고리 구현] (0) | 2021.11.08 |
페이팔기반, e커머스 플랫폼 제작 [5편. 전역객체 page구현하기 ] (0) | 2021.11.08 |
페이팔기반, e커머스 플랫폼 제작하기 [4편. 제품페이지 구현하기] (0) | 2021.11.04 |
댓글