그저 내가 되었고
항해99) 4주차:: 주특기 숙련; 개인과제 전체코드(JS)(ver.Sequelize) 본문
사족
이걸 굳이 매번 다 올리는 이유는...
항해하면서 너무 힘들지만 절대 포기하고 싶지 않은 분들이 보시고 꼭 도움 받으시길 원하기 때문입니다.
제 뒤에 항해하시게 될 누군가가 만약 저처럼 너무너무 힘들어 하고 계시다면(T T) 그 마음을 너무 잘 알기에.. 제 마음도 많이 아플 것 같아요.
답지 보고 공부하는것도 도움이 많이 되더라고요. 잘 이해만 하면 돼요.
꼭, 이해하는것 절대로 포기하지 마시고 끝까지 완주해봅시다,,,,,(나도!!!!!!!!!!!!!!!!!!!!!!!)
app.js
더보기
const express = require("express");
const postsRouter = require("./routes/posts");
const commentsRouter = require("./routes/comments");
const signupRouter = require("./routes/signup");
const loginRouter = require("./routes/login");
const app = express();
const router = express.Router();
app.use(express.json());
// 👆🏻JSON 이라는 규격의 body 데이터를 손쉽게 사용할 수 있게 도와주는 미들웨어
app.use("/", express.urlencoded({ extended: false }), router);
// 👆🏻form-urlencoded 라는 규격의 body 데이터를 손쉽게 사용할 수 있게 도와주는 미들웨어
app.use("/", [postsRouter, commentsRouter, signupRouter, loginRouter]);
app.listen(8080, () => {
console.log("서버가 요청을 받을 준비가 됐어요");
});
middlewares/auth-middleware
더보기
const jwt = require("jsonwebtoken");
const {Users} = require("../models"); //⭐
module.exports = async (req, res, next) => {
const { authorization } = req.headers;
// console.log(authorization)
const [ authType, authToken ] = (authorization || "").split(" ");
//console.log(authToken)
if (!authToken || authType !== "Bearer" ) {
res.status(401).send({
errorMessage: "로그인이 필요합니다.",
});
return;
}
try {
// 검증 ( userId만 필요)
const {userId} = jwt.verify(authToken, "시크릿키");
//console.log("TEST;", userId)
Users.findByPk(userId).then((user) => {
res.locals.user = user;
//console.log(res.locals.user)
// **** 반드시 next 먼저 호출해야함 안그러면 미들웨어 레벨 예외처리 걸려서 그 뒤에있는 미들웨어는 연결 x
next();
});
} catch (err) {
res.status(401).send({
errorMessage: "로그인이 필요한 기능입니다.",
});
}
};
migrations/user.js
더보기
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', {
userId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
nickname: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Users');
}
};
migrations/posts.js
더보기
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Posts', {
postId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
nickname: {
type: Sequelize.STRING
},
title: {
type: Sequelize.STRING
},
content: {
type: Sequelize.STRING
},
likes: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Posts');
}
};
migrations/like.js
더보기
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Likes', {
likeId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
postId: {
type: Sequelize.INTEGER
},
nickname: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Likes');
}
};
migrations/comment.js
더보기
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Comments', {
commentId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
postId: {
type: Sequelize.INTEGER
},
nickname: {
type: Sequelize.STRING
},
comment: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Comments');
}
};
models/user.js
더보기
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Users extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Users.init({
userId: {
primaryKey: true,
type: DataTypes.INTEGER,
},
nickname: DataTypes.STRING,
password: DataTypes.STRING
}, {
sequelize,
modelName: 'Users',
});
return Users;
};
models/posts.js
더보기
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Posts extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Posts.init({
postId: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true
},
nickname: DataTypes.STRING,
title: DataTypes.STRING,
content: DataTypes.STRING,
likes: DataTypes.INTEGER
}, {
sequelize,
modelName: 'Posts',
});
return Posts;
};
models/like.js
더보기
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Likes extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Likes.init({
likeId: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true
},
postId: DataTypes.INTEGER,
nickname: DataTypes.STRING,
}, {
sequelize,
modelName: 'Likes',
});
return Likes;
};
models/comment.js
더보기
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Comments extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Comments.init({ //💛💛
commentId: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true
},
postId: DataTypes.INTEGER,
nickname: DataTypes.STRING,
comment: DataTypes.STRING
}, {
sequelize,
modelName: 'Comments', //💛💛
});
return Comments; //💛💛
};
routes/signup.js
더보기
const express = require("express");
const router = express.Router();
const { Users } = require("../models"); //⭐
const Joi = require("joi");
const userSchema = Joi.object({
nickname: Joi.string(), //.pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(),
password: Joi.string(), //.pattern(new RegExp("^{4,30}$")).required(),
confirm: Joi.string() //.pattern(new RegExp("^{4,30}$")).required(),
});
router.post("/signup", async (req, res) => {
try {
const {nickname, password, confirm} = await userSchema.validateAsync(req.body); //joi...⁉⁉⁉⁉⁉
if (password.search(nickname) === nickname) {
res.status(400).send({ errorMessage: "형식에 맞지 않는 비밀번호입니다." });
return;
}
if (password !== confirm) {
res.status(400).send({ errorMessage: "패스워드가 패스워드 확인란과 다릅니다." });
return;
}
// nickname 동일한게 이미 있는지 확인
const existsUsers = await Users.findOne({where: { nickname }})
if (existsUsers) {
res.status(400).send({ errorMessage: "중복된 닉네임입니다." });
return;
}
await Users.create({nickname, password});
res.status(201).send({message: "회원가입에 성공하였습니다."});
} catch (error) {
console.log(`${req.method} ${req.originalUrl} : ${error.message}`);
res.status(400).send({ errorMessage: "요청한 데이터 형식이 올바르지 않습니다." });
}
});
module.exports = router;
routes/login.js
더보기
const express = require("express");
const {Users} = require("../models"); //⭐
const router = express.Router();
const jwt = require("jsonwebtoken");
router.post("/login", async (req, res) => {
const { nickname, password } = req.body;
const user = await Users.findOne({where: { nickname }});
if (!user || password !== user.password || nickname !== user.nickname) {
res.status(400).send({
errorMessage: "닉네임 또는 패스워드를 확인해주세요.",
});
return;
}
res.send({
token: jwt.sign({ userId: user.userId }, "시크릿키"),
});
});
module.exports = router;
routes/posts.js
더보기
const express = require('express');
const router = express.Router();
const {Posts} = require("../models"); //⭐
const {Likes} = require("../models"); //⭐
const authMiddleware = require("../middlewares/auth-middleware");
//1. 전체 게시글 목록 조회 API O
router.get("/posts", async (req, res) => {
const item = await Posts.findAll({order: [["updatedAt", "DESC"]]})
const mapItem = item.map((item) => {
return {
postId: item._id,
nickname: item.nickname,
title: item.title,
createdAt: item.createdAt,
updatedAt: item.updatedAt,
likes: item.likes
}
})
res.json({item: mapItem});
});
//6. 라잌 게시글 목록 조회 API O
router.get("/posts/like", authMiddleware, async (req, res) => {
const { nickname } = res.locals.user; //💛💛💛💛💛
const arrLike = await Likes.findAll({ where: { nickname } }) //💛💛💛💛💛
const arrPostId = arrLike.map((val) => { //💛💛💛💛💛
return val.postId //postId만 리턴해서 배열한 것
})
const item = await Posts.findAll({ where: { postId: arrPostId }, order: [[ "likes", "DESC" ]]}) //💛💛💛💛💛
const mapItem = item.map((item) => {
return {
postId: item.postId,
nickname: item.nickname,
title: item.title, //findOne 관계 지어져 있으면 join으로 가져옴.. 여기 title을 그냥 써버리면 게시글 수정되면 반영이 안됨
createdAt: item.createdAt,
updatedAt: item.updatedAt,
likes: item.likes
}
})
res.json({ item: mapItem/*, test: "test" */});
});
//7. 라잌/디스라잌 게시글 API O
router.put("/posts/:postId/like", authMiddleware, async (req, res) => {
const { postId } = req.params;
const { nickname } = res.locals.user;
const is_liked = await Likes.findOne({ where: { postId, nickname } });
console.log(is_liked)
/*취소*/
if (is_liked) {
await Likes.destroy({ where: { postId, nickname } } );
const post = await Posts.findOne({ where: { postId } } );
likesnum = (post.likes) * 1 - 1;
await Posts.update({ likes: likesnum }, { where: { postId } });
res.send({ result: "success", message: "게시글의 좋아요를 취소하였습니다." });
}
/*신규*/
else {
const post = await Posts.findOne({ where: { postId } });
likesnum = (post.likes) * 1 + 1;
await Likes.create({ postId, nickname });
await Posts.update({ likes: likesnum }, { where: { postId } });
res.send({result: "success", message: "게시글의 좋아요를 등록하였습니다."});
}
}
);
//2. 게시글 상세 조회 API O
router.get("/posts/:postId", async (req, res) => {
const {postId} = req.params;
const posts = await Posts.findAll({})
const filteredPosts = posts.filter((item) => {
return item["postId"].toString() === postId;
});
const mapPosts = filteredPosts.map((item) => {
return {
postId: item.postId,
nickname: item.nickname,
title: item.title,
content: item.content,
createdAt: item.createdAt,
updatedAt: item.updatedAt,
likes: item.likes
}
})
res.json({mapPosts});
})
//3. 게시글 작성 API O
router.post("/posts", authMiddleware, async (req, res) => {
try {
const {user} = res.locals;
const existPosts = await Posts.findAll({order: [["postId", "DESC"]]})
if (existPosts.length !== 0) {
postId = existPosts[0].postId + 1;
await Posts.create({
postId: postId,
nickname: user.nickname,
title: req.body.title,
content: req.body.content,
likes: 0
});
res.status(201).send({message: "게시글 작성에 성공하였습니다."});
} else {
await Posts.create({
postId: 1,
nickname: user.nickname,
title: req.body.title,
content: req.body.content,
likes: 0
});
res.status(201).send({message: "게시글 작성에 성공하였습니다."});
}
} catch (Error) {
return res.status(400).json({message: "게시글 작성에 실패하였습니다."});
}
})
//4. 게시글 수정 API O
router.put("/posts/:postId", authMiddleware, async (req, res) => {
const {postId} = req.params;
const {content, title} = req.body;
const existsPost = await Posts.findOne({where: {postId}});
if (existsPost) {
await Posts.update({ content, title }, { where: { postId } });
res.send({result: "success", message: "게시글을 수정하였습니다."});
} else {
res.send({result: "fail"});
}
})
//5. 게시글 삭제 API O
router.delete("/posts/:postId", authMiddleware, async (req, res) => {
const {postId} = req.params;
const existsPost = await Posts.findOne({ postId });
if (existsPost) {
await Posts.destroy({ where: { postId }});
res.send({result: "success", message: "게시글을 삭제하였습니다."});
} else {
res.send({result: "fail"});
}
})
module.exports = router;
routes/comment.js
더보기
const express = require('express');
const router = express.Router();
const {Comments} = require("../models")
const authMiddleware = require("../middlewares/auth-middleware");
//1. 댓글 목록 조회 API O
router.get("/comments/:postId", async (req, res) => {
const { postId } = req.params;
const existPosts = await Comments.findAll({where: {postId}, order: [["updatedAt", "DESC"]]})
const mapComments = existPosts.map((item) => {
return {
postId: item.postId,
commentId: item.commentId,
nickname: item.nickname,
comment: item.comment,
createdAt: item.createdAt,
updatedAt: item.updatedAt
}
})
res.json({mapComments});
});
//2. 댓글 작성 O
router.post("/comments/:postId", authMiddleware, async (req, res) => {
const { postId } = req.params;
const { user } = res.locals;
if (req.body.comment === "") {
res.status(400).json({success: false, errorMessage: "댓글 내용을 입력해주세요"});
} else {
await Comments.create({
postId: postId,
nickname: user.nickname,
comment: req.body.comment
});
res.status(201).send({message: "댓글 등록이 완료되었습니다."});
}
})
//3. 댓글 수정 O
router.put("/comments/:commentId", authMiddleware, async (req, res) => {
try {
const { commentId } = req.params;
const { nickname } = res.locals.user;
const { comment } = req.body;
const existsComment = await Comments.findOne({where: {commentId, nickname}});
if (comment === "") {
res.status(400).json({success: false, errorMessage: "댓글 내용을 입력해주세요"});
}
if (existsComment) {
await Comments.update({ comment }, {where: { commentId, nickname }});
res.send({message: "댓글을 수정하였습니다."})
} else {
res.status(400).json({
success: false, errorMessage: "본인의 댓글만 수정할 수 있어요!"
})
}
} catch (e) {
return res.status(400).json({success: false});
}
})
//4. 댓글 삭제 O
router.delete("/comments/:commentId", authMiddleware, async (req, res) => {
try {
const { commentId } = req.params;
const { nickname } = res.locals.user;
const existsComment = await Comments.findOne({where: { commentId, nickname }});
if (existsComment) {
await Comments.destroy({where: { commentId }});
res.send({result: "success", message: "댓글을 삭제하였습니다."});
} else {
res.status(400).json({success: false, errorMessage: "본인의 댓글만 삭제할 수 있어요!"});
}
} catch (e) {
return res.status(400).json({ result: "fail" });
}
})
module.exports = router;
'개발 > 항해99 9기' 카테고리의 다른 글
항해99) 5주차:: 주특기 심화; 팀과제 전체코드(JS) (0) | 2022.10.17 |
---|---|
항해99) 5주차:: 주특기 심화; 📚Socket.io (0) | 2022.10.15 |
항해99) 4주차:: 주특기 숙련; 📚미들웨어 (0) | 2022.10.12 |
항해99) 4주차:: 주특기 숙련; 📚MySQL → Sequelize (0) | 2022.10.12 |
항해99) 4주차:: 주특기 숙련; 개인과제 전체코드(JS)(ver.MongoDB) (0) | 2022.10.11 |