[Node.js] Mongoose 사용하기
MySQL에 시퀄라이즈가 있다면 몽고디비에는 몽구스가 있습니다.
몽구스에는 스키마가 있습니다. 몽고디비는 테이블이 없어서 자유롭게 데이터를 넣을 수 있지만, 때로는 이런 점이 단점으로 작용할 수 있습니다. 몽구스는 몽고디비에 데이터를 넣기 전 노드 서버 단에서 데이터를 한 번 필터링하는 역할을 해줍니다.
또한, MySQL에 있는 JSON 기능을 populate라는 메서드로 어느 정도 보완해줍니다. 따라서 관계가 있는 데이터를 쉽게 가져올 수 있습니다.
먼저 express로 learn-mongoose 폴더를 생성한 후 mongoose를 설치합니다.
express learn-mongoose 명령 폴더 이동 npm i mongoose 명령
몽고디비는 주소를 사용해 연결합니다. 주소 형식은 mongodb://[username:password@]host[:port][/[database][?options]] 와 같습니다. [] 부분은 있어도 되고 없어도 됨을 의미합니다.
먼저 schemas 폴더를 루트 디렉터리에 생성합니다. 그리고 index.js 파일을 생성합니다.
// schemas/index.js const mongoose = require('mongoose'); module.exports = () =>{ const connect = () =>{ if(process.env.NODE_ENV !== 'production'){ mongoose.set('debug',true); } mongoose.connect('mongodb://root:1234@localhost:27017/admin',{ // 접속을 시도하는 주소의 데이터베이스는 admin이지만, 실제 사용할 데이터베이스는 nodejs이다. dbName: 'nodejs', },(error)=>{ if(error){ console.log('몽고디비 연결 에러', error); }else{ console.log('몽고디비 연결 성공'); } }); }; connect(); mongoose.connection.on('error',(error)=>{ console.error('몽고디비 연결 에러', error); }); mongoose.connection.on('disconnection',()=>{ console.error('몽고디비 연결이 끊겼습니다. 연결을 재시도합니다.'); connect(); }); // 스키마 연결 부분 require('./user'); require('./comment'); };
이제 app.js와 연결하여 노드 실행 시 mongoose.connect 부분도 실행되도록 하겠습니다.
// app.js // ... var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var connect = require('./schemas'); var app = express(); connect(); // ...
시퀄라이즈에서 테이블을 만들었던 것처럼 몽구스 스키마(schema)를 만들어봅시다.
// schemas/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);
// schemas/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)
몽구스를 사용해 쿼리 수행을 위한 view 파일과 script파일을 생성합니다.
// views/mongoose.pug doctype html html head meta(charset='utf-8') title 몽구스 서버 style. table{ border: 1px solid black; border-collapse: collapse; } table th, table td { border: 1px solid black; } body div form#user-form fieldset legend 사용자 등록 div input#username(type="text" placeholder="이름") div input#age(type="number" placeholder="나이") div input#married(type="checkbox") label(for="married") 결혼 여부 button(type="submit") 등록 br table#user-list thead tr th 아이디 th 이름 th 나이 th 결혼여부 tbody for user in users tr td= user.id td= user.name td= user.age td= user.married ? '기혼' : '미혼' br div form#comment-form fieldset legend 댓글 등록 div input#userid(type="text" placeholder="사용자 아이디") div input#comment(type="text" placeholder="댓글") button(type="submit") 등록 br table#comment-list thead tr th 아이디 th 작성자 th 댓글 th 수정 th 삭제 tbody script(src='/mongoose.js')
// public/mongoose.js // 사용자 이름 눌렀을 때 댓글 로딩 document.querySelectorAll('#user-list tr').forEach(function (el) { el.addEventListener('click', function () { var id = el.querySelector('td').textContent; getComment(id); }); }); // 사용자 로딩 function getUser() { var xhr = new XMLHttpRequest(); xhr.onload = function () { if (xhr.status === 200) { var users = JSON.parse(xhr.responseText); console.log(users); var tbody = document.querySelector('#user-list tbody'); tbody.innerHTML = ''; users.map(function (user) { var row = document.createElement('tr'); row.addEventListener('click', function () { getComment(user._id); }); var td = document.createElement('td'); td.textContent = user._id; row.appendChild(td); td = document.createElement('td'); td.textContent = user.name; row.appendChild(td); td = document.createElement('td'); td.textContent = user.age; row.appendChild(td); td = document.createElement('td'); td.textContent = user.married ? '기혼' : '미혼'; row.appendChild(td); tbody.appendChild(row); }); } else { console.error(xhr.responseText); } }; xhr.open('GET', '/users'); xhr.send(); } // 댓글 로딩 function getComment(id) { var xhr = new XMLHttpRequest(); xhr.onload = function () { if (xhr.status === 200) { var comments = JSON.parse(xhr.responseText); var tbody = document.querySelector('#comment-list tbody'); tbody.innerHTML = ''; comments.map(function (comment) { var row = document.createElement('tr'); var td = document.createElement('td'); td.textContent = comment._id; row.appendChild(td); td = document.createElement('td'); td.textContent = comment.commenter.name; row.appendChild(td); td = document.createElement('td'); td.textContent = comment.comment; row.appendChild(td); var edit = document.createElement('button'); edit.textContent = '수정'; edit.addEventListener('click', function () { // 수정 클릭 시 var newComment = prompt('바꿀 내용을 입력하세요'); if (!newComment) { return alert('내용을 반드시 입력하셔야 합니다'); } var xhr = new XMLHttpRequest(); xhr.onload = function () { if (xhr.status === 200) { console.log(xhr.responseText); getComment(id); } else { console.error(xhr.responseText); } }; xhr.open('PATCH', '/comments/' + comment._id); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify({ comment: newComment })); }); var remove = document.createElement('button'); remove.textContent = '삭제'; remove.addEventListener('click', function () { // 삭제 클릭 시 var xhr = new XMLHttpRequest(); xhr.onload = function () { if (xhr.status === 200) { console.log(xhr.responseText); getComment(id); } else { console.error(xhr.responseText); } }; xhr.open('DELETE', '/comments/' + comment._id); xhr.send(); }); td = document.createElement('td'); td.appendChild(edit); row.appendChild(td); td = document.createElement('td'); td.appendChild(remove); row.appendChild(td); tbody.appendChild(row); }); } else { console.error(xhr.responseText); } }; xhr.open('GET', '/comments/' + id); xhr.send(); } // 사용자 등록 시 document.getElementById('user-form').addEventListener('submit', function (e) { e.preventDefault(); var name = e.target.username.value; var age = e.target.age.value; var married = e.target.married.checked; if (!name) { return alert('이름을 입력하세요'); } if (!age) { return alert('나이를 입력하세요'); } var xhr = new XMLHttpRequest(); xhr.onload = function () { if (xhr.status === 201) { console.log(xhr.responseText); getUser(); } else { console.error(xhr.responseText); } }; xhr.open('POST', '/users'); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify({ name: name, age: age, married: married })); e.target.username.value = ''; e.target.age.value = ''; e.target.married.checked = false; }); // 댓글 등록 시 document.getElementById('comment-form').addEventListener('submit', function (e) { e.preventDefault(); var id = e.target.userid.value; var comment = e.target.comment.value; if (!id) { return alert('아이디를 입력하세요'); } if (!comment) { return alert('댓글을 입력하세요'); } var xhr = new XMLHttpRequest(); xhr.onload = function () { if (xhr.status === 201) { console.log(xhr.responseText); getComment(id); } else { console.error(xhr.responseText); } }; xhr.open('POST', '/comments'); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify({ id: id, comment: comment })); e.target.userid.value = ''; e.target.comment.value = ''; });
앞서 sequelize를 사용했을때와 비슷한 형태의 예제입니다.
이제 라우터를 작성해보도록 하겠습니다.
// routes/index.js var express = require('express'); var User = require('../schemas/user'); var router = express.Router(); router.get('/',function(req,res,next){ User.find({}) .then((users)=>{ res.render('mongoose', { users }); }) .catch((err)=>{ console.error(err); next(err); }); }); module.exports = router;
// routes/users.js var express = require('express'); var User = require('../schemas/user') var router = express.Router(); router.get('/',function(req,res,next){ User.find({}) .then((users)=>{ res.json(users); }) .catch((err)=>{ console.error(err); next(err); }); }); router.post('/',function(req,res,next){ const user = new User({ name: req.body.name, age: req.body.age, married: req.body.married, }); user.save() .then((result)=>{ console.log(result); res.status(201).json(result); }) .catch((err)=>{ console.error(err); next(err); }); }); module.exports = router;
// routes/comments.js var express = require('express'); var Comment = require('../schemas/comment') var router = express.Router(); router.get("/:id",function(req,res,next){ Comment.find({commenter: req.params.id }).populate('commenter') .then((comments)=>{ console.log(comments); res.json(comments); }) .catch((err)=>{ console.error(err); next(err); }); }); router.post('/',function(req,res,next){ const comment = new Comment({ commenter: req.body.id, comment: req.body.comment, }); comment.save() .then((result)=>{ return Comment.populate(result,{path: 'commenter'}); }) .then((result)=>{ res.status(201).json(result); }) .catch((err)=>{ console.error(err); next(err); }); }); router.patch('/:id', function(req,res,next){ Comment.update({_id: req.params.id}, { comment: req.body.comment}) .then((result)=>{ res.json(result); }) .catch((err)=>{ console.error(err); next(err); }); }); router.delete('/:id', function(req,res,next){ Comment.remove({_id: req.params.id }) .then((result)=>{ res.json(result); }) .catch((err)=>{ console.error(err); next(err); }); }); module.exports = router;
마지막으로 라우터를 서버에 연결하기 위해 app.js 파일을 수정합니다.
// app.js var createError = require('http-errors'); var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var commentsRouter = require('./routes/comments'); var connect = require('./schemas'); var app = express(); connect(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); app.use(logger('dev')); app.use(express.static(path.join(__dirname, 'public'))); // 위로 이동 완료 app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/comments', commentsRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { next(createError(404)); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
서버를 실행하기 전에 몽고디비 서버를 먼저 실행(mongod -- auth)해야 합니다. 서버가 실행되면 npm start로 웹 서버를 실행시킵니다.
서버 실행 화면
출처 : Node.js 교과서
from http://badaturtle93.tistory.com/42 by ccl(A) rewrite - 2020-03-13 17:20:05
댓글
댓글 쓰기