이번 포스팅에서는 4. Sequelize로 CRUD 하기, 5. Sequelize 관계 쿼리, 6. 쿼리 수행 예(전체 코드 설명) 를 다루겠다. 앞선 포스팅과 이어지므로 보고오면 이해에 도움이 될 것이다.
책 Node.js 교과서(개정 2판) 책의 7장의 내용을 참고했다.
+모든 코드는 github주소에 있다.
--
이전내용 정리
- MySQL: 테이블
users
,comments
생성 - Sequelize: 모델
User
,Comment
생성 - 테이블-모델 연결(User-users, Comment-comments)
- Sequelize에게 관계 알림 (users - comments)
4. Sequelize로 CRUD 하기
Sequelize로 CRUD 작업을 하려면 먼저 Sequelize 쿼리를 알아야 한다. 쿼리는 프로미스를 반환하므로 then을 붙여 결과값을 받을 수 있다. async/await 문법도 사용 가능하다.
간단한 예제와 함께 MySQL과 비교하며 Sequelize 쿼리에 익숙해져보자.나는 이미 MySQL을 공부했어서 MySQL과 비교하며 공부하는 것이 편했다.
1. C: CREATE(생성)
row를 생성하는 쿼리 EXAMPLE )
MySQL
INSTERT INTO nodejs.users (name, age, married, comment) VALUES ('zero', 24, 0, '자기소개1');
Sequelize
const { User } = require('../models'); // 모델명으로 불러옴
User.create({
name: 'zero',
age: 24,
married: false,
comment: '자기소개1',
});
이 아래부터는 Sequelize 쿼리를 쓸 곳에 const { User } = require('../models');
가 있다는 가정 하에 작성했다.
2. R: READ(조회)
사용 메서드
findAll({});
: 조건에 해당하는 모든 데이터findOne({});
: 조건에 해당하는 데이터 한 개
테이블의 데이터를 모든
데이터를 조회 EXAMPLE )
MySQL
SELECT * FROM nodejs.users;
Sequelize **findAll 메서드 사용
User.findAll({});
테이블의 데이터를 한 개
만 조회 EXAMPLE )
MySQL
SELECT * FROM nodejs.users LIMIT 1;
Sequelize **findOne 메서드 사용
User.findOne({});
테이블의 데이터 중 원하는 컬럼
만 조회 EXAMPLE )
MySQL
SELECT name, married FROM nodejs.users;
Sequelize **attributes 옵션 사용
User.findAll({
attributes: ['name', 'married'],
});
테이블의 데이터 중 원하는 컬럼에 조건
을 달아 조회 EXAMPLE )
MySQL
SELECT name, married FROM nodejs.users WHERE married =1 AND age > 30;
Sequelize **where 옵션 사용
const { Op } = require('sequelize'); // 시퀄라이즈는 자바스크립트 객체를 사용해 쿼리를 생성해야 하므로 이와 같이 특수한 연산자를 사용함, Sequelize 객체 내부의 Op 객체를 불러와 사용
const { User } = require('../models');
User.findAll({
attributes: ['name', 'married'],
where: {
married: true,
age: { [Op.gt]: 30 }, // [Op.gt]: 30은 ES2015 문법
},
});
주의: where 옵션에 undefined
가 들어가면 안 되고, 빈 값은 null
을 넣어야 한다.
+null과 undefined의 차이를 잘 설명해둔 블로그
[Op.gt]: 30
에 대한 추가설명이게 무슨말인지 처음 만났을때 당황했는데이 블로그를 보고 깨달았다!!
정리하자면,
const { Op } = require('sequelize');
코드를 보면 구조분해할당으로 sequelize안의 Op 객체를 가져온다.- 근데 Op객체는 사실, Operator(연산자)가 들어있는 객체이다!
- 자주 쓰이는 연산자
Op.gt(초과), Op.gte(이상), Op.lt(미만), Op.lte(이하), Op.ne(같지 않음), Op.or(또는), Op.in(배열 요소 중 하나), Op.notIn(배열 요소와 모두 다름)Op.or EXAMPLE )
Op.or 속성에 OR 연산을 적용할 쿼리를 배열로 나열하면 된다.
MySQL
SequelizeSELECT id, name FROM users WHERE married = 0 OR age > 30;
const { Op } = require('sequelize'); const { User } = require('../models'); User.findAll({ attributes: ['id', 'name'], where: { [Op.or]: [{ married: false }, {age: {[Op.gt]: 30} }], }, });
테이블의 데이터를 정렬
되어 보이게 조회 EXAMPLE )
실제로 저장된 데이터베이스가 정렬되지는 않는다. 출력 값만 정렬된다.
MySQL
SELECT id, name FROM users ORDER BY age DESC;
Sequelize **order 옵션 사용(주의: 배열 안에 배열이 있음 - 정렬은 여러 개의 컬럼으로도 가능하기 때문)
User.findAll({
attributes: ['id', 'name'],
order: [['age', 'DESC']],
});
테이블의 데이터 중 row의 개수
를 설정 후 조회 EXAMPLE )
MySQL
SELECT id, name FROM users ORDER BY age DESC LIMIT 1;
Sequelize **limit 옵션 사용(몇 개까지 가져올지)
User.findAll({
attributes: ['id', 'name'],
order: [['age', 'DESC']],
limit: 1,
});
테이블의 데이터 중 row 개수
를 설정 후 조회 EXAMPLE2 )
MySQL
SELECT id, name FROM users ORDER BY age DESC LIMIT 1 OFFESET 1;
Sequelize **offeset 옵션 사용(몇 번부터 가져올지)
User.findAll({
attributes: ['id', 'name'],
order: [['age', 'DESC']],
limit: 1,
});
3. U: UPDATE(수정)
테이블의 데이터를 수정
EXAMPLE )
MySQL
UPDATE nodejs.users SET comment = '바꿀 내용' WHERE id=2;
Sequelize **update 메서드 사용
User.update({ // 첫 번째 인수: 수정할 내용
comment: '바꿀 내용',
}, { // 두 번째 인수: 어떤 로우를 수정할지에 대한 조건 **where 옵션
where: {id: 2},
});
4. D: DELETE(삭제)
테이블의 데이터를 삭제
EXAMPLE )
MySQL
DELETE FROM nodejs.users WHERE id = 2;
Sequelize **destroy 메서드 사용
User.destroy({
where: {id: 2},
});
5. Sequelize 관계 쿼리
MySQL로 따지면 JOIN 기능을 지원한다.
메서드 반환 값
findAll({});
: 프로미스의 결과로 모델의 배열을 반환findOne({});
: 프로미스의 결과로 모델을 반환
_아래의 예시는 1:N(일대다)를 기준으로 작성되었다.
1 (User) : N (Comment)
_
모델을 반환하는 findOne EXAMPLE )
const user = await User.findOne({});
console.log(user.nick); // 사용자 닉네임
특정 사용자를 가져오면서 그 사람의 댓글까지 가져오는 EXAMPLE )
const user = await User.findOne({
include: [{
model: Comment,
}]
});
console.log(user.Comments); // 사용자 댓글
user.Comments
에 대해 내가 이해한 내용
나는 분명User
,Comment
모델,users
,comments
테이블을 만들 때Comments
를 만든 적이 없다!
내가 이해한 바로는 1:N같이 일대다(hasMany) 관계를 가지면, s가 붙은 이 것(Comments와 같은)을 통해 여러 개의 Comment가 출력이 되는 것이다.console.log(user)
을 통해 이 형태를 찾아주면 사용이 가능하다.
N(Comment: 댓글)에 접근하는 또다른 EXAMPLE )
const user = await User.findOne({});
const comments = await User.getComments();
console.log(commments); // 사용자 댓글
getComments()
추가 설명
관계를 설정했다면 동사 뒤에 모델의 이름이 붙는 형식의 메서드를 지원한다.
getComments(조회)
: 조회setComments(수정)
: 수정addComment(하나 생성)
: 하나 생성addComments(여러 개 생성)
: 여러 개 생성removeComments(삭제)
: 삭제- 동사 뒤의 모델 이름을 바꾸고 싶다면?*
as 옵션
을 쓰자! ** include 시 추가되는 모델(Comment) 객체도 user.Answers로 바뀜// 관계를 설정할 때 as로 등록 db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', as: 'Answers'}); // 쿼리할 때는 const user = await User.findOne({}); const comments = await user.getAnswer(); console.log(comments); // 사용자 댓글
관계가 적용된 조회
EXAMPLE ) ** where, attributes 같은 옵션 가능
const user = await User.findOne({
include: [{
model: Comment,
where: {
id: 1,
},
attributes: [id],
}]
});
// 또는
const comments = await user.getComments({
where: {
id: 1,
},
attributes: ['id']
});
관계가 적용된 생성
EXAMPLE )
const user = await User.findOne({}); // User 모델의 배열의 첫 번째에 있는(첫 번째로 찾은) user 객체를 가져옴
const comment = await Comment.create(); // 빈 comment 만듦
// 1과 2의 결과는 동일
// 1 관계 쿼리 메서드의 인수로 추가할 댓글 모델을 넣음
await user.addComment(comment);
// 2 관계 쿼리 메서드의 인수로 추가할 댓글의 아이디를 넣음
await user.addComment(comment.id);
관계가 적용된 여러개 생성
, 배열
EXAMPLE )
const user = await User.findOne({});
const comment1 = await Comment.create();
const comment2 = await Comment.create();
await user.addComment([comment1, comment2]); // 배열 형태로 2개의 comment를 추가함
+Sequelize로 할 수 없는 경우 SQL 쿼리
직접 SQL문을 통해 쿼리할 수도 있다. 그치만 안 쓰는게 좋을 것 같다
const [result, metadata] = await sequelize.query('SELECT * from comments');
console.log(result);
6. 쿼리 수행 예(전체 코드 설명)
쿼리 수행 예 읽는 법
- Git [파일명]이 먼저 제시된다.
- 대부분의 설명은 주석으로 담았으며, 추가 설명이 필요한 부분은 블로그에 설명/링크해두었다.
- =1>, =2> ... 은 Git [파일명]이 실행되었을 때 이동/실행될 것을 알려준다.
- 숫자와 주석을 차근차근 따라가다보면 모든 코드가 이해된다!!
- 전체 코드/주석(이)가 궁금하다면? git주소를 확인해보자!
Git [package.json
]
- package.json 세팅하기
{ "name": "delay100_learn-sequelize", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "nodemon app" }, "author": "delay100", "license": "ISC", "devDependencies": { "nodemon": "^2.0.15" }, "dependencies": { "express": "^4.17.2", "morgan": "^1.10.0", "mysql2": "^2.3.3", "nunjucks": "^3.2.3", "sequelize": "^6.13.0", "sequelize-cli": "^6.4.1" } }
- =1> "main"에 의해
app.js
실행*
Git [app.js
]
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const nunjucks = require('nunjucks');
const { sequelize } = require('./models'); // require('./models/index.js')와 같음 - index.js는 require 시 이름 생략 가능
const indexRouter = require('./routes');
const usersRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');
const app = express(); // require해온 express 실행
app.set('port', process.env.PORT || 3001); // 포트번호 3001로 세팅
app.set('view engine', 'html');
nunjucks.configure('views', { // render 시, views 폴더로 이동
express: app,
watch: true,
});
sequelize.sync({force: false}) // 서버 실행 시 MySQL과 연동되도록 함, force: true면 서버 실행 시 마다 테이블을 재생성, 테이블을 잘못 만든 경우에 true로 설정
.then(() => {
console.log('데이터베이스 연결 성공');
})
.catch((err) => {
console.error(err);
});
app.use(morgan('dev'));
app.use('/',express.static(path.join(__dirname, 'public'))); // static이라서 사용자가 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'), '번 포트에서 대기 중');
});
=2> http://127.0.0.1:3001/ 로 접속 시, '/'로 선언된 indexRouter에 의해 "routes/index.js
" 실행
Git [routes/index.js
]
const express = require('express');
const User = require('../models/user');
const router = express.Router();
// '/' get 요청
router.get('/', async (req,res,next) =>{
try{
const users = await User.findAll(); // 모든 유저를 찾아서 sequelize.html에 users로 렌더링
res.render('sequelize', {users});
} catch(err){
console.error(err);
next(err);
}
});
module.exports = router;
=3> render에 의해 'views/sequelize.html
'로 이동
Git [views/sequelize.html
]
**nunjucks의 주석 표시({# #}) 주의해서 볼 것!
<!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> {# fieldset: 웹 양식의 여러 컨트롤과 레이블(<label>)을 묶을 때 사용 #}
<legend>사용자 등록</legend> {# legend: <fieldset> 요소의 캡션(caption)을 정의할 때 사용 #}
<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> {# thead: table head #}
<tr> {# <tr>: table row - 가로줄 #}
<th>아이디</th> {# <th>: table head - 표의 제목 #}
<th>이름</th>
<th>나이</th>
<th>결혼여부</th>
</tr>
</thead>
<tbody> {# tbody: table body #}
{% 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> {# AJAX(페이지 이동X) - axios 사용 언급 #}
<script src="/sequelize.js"></script> {# app.js의 app.use('/',express.static(path.join(__dirname, 'public')));에 의해 접근 가능 #}
</body>
</html>
=4> 실행화면1
처럼 sequelize.html
이 http://127.0.0.1:3001/
에 표시됨, /sequelize.js
에 의해 public/sequlelize.js
실행
- 5로 이동: id="user-form"
의 등록
을 눌렀을 때
- 6로 이동: id="comment-form"
의 등록
을 눌렀을 때
Git [public/sequelize.js
] 中 사용자 등록(user-form)
// 사용자 등록 시
document.getElementById('user-form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = e.target.username.value; // e.target:user-form이라는 id를 가진 target, username: id 이름이 username, value: 들어있는 값
const age = e.target.age.value;
const married = e.target.married.checked;
console.log(name);
if(!name){
return alert('이름을 입력하세요');
}
if(!age){
return alert('나이를 입력하세요');
}
try{
await axios.post('/users', {name, age, married}); // app.js의 '/users'에 의해 routes/users.js의 post가 실행(post에 name, age, married 값이 전송됨)
getUser(); // getUser() 함수 실행
} catch(err){
console.error(err);
}
// 입력한 것 초기화
e.target.username.value = '';
e.target.age.value = '';
e.target.married.checked = false;
});
=5> 5-1, 5-2가 순차적으로 실행된 후 User가 등록/표시
5-1: axios.post에 의해 routes/users.js
의 post
가 실행, post에 name, age, married 값이 전송됨
5-2: 5-1에 의해 DB에만 새로운 User가 생성된 상태에서 getUser()
실행
Git [routes/users.js
] 中 '/'의 post
- .json(응답)
.post(async (req, res, next) => { try { const user = await User.create({ // 유저모델 생성 name: req.body.name, // sequelize.js에서 전송받은 name값을 가져옴 age: req.body.age, // sequelize.js에서 전송받은 age값을 가져옴 married: req.body.married, // sequelize.js에서 전송받은 married값을 가져옴 }); console.log(user); res.status(201).json(user); // 성공 user(응답) 전송 } catch(err){ console.error(err); next(err); } });
- 참고) Git [models/
user.js
]const Sequelize = require('sequelize');
module.exports = class User extends Sequelize.Model { // User 모델을 만들고 모듈로 exports함(User 모델은 Sequelize.Model을 확장한 클래스)
static init(sequelize){ // 테이블에 대한 설정 <-> static associate: 다른 모델과의 관계
return super.init({ // super.init의 첫 번째 인수: 테이블에 대한 컬럼 설정
name: {
type: Sequelize.STRING(20), // STRING: MySQL의 VARCHAR
allowNull: false, // allowNull: MySQL의 NOT NULL
unique: true, // unique: MySQL의 UNIQUE
},
age: {
type: Sequelize.INTEGER.UNSIGNED, // INTEGER: MySQL의 INT
allowNull: false,
},
married: {
type: Sequelize.BOOLEAN, // BOOLEAN: MySQL의 TINYINT
allowNull: true,
},
comment: {
type: Sequelize.TEXT,
allowNull: true,
},
create_at: {
type: Sequelize.DATE, // DATE: MySQL의 DATETIME
allowNull: false,
defaultValue: Sequelize.NOW, // defaultValue: MySQL의 default, Sequelize.NOW: MySQL의 now()
},
}, { // super.init의 두 번째 인수: 테이블 자체에 대한 설정(테이블 옵션)
sequelize, // static init 메서드의 매개변수와 연결되는 옵션
timestamps: false, // true: Sequelize가 자동으로 createdAt과 updatedAt 컬럼을 추가
underscored: false, // true: create_at같이(스네이크 케이스), false: createdAt같이(캐멀 케이스)
modelName: 'User', // 모델 이름
tableName: 'users', // 테이블 이름
paranoid: false, // 컬럼을 지워도 완전히 지워지지 않고 deletedAt이라는 컬럼이 생김(지운 시각이 기록됨)
charset: 'utf8', // 한글 입력, 이모티콘까지 입력: utf8mb4
collate: 'utf8_general_ci', // 한글 입력, 이모티콘까지 입력: utf8mb4_general_ci
});
}
static associate(db){ // 다른 모델과의 관계 <-> static init: 테이블에 대한 설정
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id'});
}
}
**=5-1> DB에만 새로운 User가 생성됨**
Git [public/`sequelize.js`] 中 getUser()
- DOM: 브라우저 안에 출력될 HTML 창에 대한 객체 / DOM 트리의 루트는 document 객체(출처: [document](https://codingwanee.tistory.com/entry/JavaScript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-document-%EA%B0%9D%EC%B2%B4))
- [innerHTML](https://codingwanee.tistory.com/entry/JavaScript-HTML-DOM-%EA%B0%9D%EC%B2%B4-Document-Object-Model)
```javascript
// 사용자 로딩 함수
async function getUser(){
try{
const res = await axios.get('/users'); // app.js의 '/users'에 의해 routes/users.js의 get이 실행되고 결국, User.findAll()이 가져와짐
const users = res.data; // .data는 res의 데이터가 존재함
// console.log(users);
const tbody = document.querySelector('#user-list tbody'); // document: html의 가장 상위 객체(설명 블로그 참조)
tbody.innerHTML = ''; // 일단 tbody에 대한 모든 내용을 초기화함
users.map(function (user){ // map: 반복문
const row = document.createElement('tr'); // tr을 생성하면서 빈 row 객체 생성
// 추가된 데이터에 대한 listener도 필요
row.addEventListener('click', () => { // 이 row를 클릭하면 실행
getComment(user.id); // getComment 함수 실행(파라미터로 user의 id를 줌)
console.log(user.id);
});
// row 셀 추가
let td = document.createElement('td'); // 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);
}
}
=5-2> 실행화면2
처럼 DB에 있는 User 데이터가 화면에 보이게 됨, 중간에 getComment(user.id)
는 =6-2> 참고
Git [public/sequelize.js
] 中 댓글 등록(comment-form)
** 코드의 내용은 5와 유사(참고하면 됨)
// 댓글 등록 시
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); // getComment 함수 실행, 파라미터로 userid를 보냄
} catch(err){
console.error(err);
}
e.target.userid.value='';
e.target.comment.value='';
});
=6> 6-1, 6-2가 순차적으로 실행된 후 Comment가 등록/표시
6-1: axios.post에 의해 routes/comments.js
의 post
가 실행, post에 id, comment 값이 전송됨
6-2: 6-1에 의해 DB에만 새로운 Comment가 생성된 상태에서 getComment()
실행
Git [routes/comments.js
] 中 '/'의 post
router.post('/', async (req, res, next) => {
try {
const comment = await Comment.create({ // Comment 모델 생성
commenter: req.body.id, // foreignKey 저장
comment: req.body.comment,
});
console.log(comment);
res.status(201).json(comment);
} catch(err){
console.error(err);
next(err);
}
});
+참고) Git [models/comment.js
]
**foreigKey에 commenter을 확인할 수 있음
const Sequelize = require('sequelize');
module.exports = class Comment extends Sequelize.Model {
static init(sequelize){
return super.init({
comment: {
type: Sequelize.STRING(100),
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: true,
defaultVlue: Sequelize.NOW,
},
}, {
sequelize,
timestamps: false,
modelName: 'Comment',
tableName: 'comments',
paranoid: false,
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci',
});
}
static associate(db){
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id'});
}
};
=6-1> DB에만 새로운 comments가 생성됨
Git [public/sequelize.js
] 中 getComment(id)
// 댓글 로딩 함수
async function getComment(id) {
try{
const res = await axios.get(`/users/${id}/comments`); // app.js의 '/users'에 의해 routes/users.js의 '/:id/comments'가 실행
const comments = res.data;
const tbody = document.querySelector('#comment-list tbody');
tbody.innerHTML = '';
comments.map(function (comment) { // map: 반복문
// row 셀 추가
const row = document.createElement('tr');
let td = document.createElement('td'); // 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 = '수정';
console.log(edit);
edit.addEventListener('click', async () => { // 수정 클릭 시
const newComment = prompt('바꿀 내용을 입력하세요');
if (!newComment){
return alert('내용을 반드시 입력하셔야 합니다');
}
try {
await axios.patch(`/comments/${comment.id}`, { comment: newComment }); // app.js의 '/users'에 의해 routes/comments.js의 '/:id'의 patch가 실행, 새로 작성한 수정 글도 보냄
getComment(id);
} catch(err){
console.error(err);
}
});
// 삭제
const remove = document.createElement('button'); // 삭제 버튼
remove.textContent = '삭제';
remove.addEventListener('click', async() => { // 삭제 클릭 시
try {
await axios.delete(`/comments/${comment.id}`); // app.js의 '/users'에 의해 routes/comments.js의 '/:id'의 delete가 실행
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);
}
}
=6-2> 실행화면3
처럼 파라미터로 온 user.id에 맞는 댓글만 출력되고, 실행화면4
처럼 댓글 수정, 실행화면5
처럼 댓글 삭제
+참고)
Git [public/sequelize.js
] 中 처음 html 로딩 시 실행됨
// html이 로딩되었을 때 실행됨(기존 데이터)
// 사용자 이름을 눌렀을 때 댓글 로딩
document.querySelectorAll('#user-list tr').forEach((el) => { // id: #, el: element 반복문
el.addEventListener('click', function() {
const id = el.querySelector('td').textContent; // body 값(모든 요소를 포함한)을 하나씩 불러옴 (user.id, user.name, user.age, '기혼' if user.married else '미혼')
getComment(id); // getComment 함수 호출(아래에 작성되어있음)
});
});
Git [models/index.js
]
**model과 DB를 관리
const Sequelize = require('sequelize'); // 시퀄라이즈 패키지이자 생성자
const User = require('./user');
const Comment = require('./comment');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env]; // config/config.json에서 데이터베이스 설정을 불러옴
const db = {};
const sequelize = new Sequelize(config.database, config.username, config.password, config); // new Sequelize를 통해 MySQL 연결 객체 생성
db.sequelize = sequelize; // 연결 객체를 나중에 재사용 하기 위해 db.Sequelize에 넣음
db.Sequelize = Sequelize;
// db 객체에 User, Comment 모델을 담음 -> 앞으로 db를 require해서 User, Comment에 접근 가능
db.User = User;
db.Comment = Comment;
// 각 모델의 static init을 호출, init이 실행되어야 테이블이 모델로 연결(테이블-모델 연결)
User.init(sequelize);
Comment.init(sequelize);
// 다른 테이블과 관계를 연결
User.associate(db);
Comment.associate(db);
module.exports = db;
--
실행 화면
실행화면(console)
실행화면 1(웹 브라우저) - http://127.0.0.1:3001
실행화면 2(웹 브라우저) - 사용자 등록
실행화면 3(웹 브라우저) - 댓글 등록 ** 사용자 등록의 아이디 클릭 시 작성자별 작성한 댓글이 보이게 됨!
실행화면 4(웹 브라우저)
댓글 수정 화면
댓글 수정 결과
실행화면 5(웹 브라우저) - 댓글 삭제
디렉토리 구조
오타가 너무 많아서 실행하기까지 힘들었다,... 특히 코드들이 왔다갔다 하면서 실행해서 머리가 복잡해졌었다...그래도 2일동안 이 코드들만 본 결과!! 나름 잘 이해한 것 같아 뿌듯하다😊
잘못된 정보 수정 및 피드백 환영합니다!!
'Study > Node.js' 카테고리의 다른 글
14 - SNS 만들기 -1(with Node, MySQL, Nunjucks) (0) | 2022.06.13 |
---|---|
13 - Mongoose(with MongoDB) (0) | 2022.06.13 |
11 - Sequelize(with MySQL) -1 (0) | 2022.06.13 |
10 - express 웹 서버 만들기 -3(템플릿 엔진(feat. 퍼그, 넌적스)) (0) | 2022.06.13 |
9 - express 웹 서버 만들기 -2(multer, 라우팅 분리, req, res, +전체 코드) (0) | 2022.06.13 |