쿼리 수행
이전 포스트에서 작성한 코드들로 사용자 정보를 등록하고, 사용자가 등록한 댓글을 가져오는 서버를 만들어 보자.
우선 view부터 작성하자.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>시퀄라이즈 서버</title>
<style>
table { border: 1px solid black; border-collapse: collapse;}
table th, table td { border: 1px solid black;}
</style>
</head>
<body>
<div>
<form id="user-form">
<fieldset>
<legend>사용자 등록</legend>
<div><input id="username" type="text" placeholder="이름"></div>
<div><input id="age" type="number" placeholder="나이"></div>
<div><input id="married" type="checkbox"><label for="married">결혼여부</label></div>
<button type="submit">등록</button>
</fieldset>
</form>
</div>
<br>
<table id="user-list">
<thead>
<tr>
<th>아이디</th>
<th>이름</th>
<th>나이</th>
<th>결혼여부</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>{{'기혼' if user.married else '미혼'}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<div>
<form id="comment-form">
<fieldset>
<legend>댓글 등록</legend>
<div><input id="userid" type="text" placeholder="사용자 아이디"></div>
<div><input id="comment" type="text" placeholder="댓글"></div>
<button type="submit">등록</button>
</fieldset>
</form>
</div>
<br>
<table id="comment-list">
<thead>
<tr>
<th>아이디</th>
<th>작성자</th>
<th>댓글</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="/sequelize.js"></script>
</body>
</html>
views/sequelize.html
<h1>{{ message }}</h1>
<h2>{{ error.status }}</h2>
<pre>{{ error.stack }}</pre>
views/error.html
이제 sequelizejs 파일도 만들자.
//사용자 이름을 눌렀을 때 댓글 로딩
document.querySelectorAll('#user-list tr').forEach((el) => {
el.addEventListener('click', function() {
const id = el.querySelector('td').textContent;
console.log(id);
getComment(id);
});
});
//사용자 로딩
async function getUser(){
try{
const res = await axios.get('/users');
const users = res.data;
console.log(users);
const tbody = document.querySelector('#user-list tbody');
tbody.innerHTML = '';
users.map(function (user){
const row = document.createElement('tr');
row.addEventListener('click', () => {
getComment(user.id);
});
//로우 셀 추가
let 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);
});
}catch (err){
console.error(err);
}
}
//댓글 로딩
async function getComment(id){
try{
const res = await axios.get(`/users/${id}/comments`);
const comments = res.data;
const tbody = document.querySelector('#comment-list tbody');
tbody.innerHTML = '';
comments.map(function (comment){
const row = document.createElement('tr');
let td = document.createElement('td');
td.textContent = comment.id;
row.appendChild(td);
td = document.createElement('td');
td.textContent = comment.User.name;
row.appendChild(td);
td = document.createElement('td');
td.textContent = comment.comment;
row.appendChild(td);
const edit = document.createElement('button');
edit.textContent = '수정';
edit.addEventListener('click', async () => { //수정 클릭 시
const newComment = prompt('바꿀 내용을 입력하세요');
if(!newComment){
return alert('내용을 입력하세요');
}
try{
await axios.patch(`/comments/${comment.id}`, { comment: newComment });
getComment(id);
}catch(err){
console.error(err);
}
});
const remove = document.createElement('button');
remove.textContent = '삭제';
remove.addEventListener('click', async () => { //삭제 클릭 시
try{
await axios.delete(`/cmments/${comment.id}`);
getComment(id);
}catch(err){
console.error(err);
}
});
//버튼 추가
td = document.createElement('td');
td.appendChild(edit);
row.appendChild(td);
td = document.createElement('td');
td.appendChild(remove);
row.appendChild(td);
tbody.appendChild(row);
});
}catch (err){
console.error(err);
}
}
//사용자 등록 시
document.getElementById('user-form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = e.target.username.value;
const age = e.target.age.value;
const married = e.target.married.value;
if(!name){
return alert('이름을 입력하세요');
}
if(!age){
return alert('나이를 입력하세요');
}
try{
await axios.post('/users', {name, age, married});
getUser();
}catch(err){
console.error(err);
}
e.target.username.value = '';
e.target.age.value = '';
e.target.married.value = false;
});
//댓글 등록시
document.getElementById('comment-form').addEventListener('submit', async (e) => {
e.preventDefault();
const id = e.target.userid.value;
const comment = e.target.comment.value;
if(!id){
return alert('아이디를 입력하세요');
}
if(!comment){
return alert('댓글을 입력하세요');
}
try{
await axios.post('/comments', { id, comment });
getComment(id);
}catch(err){
console.error(err);
}
e.target.userid.value = '';
e.target.comment.value = '';
});
public/sequelizejs
이제 라우터를 만들고 연결하면 된다.
미리 app에 연결해두자.
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comments', commentsRouter);
const express = require('express');
const User = require('../models/user');
const router = express.Router();
router.get('/', async (req, res, next) => {
try{
const users = await User.findAll();
res.render('sequelize', { users});
}catch(err){
console.error(err);
next(err);
}
});
module.exports = router;
routes/index.js
GET /으로 접속했을 때 라우터이다. User.findAll 메서드로 모든 사용자를 찾은 후 sequelize.html을 렌더링 할 때 결괏값인 users를 넣는다.
시퀄라이즈는 프로미스를 기본적으로 지원하므로 async/await과 try/catch문을 사용해서 각각 조회 성공 시와 실패 시의 정보를 얻을 수 있다.
const express = require('express');
const User = require('../models/user');
const Comment = require('../models/comment');
const router = express.Router();
router.route('/')
.get(async (req, res, next) => {
try {
const user = await User.findAll();
res.json(user);
}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.log(err);
next(err);
}
});
router.get('/:id/comments', async (req, res, next) => {
try{
const comments = await Comment.findAll({
include: {
model: User,
where: {id: req.params.id},
},
});
console.log(comments);
res.json(comments);
}catch(err){
console.error(err);
}
});
module.exports = router;
routes/users.js
GET /users와 POST /users 주소로 요청이 들어올 때의 라우터이다. 각각 사용자를 조회하는 요청과 사용자를 등록하는 요청을 처리한다.
GET /users/:id/comments 라우터에는 findAll 메서드에 옵션을 추가했다. include 옵션으로 User model을 where 속성에는 :id로 받은 아이디 값을 넣어서 데이터베이스에서 찾는다.
const express = require('express');
const { User, Comment} = require('../models');;
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);
res.status(201).json(comment);
}catch(err){
console.error(err);
next(err);
}
});
router.route('/:id')
.patch(async (req, res, next) => {
try{
const result = await Comment.update({
comment: req.body.comment,
}, {
where: {id: req.params.id},
});
res.json(result);
}catch(err){
console.error(err);
next(err);
}
})
.delete(async(req, res, next) => {
try{
const result = await Comment.destroy({ where: { id: req.params.id}});
res.json(result);
}catch(err){
console.error(err);
next(err);
}
});
module.exports = router;
routes/comments.js
댓글과 관련된 CRUD 작업을 하는 라우터이다. POST /comments, PATCH/comments/:id, DELETE/comments/:id를 등록했다.
이제 서버를 실행해서 테스트해보자.
사용자의 이름을 누르면 댓글이 나온다.
사용자와 댓글을 추가해보자.