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