ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [항해4기] 스파르타코딩클럽 Node.js 기초반 4주차 회고록
    스파르타코딩클럽 웹개발 종합반 2021. 11. 16. 22:14

    스파르타코딩클럽 Node.js 기초반

    4주차 개발일지

     

     

     

     

    목차

     

    1.  쇼핑몰 페이지 구현
    2.  4주차 소감

     

     

     

     

    1. 쇼핑몰 페이지 구현

     

    - 상품 필터링하기

    • 카테고리의 음료면 음료 음식이면 음식으로 나게 구현해줬다. 
    • HTML을 .ejs로 변환해서 만들어줬고, 라우팅을 해줬다.
    • 라우팅 후 DB를 활용하기 위해 API 작업도 해줬다.

     

     

    카테고리 '음료'

    음료를 DB에 담아서 호출을 해줬다.

    카테고리 '음식'

    음식을 DB에 담아서 호출을 해줬다.

     

    - 상품 장바구니에 담기

    • 상품을 장바구니에 담기 위해서 데이터 정보를 담아서 정보를 넘겨주는 작업을 했다.
    • 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">&times;</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">&times;</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">&times;</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주차 프로젝트를 하러가야겠다 ㅜㅜ

     

     

Designed by Tistory.