ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Node.js LV-1,2 (게시판 게시글, 댓글 CRUD + 몽구스 Mongoose)
    Projects/Node.js 2023. 6. 23. 15:25

    https://github.com/sangwoorhie/LV2

     

    GitHub - sangwoorhie/LV2

    Contribute to sangwoorhie/LV2 development by creating an account on GitHub.

    github.com

    https://github.com/sangwoorhie/CRUDmongoosever

     

    GitHub - sangwoorhie/CRUDmongoosever

    Contribute to sangwoorhie/CRUDmongoosever development by creating an account on GitHub.

    github.com

    app.js에서는

     1. express를 호출하고, port를 3000번으로 쏴준 뒤 app.listen으로 해당 포트로 서버가 열리도록 해준다. (Express는 Node.js 웹 애플리케이션 프레임워크다.)
    2. 그리고 라우터 2개의 폴더(comments.js / posts.js)에 각각 api를 쏴준다. 
    3. connect로 스키마 폴더와도 연결한다.

     

    app.js
    
    const express = require('express');
    const app = express();
    const port = 3000;
    
    const commentsRouter = require('./routes/comments.js')  // ★★ require ('./폴더명')
    const postsRouter = require('./routes/posts.js')   // ★★ require ('./폴더명')
    
    const connect = require('./schemas');   // ★★ require ('./폴더명')
    connect(); 
    
    app.listen(port, () => {
        console.log(port, "포트로 서버가 열렸어요!");
    });
    
    app.use(express.json()); // 전역 미들웨어 body-parser
    
    // localhost:3000
    app.use("", [postsRouter, commentsRouter]); 
    
    app.get('/', (req, res) => {
        res.send('Hello World!');
    });
    
    // ★★ require ('./폴더명')
    // ★★ 폴더를 require하면, models 폴더 안의 index.js 파일에서 module.exports하는 객체를 가져오게 된다.

     

    스키마 폴더에는 comment,js (댓글), post.js( 게시글) 및 모듈을 받아올 index.js를 만들었다.

     

    여기서 스키마 폴더란 : 

    컬렉션
    JSON형식의 여러가지 문서(document)를 보유할수있다
    이후에 설명할 관계형 데이터베이서의 table과 동일한역할을한다
    
    스키마
    컬렉션에 들어가는 문서(document)에 어떤 종류의 값이 들어가는지를 정의함
    
    대표적 스키마의 타입
    null
    String 문자열
    Number 숫자
    Date 날짜
    Buffer 파일을 담을수있는 버퍼, UTF-8 타입이 아닌 문자열을 저장 ex)0x65
    Boolean true/false
    ObjectId 객체아이디, 주로 다른 객체를 참조할때 넣음
    Array 배열 형태의 값

     

     

    Index 스키마에서는 Mongoose를 연결했다.

    /schemas/index.js
    
    const mongoose = require("mongoose");
    mongoose.set("strictQuery", false); 
    
    const connect = () => { 
        mongoose 
            .connect("mongodb://127.0.0.1:27017/post", {})
            .then(() => console.log("MongoDB 연결 완료"))
            .catch(err => console.log(err));
            };
    
    mongoose.connection.on("error", err => {
        console.error("MongoDB 연결 에러", err);
    });
    
    
    module.exports = connect;

     

    /schemas/post.js
    
    const mongoose = require("mongoose");
    
    const postSchema = new mongoose.Schema(
      {
        postId: {
          type: String,
          required: true, 
        },
        user: {
          type: String
        },
        title: {
          type:String  
        },
        password: {
          type:String    
        },
        content: {
          type:String 
        },
        createdAt: {
          type:String,
    
        },
      });
    
      // postSchema.set('timestamps', { createdAt: true, updatedAt: false});
    
      module.exports = mongoose.model("Posts", postSchema); // model

     

    /comment/post.js
    
    const mongoose = require("mongoose");
    
    const commentSchema = new mongoose.Schema(
      { 
        postId: {
          type: String,
          required: true, 
          // type: mongoose.Schema.Types.ObjectId,
          // ref: "Post",
       },
        commentId: {
          type: String,
          required: true, 
        },
        user: {
          type: String,
        },
        password: {
          type:String, 
        },
        content: {
          type:String,
        },
        createdAt: {
          type:String,
        }},
      );
    
    // commentSchema.set('timestamps', { createdAt: true, updatedAt: false});
    
    module.exports = mongoose.model("Comments", commentSchema);  //model에 담으면 양식이 정해짐(스키마로 이뤄진 양식)

     

    /routes/posts.js

    /routes/posts.js
    
    const express = require("express");
    const router = express.Router();
    const Post = require("../schemas/post.js");
    const { v4: uuidv4 } = require("uuid"); // posts에 자동생성되는 postsId
    
    
    
    // 1. 게시글 작성 POST API  (성공)
    router.post("/posts", async (req, res) => {
      const { title, user, password, content } = req.body;
      const createdAt = new Date().toLocaleString();
      const postId = uuidv4();
    
       if (!title.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "제목을 입력해주세요.",
        });
      } else if (!user.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "유저명을 입력해주세요.",
        });
      } else if (!password.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호를 입력해주세요.",
        });
      } else if (!content.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "게시글 내용을 입력해주세요.",
        });
      } else {
        await Post.create({
          postId: postId, // 여기서 모든 key값들은 db컬럼(Studio 3T와 일치해야 한다)
          title: title,
          user: user,
          content: content,
          password: password,
          createdAt: createdAt 
        });
        return res.status(201).json({ 
          success: true,
          message: "게시글이 작성되었습니다.", 
        });
      }
    });
    
    
    
    // 2. 전체 게시글 목록 조회 GET API  (성공)
    router.get("/posts", async (req, res) => {
      const allPosts = await Post.find()
        .select({ //1 = true, 0 = false
          postId: 1,
          user: 1,
          title: 1,
          content: 0,    //목록조회에서 게시글내용은 안보여도됨
          createdAt: 1,
          password: 0,
          _id: 0,
          __v: 0
        })
        .sort({ createdAt: -1 })
        .exec();
        return res.status(200).json({ allPosts: allPosts });
    });
    // const filteredPosts = allPosts.map( post => {
    //   const {password, ...rest} = post     // ...rest가 스키마정보 틀까지 다 가져옴. 이렇게쓰면 X
    //   return rest;
    // });
    
    
    
    // 3. 단일 게시글 조회 GET API (성공)
    router.get("/posts/:postId", async (req, res) => {
      const postId = req.params.postId;
      const data = await Post.findOne(
        {postId: postId}, 
        { //1 = true, 0 = false
          postId: 0,
          password: 0,
          _id: 0,
          __v: 0
        })
       .catch(console.error);
      if (!data) {
      return res.status(400).json({
         success: false,
         errorMessage: "존재하지 않는 게시물입니다." 
        });
      }
      return res.status(200).json({ singlePost: data });
      });
    
    
    
    // 4. 게시글 수정 PUT API (성공)
    router.put("/posts/:postId", async (req, res) => {
      const postId = req.params.postId;
      const newPw = req.body.password;
      const {user, title, content} = req.body;
    
      const data = await Post.find({ postId: postId }).catch(console.error);
      const existPw = data[0].password; // find함수로 찾은 data는 배열형식이므로 0번째 인덱스의 password를 가져와야함
      // console.log(data); 
      // console.log(existPw);
      // console.log(newPw);
    
      if (!postId) {
        return res.status(400).json({
          success: false,
          errorMessage: "데이터 형식이 올바르지 않습니다.",
        });
      } else if (!data) {
        return res.status(400).json({
          success: false,
          errorMessage: "게시글 조회에 실패하였습니다.",
        });
      } else if (!content.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "게시글 내용을 입력해주세요.",
        });    
      } else if (!newPw.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호를 입력해주세요.",
        });
      } else if (existPw !== newPw) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호가 일치하지 않습니다.",
        });    
      } else if (existPw === newPw) {
        await Post.updateOne(
          { postId: postId }, 
          { $set: { user: user, title: title, content: content} }
          )
      };
        return res.status(200).json({
          success: true,
          message: "게시글을 수정하였습니다.",
      });
    });
    
    
    
    // 5. 게시글 삭제 DELETE API  (성공)
    router.delete("/posts/:postId", async (req, res) => {
      const postId = req.params.postId;
      const newPw = req.body.password;
    
      const data = await Post.find({ postId: postId }).catch(console.error);
      const existPw = data[0].password; // find함수로 찾은 data는 배열형식이므로 0번째 인덱스의 password를 가져와야함
      // console.log(data);
      // console.log(existPw);
      // console.log(newPw);
    
      if (!postId) {
        return res.status(400).json({
          success: false,
          errorMessage: "데이터 형식이 올바르지 않습니다.",
        });
      } else if (!data) {
        return res.status(400).json({
          success: false,
          errorMessage: "게시글 조회에 실패하였습니다.",
        });
      } else if (!newPw.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호를 입력해주세요.",
        });
      } else if (existPw !== newPw) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호가 일치하지 않습니다.",
        });
      } else if (existPw === newPw) {
        await Post.deleteOne({ postId });
      }
        return res.status(200).json({
          success: true,
          message: "게시글을 삭제하였습니다.",
      });
    });
    
    module.exports = router;

     

    /routes/comments.js

    const express = require("express");
    const router = express.Router();
    const Comment = require("../schemas/comment.js");
    const Post = require("../schemas/post.js");
    const { v4: uuidv4 } = require("uuid"); // comments에 자동생성되는 commentId
    
    
    // 6. 댓글 목록 조회 GET   (성공)
    router.get("/comments/:postId", async (req, res) => {
      const { postId } = req.params;
      const comments = await Comment.find(
        {'postId': postId}, 
        { //1 = true, 0 = false
          postId: 0,
          password: 0,
          _id: 0,
          __v: 0
        })
        .sort({createdAt: -1})
        .catch(console.error); 
    
        if (!postId) {
          return res.status(400).json({
            success: false,
            errorMessage: "데이터 형식이 올바르지 않습니다."
          })
        } else {
           return res.status(200).json({
            "comments" : comments}
            )}
        });
    
    
    
    // 7. 댓글 작성 POST   (성공)
    router.post("/comments/:postId", async (req, res) => {
      const postId = req.params.postId;
      const { user, password, content } = req.body;
    
      const createdAt = new Date().toLocaleString();
      const commentId = uuidv4();
    
      if (!postId) {
        return res.status(400).json({
          success: false,
          errorMessage: "데이터 형식이 올바르지 않습니다."
        });
      } else if (!user) {
        return res.status(400).json({
          success: false,
          errorMessage: "유저명을 입력해주세요."
        });
      } else if (!password.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호를 입력해주세요."
        });
      } else if (!content.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "댓글 내용을 입력해주세요."
        });
      } else {
          await Comment.create({ 
          postId : postId,        //create에 위에서 변수선언한 postId를 넣음으로써 postId를 해당 댓글에 생성한다
          commentId : commentId,  // 여기서 모든 key값들은 db컬럼(Studio 3T와 일치해야 한다)
          user: user, 
          password: password,
          content: content,  
          createdAt: createdAt,
        }); 
    
        return res.status(201).json({ 
          success: true,
          message: "댓글이 작성되었습니다." 
      });
    }});
    
    
    
    // 8. 댓글 수정 PUT  (성공)
    router.put("/posts/:postId/comments/:commentId", async (req, res) => {
      const commentId = req.params.commentId;
      const postId = req.params.postId;
      const newPw = req.body.password;
      const { user, content } = req.body;
    
      const data = await Comment.find({commentId:commentId}).catch(console.error); 
      const existPw = data[0].password // find함수로 찾은 data는 배열형식이므로 0번째 인덱스의 password를 가져와야함
    
      if (!commentId || !postId) {
        return res.status(400).json({
          success: false,
          errorMessage: "데이터 형식이 올바르지 않습니다."
        });
      } else if (!data) {
        return res.status(400).json({
          success: false,
          errorMessage: "댓글 조회에 실패하였습니다."
        });
      } else if (!content.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "댓글 내용을 입력해주세요."
        });
      } else if (!newPw.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호를 입력해주세요."
        });
      } else if (existPw !== newPw) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호가 일치하지 않습니다."
        });
      } else if (existPw === newPw) {
        await Post.updateOne(
          { commentId: commentId }, 
          { $set: { user: user, content: content } }
          );
      };
        return res.status(200).json({ 
          success: true,
          message: "댓글을 수정하였습니다." 
      });
    });
    
    
    
    // 9. 댓글 삭제 DELETE  (성공)
    router.delete("/posts/:postId/comments/:commentId", async (req, res) => {
      const commentId = req.params.commentId;
      const postId = req.params.postId;
      const newPw = req.body.password;
     
      const data = await Comment.find({commentId: commentId}).catch(console.error);
      const existPw = data[0].password // find함수로 찾은 data는 배열형식이므로 0번째 인덱스의 password를 가져와야함
    
      if (!commentId || !postId) {
        return res.status(400).json({
          success: false,
          errorMessage: "데이터 형식이 올바르지 않습니다.",
        });
      } else if (!data) {
        return res.status(400).json({
          success: false,
          errorMessage: "댓글 조회에 실패하였습니다.",
        });
      } else if (!newPw.length) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호를 입력해주세요."
        });
      } else if (existPw !== newPw) {
        return res.status(400).json({
          success: false,
          errorMessage: "비밀번호가 일치하지 않습니다."
        });
      } else if (existPw === newPw) {
        await Post.deleteOne({commentId});
      };
        return res.status(200).json({ 
          success: true,
          message: "댓글을 삭제하였습니다." 
      });
    });
    
    module.exports = router;

    댓글

Designed by Tistory.