TIL 230419_Node.js 입문3
✅오늘 학습 Keyword
댓글 작성, 조회, 상세조회, 수정, 삭제 기능 구현하기
✅오늘 겪은 문제
comments부분의 라우터를 아무리 만져봐도 .. 서버가 연결되지 않는다.
comments의 POST 부분만 구현하면 그 다음 것들은 일사천리 일텐데 계속 답이 보이지 않았음.
✅해결 시도, 알게된 점
처음부터 comments와 posts를 연결지어 생각했던게 문제였던 것 같았다.
posts와 같이 우선 comments를 별개의 data로 생각해서 다시 시작했다.
또한 posts에 comments를 연결짓기보단, comments에 post를 연결짓는 관점으로 접근했다.
1. 우선 아래와 같이 comments 스키마를 만들어주었다.
물론 처음엔 _postId란은 없었고, comments만 생성해줬었다.
comments가 정상 생성되는 것을 확인 후에 _postId를 통해 게시글에 comments달아주었다.
const mongoose = require("mongoose");
//어떤 게시글의 댓글인지 표기
//게시글의 id를 string화 해서 연결
const CommentSchema = new mongoose.Schema({
user: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
_postId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
},
});
const Comment = mongoose.model("Comments", CommentSchema);
module.exports = Comment;
2. 제일 애먹었던 부분인..서버 부분.
처음엔 router 경로를 아래와 같이 posts, comments 따로 잡아주었는데
app.use("/api", postsRouter);
app.use("/api/posts/comments", commentsRouter);
이 상태에서 댓글을 작성하려고 /api/posts/comments/create를 시도하면 Cannot GET /api/posts에러가 떠서
진짜 미칠 것 같았다. 계속 찾아본 결과 문제는 아래와 같았다.
commentsRouter에 이미 /create 라우트가 등록되어 있기 때문에, app.use("/api/posts/comments", commentsRouter)를 사용하면 /api/posts/comments/create 경로가 중복되어 생겨서 오류가 발생합니다. 따라서, app.use("/api/posts", postsRouter)를 사용하고 postsRouter에서 /comments 경로를 추가하는 방식으로 수정해보세요.
그러니까 위의 상태에선 경로가 중복되어 오류가 나는 것이였다.
우선 모든 app.js를 포함한 모든 라웃츠의 경로를 하나하나 살펴보고 겹치거나 잘못된 것이 없는지 확인 후 수정해주었다.
그리고 완성된 코드.
const express = require("express");
const app = express();
const port = 3000;
const postsRouter = require("./routes/posts.js");
// const commentsRouter = require("./routes/comments.js");
const connect = require("./schemas");
connect();
app.use(express.json()); //실제로 body에 data가 들어왔을때 들어온 body data를 사용하게 해주는 미들웨어
app.use("/api/posts", postsRouter); //전역 middleware로써 router를 등록한다
// app.use("/api/posts", commentsRouter);
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(port, () => {
console.log(port, "포트로 서버가 열렸어요!");
});
3. 댓글작성 기능은 위의 코드로 다 해결될 줄 알았는데 작성은 여전히 안되고 이젠 전체 게시글도 조회가 안된다..
아마도 comments를 posts로 이어주는 부분에서 뭔가가 잘못 된 것 같았다.
곰곰히 생각해보니 post에 comments라는 키값을 줘야 comments data가 정상적으로 전달 될 것 같았다.
아래와 같이 post 스키마에도 comments 항목을 추가해주었다.
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
user: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
});
const Post = mongoose.model("Posts", postSchema);
module.exports = Post;
그 다음엔 힘든 것 없이 차근차근 잘 만들었고 정상적으로 모든 기능들이 구현되었다.
과제에서는 routes에 comments, posts를 각각 만드는 structure를 요구했으나 서버가 더 꼬이는 것 같아서
posts로 한번에 작성했다.
완성!
const express = require("express");
const router = express.Router();
const Post = require("../schemas/post.js");
const Comment = require("../schemas/comment.js");
//게시글 목록 조회
// router.get("/posts", (req, res) => {
// res.status(200).json({ posts: posts });
// });
// 게시글 전체 목록 조회하기
router.get("/", async (req, res) => {
const posts = await Post.find({}).select("-password -content"); //Post 스키마 안에 있는 password를 제외한 모든 데이터
res.status(200).json({ posts: posts });
});
// 게시글 상세조회
router.get("/:_postId", async (req, res) => {
const { _postId } = req.params;
const detail = await Post.findOne({ _id: _postId }).select("-password"); //data에 _postId와 _id 대응되는 값 = detail
console.log(detail);
if (!detail) {
return res
.status(400)
.json({ message: "데이터 형식이 올바르지 않습니다." });
}
res.json({ detail });
});
//게시글 수정 //비밀번호 있어야함
router.put("/:_postId", async (req, res) => {
const { _postId } = req.params;
const { content, password } = req.body;
const existsPost = await Post.findById(_postId);
if (existsPost) {
if (existsPost.password === password) {
await Post.updateOne({ _id: _postId }, { $set: { content } });
res.json({ success: true });
} else {
return res.status(401).json({ message: "비밀번호가 올바르지 않습니다." });
}
} else {
return res.status(400).json({ message: "해당 ID의 게시글이 없습니다." });
}
});
//게시글 삭제 //비밀번호 있어야함
router.delete("/:_postId", async (req, res) => {
const { _postId } = req.params;
const { password } = req.body; // 요청 본문에서 비밀번호를 추출합니다.
const post = await Post.findById(_postId);
if (post) {
if (post.password !== password) {
// 비밀번호 검증 절차입니다.
return res.status(400).json({ message: "비밀번호가 일치하지 않습니다." });
}
await Post.deleteOne({ _id: _postId });
res.json({ success: true });
} else {
return res.status(400).json({ message: "해당 ID의 게시글이 없습니다." });
}
});
//게시글등록
router.post("/create", async (req, res) => {
const { user, title, password, content, createdAt } = req.body;
console.log(user, title, password, content, createdAt);
try {
const createdPosts = await Post.create({
user,
title,
password,
content,
createdAt,
});
console.log(createdPosts);
res
.status(200)
.json({ message: "게시글을 등록하였습니다.", posts: createdPosts });
} catch (error) {
console.error(error);
res.status(400).json({ message: "게시글 등록에 실패하였습니다." });
}
});
//게시물의 댓글들 조회
router.get("/:_postId/comments/", async (req, res) => {
const _postId = req.params._postId;
const comments = await Comment.find({ _postId }).select("-password");
res.status(200).json({ comments: comments });
});
//댓글 상세조회
router.get("/:_postId/comments/:_commentsId", async (req, res) => {
const { _postId, _commentsId } = req.params;
const detail = await Comment.findOne({ _id: _commentsId, _postId }); //
if (!detail) {
return res
.status(400)
.json({ message: "데이터 형식이 올바르지 않습니다." });
}
res.json({ detail });
});
//댓글 등록
router.post("/:_postId/comments/create", async (req, res) => {
const { user, password, content, createdAt } = req.body;
const _postId = req.params._postId;
try {
const createdComments = await Comment.create({
user,
password,
content,
createdAt,
_postId,
});
const post = await Post.findOneAndUpdate(
{ _id: _postId },
{ $push: { comments: createdComments.id } }
);
res
.status(200)
.json({ message: "댓글을 등록하였습니다.", comments: createdComments });
} catch (error) {
console.error(error);
res.status(400).json({ message: "댓글 등록에 실패하였습니다." });
}
});
//댓글 수정
router.put("/:_postId/comments/:_commentId", async (req, res) => {
const { _commentId } = req.params;
const { content, password } = req.body;
const existsComment = await Comment.findById(_commentId); //existsPost-->단일 게시물이므로 post로 받아서 찾아보자
if (!existsComment) {
return res.status(400).json({ message: "댓글을 찾을 수 없습니다." });
}
if (existsComment.password !== password) {
return res.status(400).json({ message: "비밀번호가 올바르지 않습니다." });
}
existsComment.content = content;
await existsComment.save();
res.status(200).json({ message: "댓글을 수정했습니다.", existsComment });
});
//댓글삭제
router.delete("/:_postId/comments/:_commentId", async (req, res) => {
const { _commentId } = req.params;
const { password } = req.body; // 요청 본문에서 비밀번호를 추출합니다.
const comment = await Comment.findById(_postId);
if (comment) {
if (comment.password !== password) {
// 비밀번호 검증 절차입니다.
return res.status(400).json({ message: "비밀번호가 일치하지 않습니다." });
}
await Comment.deleteOne({ _id: _commentId });
res.json({ success: true });
} else {
return res.status(400).json({ message: "해당 ID의 게시글이 없습니다." });
}
});
module.exports = router;