<!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;"token interpolation">${
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"token interpolation">${
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
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" });
});
이 부분에서 데이터를 post받음 --> 비동기 할당방식
router.post("/goods/:goodsId/cart", async (req, res) => {
const { goodsId } = req.params;
const { quantity } = req.body;
--> goodsId는 url을 통해서 받음 --> 코드의 구조상 그 데이터의 id가 path에 나타나기 때문( 이전의 코드 참고 )
--> quantity는 body에 들어있는 데이터를 통해서 받음 --> post형식의 api에 request할 때 body에 담은 데이터
이 부분에서 장바구니에 추가하려는 데이터가 mongoDB에 이미 있는지 확인함
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" });
});
--> 데이터가 이미 있다면 해당 데이터에 quantity부분만 수정 --> 개수 부분]
Cart.updateOne({ goodsId }, { $set: { quantity } });
---> goodsId에 해당하는 데이터의 quantity부분을 덮어씌움
--> 데이터가 없다면 해당 id와 quantity를 포함하여 데이터를 삽입
Cart.create({ goodsId: goodsId, quantity: quantity });
--> 장바구니에 있는 데이터를 가져오기 위한 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
});
});
이 부분에서 Cart(mongoDB)에 있는 데이터를 모두 가져온 후, id만 추려낸다.
router.get("/cart", async (req, res) => {
const cart = await Cart.find({});
const goodsId = cart.map(cart => cart.goodsId);
--> 즉, 장바구니에 있는 데이터들의 id가 goodsId 변수에 리스트형식으로 할당된다.
이 부분에서 전체 데이터(mongoDB)에서 장바구니에 있는 데이터에 해당하는 정보들이 가져와진다.
goodsInCart = await Goods.find()
.where("goodsId")
.in(goodsId);
--> Cart DB에는 goodsId와 quantity밖에 없다
--> Goods DB에는 goodsId, 이미지주소, 제목, 등등 해당 goodsId에 대응하는 상품에 대한 정보가 있다.
--> 따라서 Cart에 담긴 goodsId에 대한 정보들을 바탕으로 Goods 에서 해당 삼품에 대한 정보들을 가져와 goodsInCart에 저장한 것이다.
이 부분에서 Cart에서 가져온 데이터와 Goods에서 가져온 데이터를 goodsId를 기준으로 Join한다.
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] };
}
}
});
이 부분에서 cart 라는 이름으로 json형태로 데이터가 생성되어 response 된다.
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"});
})
--> mongoDB에서 데이터를 삭제하기 위한 delete 표현의 api
--> delete의 특징 --> post같은 표현과 다르게 get표현처럼 일반적으로 body를 사용하지 않음
--> 누구를 지우겠다만 필요 ==> 추가적인 정보가 필요없기 떄문
--> 삭제하려는 데이터가 DB에 존재하는지 확인후
아래의 코드를 통해 제거
await Cart.deleteOne({ goodsId });
데이터를 삭제하는 api에서 사용한다.
--> 자세한 사용은 바로 위 코드 참고
--> 수량 부분이 바뀌면 바뀐 내용이 mongoDB에 저장되도록 할것임
$(`#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();
}
}
});
});
--> goodsId를 바탕으로 데이터를 구분한 상태에서 수량부분의 변경을 감지하면
request를 보냄
request의 내용
type: "PATCH",
url: `/api/goods/${item["goodsId"]}/cart`,
data: {
quantity: parseInt($(this).val())
},
request에 대해 성공적으로 처리되어 response를 받았을 떄의 내용
success: function(response) {
if (response["result"] == "success") {
console.log(response["msg"]);
getSum();
}
참고 --> getSum함수 --> 개수에 따라 값을 변경하는 함수로 구성함
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));
}
router.patch("/goods/:goodsId/cart", async (req,res) =>{
const {goodsId} = req.params;
const {quantity} = req.body;
const isGoodsInCart = await Goods.find({goodsId});
if (isGoodsInCart.length > 0){
await Cart.updateOne({ goodsId },{$set: {quantity}})
}
res.send({ result : "success" });
})
--> 업데이트 하려는 데이터가 mongoDB에 존재하는지 확인
--> 있다면 아래의 코드를 통해 mongoDB에 있는 해당 데이터를 업데이트
await Cart.updateOne({ goodsId },{$set: {quantity}})
---> goodsId가 담고있는 내용과 일치하는 데이터에 대해 quantity부분을 quantity가 담고있는 데이터로 바꾼다는 뜻
( 일종의 비동기 할당방식이라고 볼 수 있다. )
--> DB에 있는 데이터를 수정할 경우에 put과 patch를 모두 사용할 수 있는데,
일반적으로는 둘을 구분해서 사용한다. ( 반드시 그럴 필요는 없음 )
보통 해당 데이터의 내용 전체를 수정할 때 사용한다.
--> 예를 들어)
id, 이름, 나이, 성별, 주소 라는 5가지 속성을 가지는 스키마의 DB의 경우
id = 25인 데이터를 수정한다고 쳤을 때
put을 사용하면 ( 25, 임수지, 27, 여, 두꺼비집 ) 처럼 해당 스키마의 속성 모두를 입력하여 데이터를 수정한다.
보통 해당 데이터의 일부 내용을 수정할 때 사용한다
--> 예를 들어)
id, 이름, 나이, 성별, 주소 라는 5가지 속성을 가지는 스키마의 DB의 경우
id = 25인 데이터를 수정한다고 쳤을 때
patch을 사용하면 ( 25, 두꺼비집 ) 처럼 해당 데이터의 일부 속성을 수정한다.
---> 여기서 두꺼비집이라는 값을 '주소'라는 변수에 담아 넣는 것으로 해당 스키마의 속성에 대해 비동기 할당하여 어느 속성에 대한 값을 변경할지 나타낸다.