본문 바로가기
개발/차근차근 개발일지 TIL

TIL 230419_Node.js 입문3

by 코딩하는짱구 2023. 4. 19.
반응형

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;
반응형