시퀼라이즈
로 CRUD 작업을 하려면, 먼저 시퀼라이즈
쿼리를 알아야 한다. 쿼리는 프로미스를 반환하므로 then을 붙여 결괏값을 받을 수 있다.async/await 문법도 같이 사용할 수 있다.
INSERT INTO nodejs.users (name, age, married, comment) VALUES ('zero',24,0,'자기소개');
다음과 같은 SQL문을 시퀼라이즈 쿼리로 변환하면,
const {User} = require('../models')
User.create({
name : 'zero',
age : 24,
married :false,
comment : '자기소개'
});
models 모듈
에서 User 모델
을 불러와 create 메서드
를 사용하면 된다.
한 가지 주의해야 할 점은 데이터를 넣을 때 MySQL
의 자료형이 아니라 시퀼라이즈
모델에 정의한 자료형대로 넣어야한다. 이때, 자료형이나 옵션에 부합하지 않은 데이터를 넣었을 경우 시퀼라이즈
가 에러를 발생 시킨다.
1. 전체 조회
SELECT * FROM nodejs.users;
User.findAll({});
2. 일부 조회
User.find({});
3. 원하는 컬럼만 조회
User.findAll({
attributes:['name','marride'],
});
4. 조건에 부합하는 컬럼만 조회
(married가 1이고, age가 30보다 높은 컬럼만 조회)
const {User, Sequelize : { Op } } = require('../models');
User.findAll({
attributes : ['name','age'],
where : {
marride : 1,
age : {[Op.gt]:30},
},
});
시퀼라이즈
는 자바스크립트 객체를 사용해서 쿼리를 생성하기 때문에 Op.gt
같은 특수한 연산자들이 사용된다.
추가로, 쿼리에 order : [['age','DESC']]
구문을 사용하면, 정렬도 가능하다.
또, limit : 1
등의 옵션을 사용하면 조회할 로우 개수를 설정 할 수도 있다.
OFFSET도 역시 offset : 1
등으로 구현 가능하다.
UPDATE nodejs.users SET comment = '바꿀 내용' WHERE id = 2;
User.update({
comment : '바꿀 내용',
}, {
where : { id:2 },
});
DELETE FROM nodejs.users WHERE id = 2;
User.destory ({
where : { id : 2 },
});
이제 배운 쿼리를 이용해서 CRUD 작업을 수해하려고 한다. 모델에서 데이터를 받아서 페이지를 렌더링
하는 방식과 JSON 형식
으로 데이터를 가져오는 방법 총 두가지를 사용 할 예정이다.
먼저, 다음과 같이 view 폴더에 sequelize.pug 파일을 만들었다. AJAX를 사용해 서버와 통신하도록 만들 예정이다.
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='/sequelize.js')
다음으로, public 폴더 안에 sequelize.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.user.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 = '';
});
위 두개 파일은 프런트엔드 코드이기 때문에 그냥 복붙해줬다~~
이제 본격적으로 서버 코드를 확인해야한다. script 태그에는 버튼들을 눌렀을 때 서버의 라우터로 AJAX 요청을 보내는 코드가 들어있다.
조금 이따 만들 라우터들을 미리 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 sequelize = require('./models').sequelize;
var app = express();
sequelize.sync();
// 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;
이제 라우터의 내용을 추가해준다!
라우터는 GET, POST, PUT, DELETE 요청에 해당하는 라우터를 만들어준다.
var express = require('express');
var User = require('../models').User;
var router = express.Router();
router.get('/', function(req, res, next) {
User.findAll()
.then((users) => {
res.render('sequelize', { users });
})
.catch((err) => {
console.error(err);
next(err);
});
});
module.exports = router;
먼저, GET /
로 접속했을 때의 라우터이다. User.findAll 메서드
로 모든 사용자를 찾은 후, sequelize.pug
를 렌더링 할 때 결괏값인 users를 넣어준다.
시퀼라이즈는 프로미스
를 기본적으로 지원하므로, then
과 catch
를 사용해서 각각 조회 성송 시와 실패 시의 정보를 얻을 수 있다. 이렇게 미리 데이터베이스
에서 데이터를 조회한 후 템플릿 렌더링
에 사용 할 수 있다.
async/await 문법으로 바꿀 경우
router.get('/',async(req,res,next) => {
try {
const users = await User.findAll();
res.render('sequelize',{ users });
} catch(error) {
console.error(error);
next(error);
}
});
다음은 users.js
이다! 프로미스 형식으로 만들거당
var express = require('express');
var User = require('../models').User;
var router = express.Router();
router.get('/', function(req, res, next) {
User.findAll()
.then((users) => {
res.json(users);
})
.catch((err) => {
console.error(err);
next(err);
});
});
router.post('/', function(req, res, next) {
User.create({
name: req.body.name,
age: req.body.age,
married: req.body.married,
})
.then((result) => {
console.log(result);
res.status(201).json(result);
})
.catch((err) => {
console.error(err);
next(err);
});
});
module.exports = router;var express = require('express');
var User = require('../models').User;
var router = express.Router();
router.get('/', function(req, res, next) {
User.findAll()
.then((users) => {
res.json(users);
})
.catch((err) => {
console.error(err);
next(err);
});
});
router.post('/', function(req, res, next) {
User.create({
name: req.body.name,
age: req.body.age,
married: req.body.married,
})
.then((result) => {
console.log(result);
res.status(201).json(result);
})
.catch((err) => {
console.error(err);
next(err);
});
});
module.exports = router;
GET /users
와 POST /users
주소로 요청이 들어올 때의 라우터이다. 각각 사용자 조회
요청과 사용자 등록
요청을 처리한다.
GET /
에서도 사용자 데이터를 조회했지만, GET / users
에서는 데이터를 JSON 형식
으로 반환한다는 것에 차이가 있다.
다음은 comments.js
이다.
var express = require('express');
var { User, Comment } = require('../models');
var router = express.Router();
router.get('/:id', function(req, res, next) {
Comment.findAll({
include: {
model: User,
where: { id: req.params.id },
},
})
.then((comments) => {
console.log(comments);
res.json(comments);
})
.catch((err) => {
console.error(err);
next(err);
});
});
router.post('/', function(req, res, next) {
Comment.create({
commenter: req.body.id,
comment: req.body.comment,
})
.then((result) => {
console.log(result);
res.status(201).json(result);
})
.catch((err) => {
console.error(err);
next(err);
});
});
router.patch('/:id', function(req, res, next) {
Comment.update({ comment: req.body.comment }, { where: { id: req.params.id } })
.then((result) => {
res.json(result);
})
.catch((err) => {
console.error(err);
next(err);
});
});
router.delete('/:id', function(req, res, next) {
Comment.destroy({ where: { id: req.params.id } })
.then((result) => {
res.json(result);
})
.catch((err) => {
console.error(err);
next(err);
});
});
module.exports = router;
댓글과 관련된 CRUD 작업을 처리한다. GET /comments
, POST /comments
, PATCH /comments/:id
, DELETE /comments/:id
를 등록한다.
브라우저 창에 http://localhost:3000
으로 접속하면 테스트가 가능하다!
해당화면이 나온다.
새로운 사용자 정보를 입력 한 후, 등록
버튼을 누르면 사용자 등록이 가능하다.
사용자 아이디(사용자 이름 말고 아이디..)와 댓글 내용을 입력하고 등록
버튼을 누르면 댓글 등록이 된다.
사용자의 이름을 클릭하면 각 사용자들이 작성한 댓글을 확인 할 수 있다.
각 댓글의 수정
버튼을 클릭하면 수정 내용을 입력 하라는 콘솔창이 나온다.
아무 내용도 입력하지 않고 버튼을 누를 경우 경고창이 출력된다.
각 댓글의 삭제
버튼을 클릭 하면 삭제가 가능하다.
워크벤치
에서도 확인 가능하다.