프로젝트 마무리하기
마지막으로 낙찰자가 낙찰 내역을 볼 수 있도록 해보겠습니다.
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 <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에서 낙찰된 목록을 확인할 수 있습니다.
'프로그래밍 언어 > NODE JS' 카테고리의 다른 글
| 간단한 콘솔 명령어 만들기(2) (0) | 2026.03.16 |
|---|---|
| 간단한 콘솔 명령어 만들기(1) (0) | 2026.03.13 |
| 스케줄링 구현하기(2) (0) | 2026.03.07 |
| 스케줄링 구현하기 (0) | 2026.03.04 |
| 실시간 경매 시스템 - 서버센트 이벤트 사용하기(3) (0) | 2026.03.01 |