-
[항해4기] 스파르타코딩클럽 Node.js 기초반 4주차 회고록스파르타코딩클럽 웹개발 종합반 2021. 11. 16. 22:14
스파르타코딩클럽 Node.js 기초반
4주차 개발일지
목차
- 쇼핑몰 페이지 구현
- 4주차 소감
1. 쇼핑몰 페이지 구현
- 상품 필터링하기
- 카테고리의 음료면 음료 음식이면 음식으로 나게 구현해줬다.
- HTML을 .ejs로 변환해서 만들어줬고, 라우팅을 해줬다.
- 라우팅 후 DB를 활용하기 위해 API 작업도 해줬다.
카테고리 '음료'
카테고리 '음식'
- 상품 장바구니에 담기
- 상품을 장바구니에 담기 위해서 데이터 정보를 담아서 정보를 넘겨주는 작업을 했다.
- Schema작업도 해주고 API 작업을 해줬다.
- 장바구니에 DB만 있으면 안되니까 .ejs를 이용해서 페이지 구현을 하면서 라우팅도 해줬다.
장바구니 Schema
const mongoose = require("mongoose"); const { Schema } = mongoose; const cartSchema = new Schema({ goodsId: { type: Number, required: true, unique: true }, quantity: { type: Number, required: true } }); module.exports = mongoose.model("Cart", cartSchema);
장바구니 Schema에 따른 API
const Cart = require("../schemas/cart"); router.post("/goods/:goodsId/cart", async (req, res) => { const { goodsId } = req.params; const { quantity } = req.body; isCart = await Cart.find({ goodsId }); console.log(isCart, quantity); if (isCart.length) { await Cart.updateOne({ goodsId }, { $set: { quantity } }); } else { await Cart.create({ goodsId: goodsId, quantity: quantity }); } res.send({ result: "success" }); });
장바구니 라우팅
app.get("/cart", (req, res) => { res.render("cart"); });
장바구니 페이지
```jsx <!DOCTYPE html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <link rel="icon" href="/static/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon" /> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous" /> <!-- Font Awesome CSS --> <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous" ></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script> <link href="/static/mystyle.css" rel="stylesheet" /> <title>스파르타 쇼핑몰 | 장바구니</title> <script> $(document).ready(function() { get_goods(); $("#categorySelect").on("change", function() { get_goods($(this).val()); }); }); function sign_out() { $.removeCookie("mytoken", { path: "/" }); $.removeCookie("userName", { path: "/" }); window.location.href = "/"; } function get_goods() { $("#goodsList").empty(); $.ajax({ type: "GET", url: `/api/cart`, data: {}, success: function(response) { let goods = response["cart"]; for (let i = 0; i < goods.length; i++) { make_card(goods[i]["goods"]); } getSum(); } }); } function order() { $.ajax({ type: "GET", url: `/api/cart`, data: {}, success: function(response) { let goods = response["cart"]; let cart = []; for (let i = 0; i < goods.length; i++) { cart.push({ goodsName: goods[i]["name"], quantity: goods[i]["goodsId"] }); } sessionStorage.setItem("cart", JSON.stringify(cart)); window.location.href = "/order"; } }); } function make_card(item) { let htmlTemp = `<div class="card mb-2"> <div class="row no-gutters"> <div class="col-4" style="background: #868e96;" onclick="location.href='/detail?goodsId=${ item["goodsId"] }'"> <img src="${item["thumbnailUrl"]}" class="card-img-top h-100" alt="..."> </div> <div class="col-8"> <div class="card-body py-1"> <div class="card-title row mt-2"> <p class="font-weight-bold col" style="display: inline">${ item["name"] }</p> <span class="card-price col text-right">$${number2decimals( item["price"] )}</span> </div> <div class="form-group row mr-0"> <div class="col-7 pr-2"> <button class="btn btn-outline-sparta btn-block" onclick="removeItem(${ item["goodsId"] })">삭제</button> </div> <select class="custom-select col-5" id="numberSelect-${ item["goodsId"] }"> <option selected value=1>1</option> <option value=2>2</option> <option value=3>3</option> </select> </div> </div> </div> </div> </div>`; $("#goodsList").append(htmlTemp); $(`#numberSelect-${item["goodsId"]}`).change(function() { // console.log(parseInt($(this).val())) $.ajax({ type: "PATCH", url: `/api/goods/${item["goodsId"]}/cart`, data: { quantity: parseInt($(this).val()) }, success: function(response) { if (response["result"] == "success") { console.log(response["msg"]); getSum(); } } }); }); } function removeItem(goodsId) { $.ajax({ type: "DELETE", url: `/api/goods/${goodsId}/cart`, data: {}, success: function(response) { if (response["result"] == "success") { get_goods(); } } }); } function getSum() { let $prices = $(".card-price"); let $selects = $("select"); if ($prices.length != $selects.length) { console.log("길이가 맞지 않습니다."); return; } let sum = 0; for (let i = 0; i < $prices.length; i++) { let price = parseFloat( $($prices[i]) .text() .replace("$", "") ); let count = parseInt($($selects[i]).val()); // console.log(price, count) sum += price * count; } $("#priceSum").text("$" + number2decimals(sum)); sessionStorage.setItem("priceSum", number2decimals(sum)); } function number2decimals(num) { return (Math.round(num * 100) / 100).toFixed(2); } </script> <style> .card { cursor: pointer; } </style> </head> <body> <nav class="navbar navbar-expand-sm navbar-dark bg-sparta justify-content-end" > <a class="navbar-brand" href="/goods"> <img src="/static/logo_big_tr.png" width="30" height="30" class="d-inline-block align-top" alt="" /> 스파르타 쇼핑몰 </a> <button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="true" aria-label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="navbar-collapse collapse flex-grow-0 ml-auto" id="navbarSupportedContent" style="" > <ul class="navbar-nav mr-auto text-right"> <li class="nav-item" id="link-cart"> <a class="nav-link" href="/cart"> 장바구니<i class="fa fa-shopping-cart ml-2" aria-hidden="true" ></i> </a> </li> <li class="nav-item" id="link-logout"> <a class="nav-link" data-toggle="modal" data-target="#signOutModal"> 로그아웃<i class="fa fa-sign-out ml-2" aria-hidden="true"></i> </a> <div class="modal text-left" id="signOutModal" tabindex="-1" role="dialog" aria-labelledby="signOutModalLabel" aria-hidden="true" > <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="signOutModalLabel">로그아웃</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close" > <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> 로그아웃하시면 장바구니가 사라져요! </div> <div class="modal-footer"> <button type="button" class="btn btn-outline-sparta" data-dismiss="modal" > 취소 </button> <button type="button" class="btn btn-sparta" onclick="sign_out()" > 로그아웃하기 </button> </div> </div> </div> </div> </li> </ul> </div> </nav> <div class="wrap"> <div class="mb-3"> <h4> <i class="fa fa-shopping-cart mr-1" aria-hidden="true"></i>장바구니 </h4> </div> <div id="goodsList"> <div class="card mb-2" onclick="location.href='#'"> <div class="row no-gutters"> <div class="col-4" style="background: #868e96;"> <img src="https://cdn.pixabay.com/photo/2016/09/07/19/54/wines-1652455_1280.jpg" class="card-img-top h-100" alt="..." /> </div> <div class="col-8"> <div class="card-body py-1"> <div class="card-title row mt-2"> <p class="font-weight-bold col" style="display: inline"> 상품 1 </p> <span class="card-price col text-right">$6.20</span> </div> <div class="form-group row mr-0"> <div class="col-7 pr-2"> <button class="btn btn-outline-sparta btn-block"> 삭제 </button> </div> <!-- <label for="numberSelect" class="col col-form-label">수량</label>--> <select class="custom-select col-5" id="numberSelect"> <option selected value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> </div> </div> </div> </div> </div> </div> <hr /> <div class="row mb-3"> <div class="col-5">총 상품금액</div> <div class="col-7 text-right" id="priceSum">$6.20</div> </div> <button type="button" class="btn btn-sparta btn-block" onclick="order()"> 구매 </button> </div> </body> </html> ```
장바구니 페이지 API
router.get("/cart", async (req, res) => { const cart = await Cart.find({}); const goodsId = cart.map(cart => cart.goodsId); goodsInCart = await Goods.find() .where("goodsId") .in(goodsId); concatCart = cart.map(c => { for (let i = 0; i < goodsInCart.length; i++) { if (goodsInCart[i].goodsId == c.goodsId) { return { quantity: c.quantity, goods: goodsInCart[i] }; } } }); res.json({ cart: concatCart }); });
- 장바구니 아이템 삭제하기
- 장바구니에 추가하는 기능을 구현해냈으니, 반대로 삭제하는 기능을 구현해서 만약 실제 고객이 쓰는 것이라 보면 실수로 장바구니에 담을 수도 있으니까 삭제 기능도 구현을 해줬다.
- 장바구니 추가 했던 파일에 삭제할 수 있는 기능을 구현하기 위해서 파일을 수정해줬다.
장바구니 아이템 삭제 기능 추가
router.delete("/goods/:goodsId/cart", async (req, res) => { const { goodsId } = req.params; const isGoodsInCart = await Cart.find({ goodsId }); if (isGoodsInCart.length > 0) { await Cart.deleteOne({ goodsId }); } res.send({ result: "success" }); });
- 상품 수량 수정하기
- 장바구니에서 상품 수량이 원하는 만큼 구현이 안됐어서 그 기능을 추가해줬다.
- 상품 수량을 적어주기 위해서 장바구니 페이지 만든 파일을 수정을 해줬다.
- 수량을 변경해주는 API를 추가해줬다.
장바구니 상품 수량을 변경해주는 API
router.patch("/goods/:goodsId/cart", async (req, res) => { const { goodsId } = req.params; const { quantity } = req.body; isCart = await Cart.find({ goodsId }); console.log(isCart, quantity); if (isCart.length) { await Cart.updateOne({ goodsId }, { $set: { quantity } }); } res.send({ result: "success" }); })
- 구매 페이지 추가하기
- 마지막으로 장바구니에 상품을 담거나 바로 구매를 하기 위해서 구매 페이지를 구현했다.
- 구매 페이지가 추가되어서 라우트를 추가해줬다.
쇼핑몰 구매 페이지
<!DOCTYPE html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <link rel="icon" href="/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" /> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous" /> <!-- Font Awesome CSS --> <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /> <link href="/static/mystyle.css" rel="stylesheet" /> <title>스파르타 쇼핑몰 | 주문</title> <style> .card { cursor: pointer; } </style> </head> <body> <nav class="navbar navbar-expand-sm navbar-dark bg-sparta justify-content-end" > <a class="navbar-brand" href="/goods.html"> <img src="/static/logo_big_tr.png" width="30" height="30" class="d-inline-block align-top" alt="" /> 스파르타 쇼핑몰 </a> <button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="true" aria-label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="navbar-collapse collapse flex-grow-0 ml-auto" id="navbarSupportedContent" > <ul class="navbar-nav mr-auto text-right"> <li class="nav-item" id="link-cart"> <a class="nav-link" href="/cart.html"> 장바구니<i class="fa fa-shopping-cart ml-2" aria-hidden="true" ></i> </a> </li> <li class="nav-item" id="link-logout"> <a class="nav-link" data-toggle="modal" data-target="#signOutModal"> 로그아웃<i class="fa fa-sign-out ml-2" aria-hidden="true"></i> </a> <div class="modal text-left" id="signOutModal" tabindex="-1" role="dialog" aria-labelledby="signOutModalLabel" aria-hidden="true" > <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="signOutModalLabel">로그아웃</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close" > <span aria-hidden="true">×</span> </button> </div> <div class="modal-body">정말로 로그아웃 하시겠습니까?</div> <div class="modal-footer"> <button type="button" class="btn btn-outline-sparta" data-dismiss="modal" > 취소 </button> <button type="button" class="btn btn-sparta" onclick="signOut()" > 로그아웃하기 </button> </div> </div> </div> </div> </li> </ul> </div> </nav> <div class="wrap"> <div class="mb-3"> <h4><i class="fa fa-truck mr-1" aria-hidden="true"></i>배송지 입력</h4> </div> <div> <div class="form-group"> <label for="exampleInputName">받는 사람</label> <input type="text" class="form-control" id="exampleInputName" placeholder="홍길동" /> </div> <div class="form-group"> <label for="exampleInputMobile">연락처</label> <input type="text" class="form-control" id="exampleInputMobile" placeholder="010-xxx-xxxx" /> </div> <div class="form-group"> <label for="exampleInputAddress1">주소</label> <input type="text" class="form-control" id="exampleInputAddress1" placeholder="도로명, 건물명, 번지 검색" /> <input type="text" class="form-control mt-2" id="exampleInputAddress2" placeholder="상세주소" /> </div> <button type="button" class="btn btn-sparta btn-block" data-toggle="modal" data-target="#orderModal" id="btnOrder" > $0.20 결제 </button> <div class="modal text-left" id="orderModal" tabindex="-1" role="dialog" aria-labelledby="orderModalLabel" aria-hidden="true" > <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="orderModalLabel">결제완료</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close" > <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <span id="nickname"></span>님, 주문해주셔서 감사합니다 :) </div> <div class="modal-footer"> <button type="button" class="btn btn-outline-sparta btn-confirm" data-dismiss="modal" > 확인 </button> </div> </div> </div> </div> </div> </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous" ></script> <script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script> <script> $(document).ready(function () { getSelf(function (user) { const order = JSON.parse(sessionStorage.getItem("ordered")); $("#nickname").text(user.nickname); $("#btnOrder") .text( `$${number2decimals( order.reduce( (s, o) => s + Number(o.goods.price) * o.quantity, 0 ) )} 결제` ) .click(function () { postOrder(user, order); }); $(".btn-confirm").click(function () { location.href = "/goods.html"; }); }); }); function number2decimals(num) { return (Math.round(num * 100) / 100).toFixed(2); } </script> </body> </html>
2. 4주차 소감
4주차에 들어와서는 쇼핑몰 만들기를 구현해봤다. 많이 부족한 것 같은데 벌써 5주차 하나 남았다... 처음엔 이게 뭐고 이게 뭘까해서 이해가 안갔지만 직접 코드를 써내려가면서 머릿속에 넣어지면서 기초는 알아가는 것 같다. 내 스스로 한 것은 아니고 강의 영상을 보면서 따라한거지만 그래도 할 줄 모르던 Node.js를 해봤다는게 의의를 두고 싶다... 남은 5주차 강의를 듣고 항해99 3주차 프로젝트를 하러가야겠다 ㅜㅜ
'스파르타코딩클럽 웹개발 종합반' 카테고리의 다른 글
[항해4기] 스파르타코딩클럽 웹개발 종합반 후기 회고록 (0) 2021.11.08 [항해4기] 스파르타코딩클럽 웹개발 종합반 5주차 개발일지 (2) 2021.11.08 [항해4기] 스파르타코딩클럽 웹개발 종합반 4주차 개발일지 (0) 2021.11.08 [항해4기] 스파르타코딩클럽 웹개발 종합반 3주차 개발일지 (0) 2021.11.08 [항해4기] 스파르타코딩클럽 웹개발 종합반 2주차 개발일지 (0) 2021.11.08