Node/시퀄라이즈(MySQL), 몽구스(MongoDB)

몽구스 ODM

webmaster 2022. 9. 24. 17:13
728x90

몽구스 : 몽고 디비 작업을 쉽게 할 수 있도록 도와주는 라이브러리

  • ODM: Object Document Mapping: 객체와 다큐먼트를 매핑(1대 1 짝지음)
  • 몽고 디비에 없어 불편한 기능들을 몽구스가 보완했다.
  • 테이블과 유사한 기능, JOIN 기능 추가했다.
  • mongoDB 드라이버는 몽구스 내에 내장되어 있어 다운로드할 필요가 없다

몽고 디비 연결하기(schemas/index.js)

const mongoose = require('mongoose');

const connect = () => {
    if (process.env.NODE_ENV !== 'production') { //production 일때
        mongoose.set('debug', true); //debug 모드를 true로 한다
    }
    //요청을 보내면서 id:password를 보내줄 수도 있다.
    mongoose.connect('mongodb://root:nodejsbook@localhost:27017/admin', {
        dbName: 'nodejs', //db 명
        useNewUrlParser: true,
        useCreateIndex: true,
    }, (error) => {
        if (error) {
            console.log('몽고디비 연결 에러', error);
        } else {
            console.log('몽고디비 연결 성공');
        }
    });
};

mongoose.connection.on('error', (error) => {
    console.error('몽고디비 연결 에러', error);
});
mongoose.connection.on('disconnected', () => {
    console.error('몽고디비 연결이 끊겼습니다. 연결을 재시도합니다.');
    connect();
});

module.exports = connect;
  • 인증은 admin 데이터베이스에서, 서비스는 dbName 데이터베이스에서
  • connect라는 변수에 mongoDB 연결을 정의한다(커낵션 정보)
    • connect 할 때, id:password를 보내줄 수 있다.

앱과 연결(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 || 3002);
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'), '번 포트에서 대기 중');
});
  • schemas 폴더에서 connection 정보를 읽어와 앱과 연결한다.
  • schemas/index.js의 함수가 실행된다
  • mongoose.connect 함수가 몽고 디비에 연결을 시도한다.
  • mongoose.set은 디버깅 모드(모드를 켰을 때 콘솔에 쿼리가 찍힌다)
  • 연결이 끊기면(disconnection) 다시 연결을 시도한다.

스키마 정의하기

mongoDB를 쓰면, 테이블 정의가 필요 없지만, 몽구스를 사용하면 다시 제약해야 한다

user.js

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);

comment.js

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);
  • schemas 폴더 안에 작성한다
    • type 자료형, require 필수 여부 default 기본값, unique 고유 여부
    • _id는생략이 가능하다(기본적으로 들어있다고 생각하기에 생략한다)
    • MySQL의 테이블처럼 정해진 데이터만 들어갈 수 있게 강제한다
  • 연관관계 같은 경우, 몽구스가 ref를 사용해서 연관관계를 가지도록 도와준다.
    • 몽고 디비에서는 내장 객체로, 연관관계가 있는 데이터를 가지고 있었지만 연관 관계있는 데이터가 수정되면, 직접 내장 객체도 수정해 주어야 하는 단점이 있다.(단, 조인 비용이 안 드는 장점이 있다)
    • 몽구스는 이런 문제로 인해 ref를 통해 SQL 형식을 따르도록 하였다.

라우터

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({});
    res.render('mongoose', { users });
  } catch (err) {
    console.error(err);
    next(err);
  }
});

module.exports = router;

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;

commments.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;
  • populator를 하게 되면, 몽구스가 object_id를 가지고 있던 것을 연관관계가 있는 데이터로 변경해 준다.
    • await Comment.populate(comment, { path: 'commenter' }); -> commenter로 populate
    • await Comment.find({ commenter: req.params.id }).populate('commenter'); -> 찾은 뒤, populate
  • 몽구스는 update시 set을 안붙여도 된다 (몽구스에서 안전장치로 해주었다)
728x90

'Node > 시퀄라이즈(MySQL), 몽구스(MongoDB)' 카테고리의 다른 글

관계 쿼리  (0) 2022.09.18
쿼리 알아보기  (0) 2022.09.18
관계 정의하기  (0) 2022.09.18
모델 만들기  (0) 2022.09.18
시퀄라이즈  (0) 2022.09.18