Back-end/Node.js

[Node.js+MongoDB] 몽구스 (2) (Mongoose)

poppy 2021. 7. 23. 11:01
반응형

https://soohyun6879.tistory.com/166

 

[Node.js+MongoDB] 몽구스 (1) (Mongoose)

몽구스란? (Mongoose) 몽구스는 MYSQL 의 시퀄라이즈와 같은 것입니다. 몽구스는 시퀄라이즈와 달리 ODM 이라고 불리는데 그 이유는 MongoDB는 릴레이션이 아니라 다큐먼트를 사용하기 때문입니다. Mongo

soohyun6879.tistory.com

이전 포스팅에서 몽고디비와 몽구스를 연결하는 것까지 완료했습니다! 이번 포스팅에서 스키마를 정의하고 쿼리를 수행하는 쿼리를 만들어 사용자 등록, 댓글 등록, 댓글 수정 등 기능들을 완성해보겠습니다

 

1. 스키마 정의하기

schemas 폴더에 users.js 와 comment.js 를 만듭니다. (MongoDB의 컬렉션 = Mongoose의 스키마) 

몽구스 모듈에서 Schema 생성자를 사용해 스키마를 만들고 필드를 정의합니다. 몽구스는 _id를 기본키로 자동 생성하므로 _id 필드는 따로 정의하지 않아도 됩니다! required 는 필수를 의미하고, unique는 유일함을 의미합니다. 

// schemas/user.js ( = users 컬렉션)
const mongoose = require('mongoose');

const { Schema } = mongoose;
const userSchema = new Schema({
  name: {
    type: String,
    required: true,
    unique: true,
  },
  age: {
    type: Number,
    required: true,
  },
  married: {
    type: Boolean,
    required: true,
  },
  comment: String,
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model('User', userSchema); // 몽고디비 컬렉션과 스키마 연결하는 모델 생성
// schemas/comment.js ( = comments 컬렉션)
const mongoose = require('mongoose');

const { Schema } = mongoose;
const { Types: { ObjectId } } = Schema;
const commentSchema = new Schema({
  commenter: {
    type: ObjectId,
    required: true,
    ref: 'User',
  },
  comment: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model('Comment', commentSchema); // 몽고디비 컬렉션과 스키마 연결하는 모델 생성

댓글 스키마의 commenter 속성의 자료형이 ObjectId 이고, 옵션으로 ref 속성의 값이 User로 주어져 있습니다. 이것은 commenter 필드에 User 스키마의 사용자 ObjectId가 들어간다는 의미입니다. 

 

2. 쿼리 수행하기

프론트엔드 코드는 파일로 첨부하겠습니다! 서버 코드가 중요한 것이므로 프론트엔드쪽 코드는 다루지 않겠습니다

views 폴더에 mongoose.html, error.html 을 생성하고, public 폴더에 mongoose.js 를 생성하면 됩니다.

mongoose.html
0.00MB
error.html
0.00MB
mongoose.js
0.00MB

 

서버쪽을 쿼리 수행 부분을 살펴보겠습니다. routes 폴더에 index.js, users.js, comments.js 파일을 만듭니다.

// routes/index.js
const express = require('express');
const User = require('../schemas/user');

const router = express.Router();

router.get('/', async (req, res, next) => {
  try {
    const users = await User.find({}); // 몽고디비의 db.users.find({}) 쿼리와 같음
    res.render('mongoose', { users });
  } catch (err) {
    console.error(err);
    next(err);
  }
});

module.exports = router;

이 라우터는 GET / 로 접속했을 때의 라우터입니다. User.find({}) 메서드로 모든 사용자를 조회한 뒤 mongoose.html을 렌더링할 때 users 변수로 넣습니다. 몽구스는 기본적으로 프로미스를 지원하므로 async/await 과 try/catch 를 사용하여 조회 성공 시와 실패 시의 정보를 얻을 수 있습니다. 

 

// routes/users.js
const express = require('express');
const User = require('../schemas/user');
const Comment = require('../schemas/comment');

const router = express.Router();

router.route('/')
  .get(async (req, res, next) => {
    try {
      const users = await User.find({});
      res.json(users);
    } catch (err) {
      console.error(err);
      next(err);
    }
  })
  .post(async (req, res, next) => {
    try {
      const user = await User.create({
        name: req.body.name,
        age: req.body.age,
        married: req.body.married,
      });
      console.log(user);
      res.status(201).json(user);
    } catch (err) {
      console.error(err);
      next(err);
    }
  });

router.get('/:id/comments', async (req, res, next) => {
  try {
    const comments = await Comment.find({ commenter: req.params.id })
      .populate('commenter');
    console.log(comments);
    res.json(comments);
  } catch (err) {
    console.error(err);
    next(err);
  }
});

module.exports = router;

먼저 GET /users 와 POST /users 로 접속했을 때 라우터입니다. GET 방식일 때는 사용자를 조회하는 요청을 수행하고, POST 방식일 때는 사용자를 등록하는 요청을 수행합니다. 사용자를 등록할 때는 create 메서드를 사용합니다. 정의한 스키마에 부합하지 않는 데이터를 넣으면 몽구스가 에러를 발생시킵니다. _id는 자동으로 생성됩니다.

GET /users/:id/comments 로 접속했을 때 라우터입니다. 사용자의 댓글을 조회하는 요청을 수행합니다. find 메서드에 옵션이 추가되어있습니다. 댓글을 쓴 사용자의 아이디로 댓글을 조회한 뒤 populate 메서드로 users 컬렉션에서 사용자 다큐먼트를 찾아 commenter 필드를 사용자 다큐먼트로 치환합니다. populate는 JOIN 기능을 합니다. 

 

:id 의 의미가 궁금하다면 다음 링크의 "라우트 매개변수" 부분을 참고해주세요!

https://soohyun6879.tistory.com/159

 

[Node.js] 라우터 분리하기

app.get 같은 메서드를 "라우터" 라고 합니다. app.js 에 라우터를 많이 연결하면 코드가 길어지고 복잡해지므로 라우터를 분리해보겠습니다! routes 폴더를 만들고 그 안에 라우터를 분리하여 저장하

soohyun6879.tistory.com

 

// routes/comments.js
const express = require('express');
const Comment = require('../schemas/comment');

const router = express.Router();

router.post('/', async (req, res, next) => {
  try {
    const comment = await Comment.create({
      commenter: req.body.id,
      comment: req.body.comment,
    });
    console.log(comment);
    const result = await Comment.populate(comment, { path: 'commenter' });
    res.status(201).json(result);
  } catch (err) {
    console.error(err);
    next(err);
  }
});

router.route('/:id')
  .patch(async (req, res, next) => {
    try {
      const result = await Comment.update({
        _id: req.params.id,
      }, {
        comment: req.body.comment,
      });
      res.json(result);
    } catch (err) {
      console.error(err);
      next(err);
    }
  })
  .delete(async (req, res, next) => {
    try {
      const result = await Comment.remove({ _id: req.params.id });
      res.json(result);
    } catch (err) {
      console.error(err);
      next(err);
    }
  });

module.exports = router;

이 라우터는 댓글 관련 CRUD 작업을 하는 라우터입니다. POST /comments 라우터는 댓글을 등록하는 라우터입니다. populate 메서드로 comment 객체에 다른 컬렉션 다큐먼트를 불러오는데 path 옵션으로 어떤 필드를 합칠지 설정하면 됩니다. PATCH /comments/:id 는 댓글을 수정하는 라우터입니다. update 메서드의 첫번째 인수는 어떤 다큐먼트를 수정할지를 나타낸 쿼리 객체이고, 두번째 인수는 수정할 필드와 값이 들어있는 객체입니다. DELETE /comments/:id 는 댓글을 삭제하는 라우터입니다.

 

마지막으로 app.js 에서 라우터를 가져와서 라우터를 연결해주면 됩니다!

// app.js
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const nunjucks = require('nunjucks');

const connect = require('./schemas');
// 라우터 가져오기
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');

const app = express();
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'html');
nunjucks.configure('views', {
  express: app,
  watch: true,
});
connect();

app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

// 라우터 연결하기
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comments', commentsRouter);

app.use((req, res, next) => {
  const error =  new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
  error.status = 404;
  next(error);
});

app.use((err, req, res, next) => {
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
  res.status(err.status || 500);
  res.render('error');
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

모든 코드가 완성된 후 npm start 를 하고 웹페이지에 접속하면 다음과 같은 화면을 볼 수 있습니다!

 

반응형