프로그래밍 언어/NODE JS

프로젝트 마무리하기

· 코딩마이데이

마지막으로 낙찰자가 낙찰 내역을 볼 수 있도록 해보겠습니다.

 

routes/index.js

const express = require("express");
const multer = require("multer");
const path = require("path");
const fs = require("fs");
const schedule = require("node-schedule");

const { Good, Auction, User, sequelize } = require("../models");
const { isLoggedIn, isNotLoggedIn } = require("./middlewares");

const router = express.Router();

router.use((req, res, next) => {
  res.locals.user = req.user;
  next();
});

router.get("/", async (req, res, next) => {
  try {
    const goods = await Good.findAll({ where: { SoldId: null } });
    res.render("main", {
      title: "NodeAuction",
      goods,
    });
  } catch (error) {
    console.error(error);
    next(error);
  }
});

router.get("/join", isNotLoggedIn, (req, res) => {
  res.render("join", {
    title: "회원가입 - NodeAuction",
  });
});

router.get("/good", isLoggedIn, (req, res) => {
  res.render("good", { title: "상품 등록 - NodeAuction" });
});

try {
  fs.readdirSync("uploads");
} catch (error) {
  console.error("uploads 폴더가 없어 uploads 폴더를 생성합니다.");
  fs.mkdirSync("uploads");
}
const upload = multer({
  storage: multer.diskStorage({
    destination(req, file, cb) {
      cb(null, "uploads/");
    },
    filename(req, file, cb) {
      const ext = path.extname(file.originalname);
      cb(
        null,
        path.basename(file.originalname, ext) + new Date().valueOf() + ext,
      );
    },
  }),
  limits: { fileSize: 5 * 1024 * 1024 },
});
router.post(
  "/good",
  isLoggedIn,
  upload.single("img"),
  async (req, res, next) => {
    try {
      const { name, price } = req.body;
      const good = await Good.create({
        OwnerId: req.user.id,
        name,
        img: req.file.filename,
        price,
      });
      const end = new Date();
      end.setDate(end.getDate() + 1);
      schedule.scheduleJob(end, async () => {
        const success = await Auction.findOne({
          where: { GoodId: good.id },
          order: [["bid", "DESC"]],
        });
        await Good.update(
          { SoldId: success.userId },
          { where: { id: good.id } },
        );
        await User.update(
          {
            money: sequelize.literal(`money - ${success.bid}`),
          },
          {
            where: { id: success.userId },
          },
        );
      });
      res.redirect("/");
    } catch (error) {
      console.error(error);
      next(error);
    }
  },
);

router.get("/list", isLoggedIn, async (req, res, next) => {
  try {
    const goods = await Good.findAll({
      where: { SoldId: req.user.id },
      include: { model: Auction },
      order: [[{ model: Auction }, "bid", "DESC"]],
    });
    res.render("list", { title: "낙찰 목록 - NodeAuction", goods });
  } catch (error) {
    console.error(error);
    next(error);
  }
});

module.exports = router;

 

낙찰된 상품과 그 상품의 입찰 내역을 조회한 후 렌더링합니다. 입찰 내역은 내림차순으로 정렬하여 낙찰자의 내역이 가장 위에 오도록 했습니다.

 

views/list.html

{% extends "layout.html" %} {% block content %}
<div class="timeline">
  <h2>경매 낙찰 목록</h2>
  <table id="good-list">
    <tr>
      <th>상품명</th>
      <th>사진</th>
      <th>낙찰자</th>
    </tr>
    {% for good in goods %}
    <tr>
      <td>{{ good.name }}</td>
      <td><img src="/img/{{good.img}}" /></td>
      <td>{{good.Auctions[0].bid}}</td>
    </tr>
    {% endfor %}
  </table>
</div>
{% endblock %}

 

낙찰 목록 화면을 추가합니다.

 

views/layout.html

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>{{title}}</title>
    <meta name="viewport" content="width=device-width, user-scalable=no" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="stylesheet" href="/main.css" />
  </head>
  <body>
    <div class="container">
      <div class="profile-wrap">
        <div class="profile">
          {% if user and user.id %}
          <div class="user-name">안녕하세요 {{user.nick}}님</div>
          <div class="user-money">보유 자산: {{user.money}}원</div>
          <input type="hidden" id="my-id" value="user.id" />
          <a href="/auth/logout" id="logout" class="btn">로그아웃</a>
          <a href="/good" id="register" class="btn">상품 등록</a>
          <a href="/list" id="list" class="btn">낙찰 내역</a>
          {% else %}
          <form action="/auth/login" id="login-form" method="post">
            <div class="input-group">
              <label for="email">이메일</label>
              <input type="email" id="email" name="email" required autofocus />
            </div>
            <div class="input-group">
              <label for="password">비밀번호</label>
              <input type="password" id="password" name="password" required />
            </div>
            <a href="/join" id="join" class="btn">회원가입</a>
            <button id="login" class="btn" type="submit">로그인</button>
          </form>
          {% endif %}
        </div>
        <footer>
          Made by&nbsp;<a href="https://www.zerocho.com" target="_blank"
            >ZeroCho</a
          >
        </footer>
        {% block good %} {% endblock %}
      </div>
      {% block content %} {% endblock %}
    </div>
    <script>
      window.onload = () => {
        if (new URL(location.href).searchParams.get("loginError")) {
          alert(new URL(location.href).searchParams.get("loginError"));
        }
      };
    </script>
  </body>
</html>

 

낙찰 목록을 이동할 수 있는 버튼을 추가했습니다.

낙찰자의 계정으로 로그인하면 http://localhost:8010/list에서 낙찰된 목록을 확인할 수 있습니다.