'Node.js 교과서 - 조현영 저'를 활용해 공부했습니다.
[Node.js] Express+MongoDB, API 서버 구현하기(1)와 이어집니다.
1. API 구현하기
1) Users
유형 | 라우트 | 설명 |
GET | /users | 전체 사용자 정보 조회 |
POST | /users | 사용자 추가 |
/routes/users.js
var express = require('express');
var User = require('../schemas/user');
var router = express.Router();
/* GET users listing. */
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){
// 요청 body에 전송된 정보를 기준으로 user객체 데이터 생성
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;
- User.find({}) : User테이블 전체 조회
- user.save() : 생성한 user데이터 추가
2) Comments
유형 | 라우트 | 설명 |
GET | /comments/:id | id 사용자의 댓글 목록 조회 |
POST | /comments | 댓글 추가 |
PATCH | /comments/:id | ObjectId가 일치하는 댓글 수정 |
DELETE | /comments/:id | ObjectId가 일치하는 댓글 삭제 |
/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;
- populate(): comment다큐먼트에서 user다큐먼트의 ObjectId를 참조하고 있는데 이를 실제 객체로 치환해 조회하게 해줍니다.
3) routes/index.js
기본으로 생성되는 index.js에서는 기본 index 뷰를 랜더링하도록 구현되어 있습니다.
우리가 원하는 페이지를 랜더링하고 사용자에 대한 정보도 같이 전달해 랜더링하도록 고쳐봅니다.
var express = require('express');
var User = require('../schemas/user');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
User.find({})
.then((users)=>{
res.render('mongoose',{ users });
})
.catch((err)=>{
console.error(err);
next(err);
});
});
module.exports = router;
mongoose라는 뷰를 만들어 줄 겁니다.
2. View 만들기
위와 같이 전체 사용자들을 조회, 등록할 수 있고, 사용자를 누르면 해당 사용자의 댓글 목록을 볼 수 있는 뷰를 만듭니다.
/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 디렉토리에 담기게 됩니다.
위의 페이지에서 동적으로 동작하는 부분들을 /public/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 = '';
});
테이블의 사용자가 눌렸을 때 해당 사용자의 comments 정보를 로드합니다.
해당 코멘트 정보를 담음 table 요소를 추가할 때 수정, 삭제 버튼이 담기게 됩니다.
각 버튼들에 수정, 삭제 기능을 수행하기 위핸 이벤트리스너를 연결해둡니다.
이와 별개로 사용자 등록, 댓글 등록 기능을 구현합니다.
http request를 위해 XMLHttpRequest를 활용합니다.
3. 결과 확인하기
간단하게 Express와 MongoDB를 연동한 웹을 구현해 보았습니다.
해당 프로젝트 루트 디렉토리에서 서버를 구동해봅니다.
$ npm start
서버가 정상적으로 동작하면 http://localhost:3000에 접속해 결과를 확인해 볼 수 있습니다.
참조