node.js

게시판 만들기

늘곰's 2023. 8. 28. 22:16

요구사항

  •  ✅ 서비스 완성
  • 1) 서비스 완성, 2) Directory Structure, 3) AWS 배포 세 가지를 모두 완수해야 합니다.</aside>
    1. 전체 게시글 목록 조회 API
      • 제목, 작성자명, 작성 날짜를 조회하기
      • 작성 날짜 기준으로 내림차순 정렬하기
    2. 게시글 작성 API
      • 제목, 작성자명, 비밀번호, 작성 내용을 입력하기
    3. 게시글 조회 API
      • 제목, 작성자명, 작성 날짜, 작성 내용을 조회하기 (검색 기능이 아닙니다. 간단한 게시글 조회만 구현해주세요.)
    4. 게시글 수정 API
      • API를 호출할 때 입력된 비밀번호를 비교하여 동일할 때만 글이 수정되게 하기
    5. 게시글 삭제 API
      • API를 호출할 때 입력된 비밀번호를 비교하여 동일할 때만 글이 삭제되게 하기
    6. 댓글 목록 조회
      • 조회하는 게시글에 작성된 모든 댓글을 목록 형식으로 볼 수 있도록 하기
      • 작성 날짜 기준으로 내림차순 정렬하기
    7. 댓글 작성
      • 댓글 내용을 비워둔 채 댓글 작성 API를 호출하면 "댓글 내용을 입력해주세요" 라는 메세지를 return하기
      • 댓글 내용을 입력하고 댓글 작성 API를 호출한 경우 작성한 댓글을 추가하기
    8. 댓글 수정
      • 댓글 내용을 비워둔 채 댓글 수정 API를 호출하면 "댓글 내용을 입력해주세요" 라는 메세지를 return하기
      • 댓글 내용을 입력하고 댓글 수정 API를 호출한 경우 작성한 댓글을 수정하기
    9. 댓글 삭제
      • 원하는 댓글을 삭제하기

Directory Structure

.
├── app.js
├── routes
│   ├── index.js
│   ├── comments.js
│   └── posts.js
└── schemas
    ├── index.js
    ├── comment.js
    └── post.js

AWS 배포

EC2 배포

  • Ubuntu EC2 를 구매한 뒤, Node.js의 포트(3000)를 80번 포트로 포워딩해서 포트 번호 없이도 서비스에 접속 가능하도록 하기
  • →  iptable을 사용하기
  • mongoDB를 EC2 내부에 설치해서 연결하기

app.js 기본 템플릿 

import express from 'express';

const app = express();
const PORT = 3000;

// Express에서 req.body에 접근하여 body 데이터를 사용할 수 있도록 설정합니다.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const router = express.Router();

router.get('/', (req, res) => {
  return res.json({ message: 'Hi!' });
});

app.use('/api', router);

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

스키마 설계하기 schema/index.js

// schemas/index.js

import mongoose from 'mongoose';

const connect = () => {
  mongoose
    .connect(
      // 빨간색으로 표시된 부분은 대여한 ID, Password, 주소에 맞게끔 수정해주세요!
      '몽고디비 URL 입력 ,
      {
        dbName: 'notice_board', // todo_memo 데이터베이스명을 사용합니다.
      },
    )
    .then(() => console.log('MongoDB 연결에 성공하였습니다.'))
    .catch((err) => console.log(`MongoDB 연결에 실패하였습니다. ${err}`));
};

mongoose.connection.on('error', (err) => {
  console.error('MongoDB 연결 에러', err);
});

export default connect;

app.js  파일에서 schema/index.js 가져오기

추가한 내용 

import connect from './schmas/index.js'
 
connect();
import express from 'express';
import connect from './schmas/index.js'

const app = express();
const PORT = 3000;

connect();

// Express에서 req.body에 접근하여 body 데이터를 사용할 수 있도록 설정합니다.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const router = express.Router();

router.get('/', (req, res) => {
  return res.json({ message: 'Hi!' });
});

app.use('/api', router);

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

schema/post.js  만들기

// schemas/post.schema.js

import mongoose from "mongoose";

const postSchema = new mongoose.Schema({
  user: {
    type: String,
    required: true, // 필수요소
  },
  password: {
    type: String,
    required: true, // 필수요소
  },
  title: {
    type : String,
    required : true, // 필수요소
  },
  content: {
    type : String,
    required : true, // 필수요소
  },
  createAt: {
    type: Date, // createAt 필드는 Date 타입을 가집니다.
    required: true, 
  },
});

/* 
// 프론트엔드 서빙을 위한 코드입니다. 모르셔도 괜찮아요!
postSchema.virtual("postId").get(function () {
  return this._id.toHexString();
});
postSchema.set("toJSON", {
  virtuals: true,
});
 */


// postSchema를 바탕으로 post모델을 생성하여, 외부로 내보냅니다.
export default mongoose.model("Schemapost", postSchema);

/routes/post.js 기본 템플릿 

// /routes/post.router.js

import express from 'express';

const router = express.Router();


export default router;

app.js 파일에 라우터를 연결

import postRouter from './routes/post.router.js'
 
app.use('/api', [router, postRouter]);

 

import express from 'express';
import connect from './schmas/index.js'
import postRouter from './routes/post.router.js'
const app = express();
const PORT = 3000;

connect();

// Express에서 req.body에 접근하여 body 데이터를 사용할 수 있도록 설정합니다.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const router = express.Router();

router.get('/', (req, res) => {
  return res.json({ message: 'Hi!' });
});

app.use('/api', [router, postRouter]);

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

게시판 등록

/* 게시판 등록  API*/

router.post('/posts', async(req,res, next) =>{
    // 1. 클라이언트로 받아온 데이터를 가져온다
    const {title, user, password , content } = req.body
    //1.5 클라이언트가 유저 데이터를 전달하지 않았을때 , 클라아인트에게 에러를 반환
    if(!user || !password || !title || !content){
        return res.status(400).json({ Message : '데이터 형식이 올바르지 않습니다.'})
    }
    
    // newPost 라는 새로운 Schemapost 를 만든다. / post 등록
    const newPost = new Schemapost({
        user,
        password,
        title,
        content,
        createdAt : new Date()

    })
    // newPost 를 저장한다.
    await newPost.save();
    // 클라이언트에게 반환한다.
    res.status(201).json({message:'개시글을 생성하였습니다'})
    //res.status(201).json(newPost);
    
})

게시판 조회

게시판 을 조회하면 _id 값과  postId 값 _v 값등 안보여야 할 내용들이 보여서 

map 을 이용해서 보여줘야할 내용만 보여주도록 수정 


/* 게시판 목록 조회 */
router.get('/posts',  async(req, res, next) => {
    // 게시판 목록 조회 진행
    // 게시판 목록에서 createdAt 으로 내림차순해서 정렬한다.
        //                  mongoose 모델   목록조회 .   createAt 내림차순
    const cheakpost = await Schemapost.find().sort({createdAt : -1}).exec();
                                                    //'-createAt'
    // 2 해야할 일 목록 조회 결과를 클라이언트에게 반환한다.
    
    // 맵 함수로 postId 로 id 값을 이름바꾸고 보여줘야 할 값만 보여줌
    const mapCheakpost = cheakpost.map(post =>({
        postId :post._id,  // _id 를 postId로 변경
        user: post.user,
        title : post.title,
        createdAt: post.createdAt
    }));


    return res.status(200).json({ mapCheakpost });
    
    // 오류 수정
    // 맵을 돌려서 가공해서 만듬
    // 맵으로 _id 를 postId 로 바꿔서 출력
    //var widgetSchema = new Schema({ ... attributes ... }, { versionKey: false }); 
    // 언더바 v값을 안보여준다.
	
  });

 

게시판 상세 조회

게시판 상세조회에서도 마찬가지로 map 함수를 사용해서 수정하려고 했지만. 상세조회는 가지고있는 오브젝트가 하나라서 맵 함수가 작동을 안했다.. 그래서 map 함수를 빼고 그냥 새로운 변수를 만들어 넣어

/* 게시글 상세 조회 */
router.get('/posts/:postId', async(req, res, next) => {
    // post 아이디를 찾는다
    const {postId} = req.params;
    // findById 로 포스트아이디를 가지고 와서 조회
    const cheak1post = await Schemapost.findById(postId).exec();

    if(!cheak1post){
        return res.status(400).json({Massage : '데이터 형식이 올바르지 않습니다.'})
    }

    const mapCheak1post = {
        postId :cheak1post._id,
        user: cheak1post.user,
        content : cheak1post.content,
        createdAt: cheak1post.createdAt
    }

    return res.status(201).json({mapCheak1post});
})

 

게시판 수정

/* 게시글 수정 */
router.put('/posts/:postId', async(req, res, next) => {
    const {postId} = req.params;
    const {password , title, content} = req.body;

    // 변수 post = 게시글을 조회해 postId 인 값을 찾음
    const post = await Schemapost.findById(postId);

    /* 에러 */
    // postId 값이 없으면 게시글 조회 실패
    if(!post) {
        return res.status(404).json({message : '게시글 조회에 실패하였습니다.'})
    }
    
    // 비밀번호가 틀리면 데이터 형식이 올바르지 않습니다.
    if(post.password !== password){
        return res.status(400).json({message : '데이터 형식이 올바르지 않습니다.'})
    }


    // 변수 post 에 저장된 postId 게시물 의 제목과 컨텐츠
    post.title  = title;

    post.content = content;

    // 수정 내용 저장
    await post.save();
    
    // 수정이 성공 했을 때 메세지
    return res.status(200).json({massege: '게시글이 수정되었습니다.'})


});

게시글 삭제

/* 게시글 삭제 */
router.delete('/posts/:postId', async(req,res,next)=>{
    const {postId} = req.params;
    const {password} = req.body;
    
    // 개시글을 조회해서 패스워드 값을 찾음
    const  deletepost = await Schemapost.findById(postId).exec();
    
    /* 에러 */
    
    if(!deletepost){
        return res.status(404).json({message : '게시글 조회에 실패하였습니다.'})
    }
    
    if(deletepost.password !== password){
        return res.status(400).json({message : '데이터 형식이 올바르지 않습니다.'})
    }
    
    //실제 삭제
    await deletepost.deleteOne({password: postId});
    
    // 삭제되고 난후 서버에 메세지 출력
    return res.status(200).json({message : '게시글을 삭제하였습니다.'});


});

마지막으로 

 

댓글을 만드는 것까지 있지만 아직 작성이 안되어 내일 작성 후 최종 으로 완성된 게시판 + 댓글 작성 및 수정 삭제 를 올릴 예정