Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- 이더리움
- 전역설치
- 리눅스
- 환경변수
- 설치
- wget
- 솔리디티
- 리액트
- 머클루트
- npm
- 블록체인
- 머클트리
- 깃허브
- Docker
- immer
- MariaDB
- 노드
- 리액트를 다루는 기술
- node.js 교과서 따라하기
- 자바스크립트
- 우분투
- 시퀄라이즈
- wsl
- centos
- 쉘스크립트
- 일반유저
- 변수
- 벨로포터
- Sequelize
- 라우터
Archives
- Today
- Total
코드코코
210918 [7장] 7.6 시퀄라이즈 사용하기 본문
*사용자등록 및 사용자조회/댓글조회 작성 수정 삭제 구현하기
작성한것들
app.js
models[폴더]
1.index.js
2.user.js
3.comment.js
public[폴더]
sequelize.js
views[폴더]
error.html
sequelize.html
routes[폴더]
1.index.js
2.user.js
3.comment.js
1.app.js
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const nunjucks = require('nunjucks');
const { sequelize } = require('./models');
//라우터 연결
const indexRouter = require('./routes');
const userRouter = require('./routes/users');
const commentRouter = require('./routes/comments');
const app = express();
app.set('port', process.env.PORT || 3001);
app.set('view engine', 'html');
nunjucks.configure('views', {
express: app,
watch: true
});
//db.sequelize를 불러와서 sync메서드를 사용해 서버실핼시 mysql과 연동되도록 함.
//force: false 옵션을 true로 설정시 서버실행 시마다 테이블 재생성. 테이블을 잘못 만들 경우 true하면 됨.
sequelize.sync({ force: false })
.then(() => {
console.log('데이터베이스 연결 성공');
})
.catch((err) => {
console.error(err);
})
app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
//라우터연결
app.use('/', indexRouter);
app.use('/users', userRouter);
app.use('/comments', commentRouter);
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.models[폴더]-1.index.js
//Sequelize : 시퀄라이즈 패키지이자 생성자.
const Sequelize = require('sequelize');
//모델과 연결하기
const User = require('./user');
const Comment = require('./comment');
const env = process.env.NODE_ENV || 'development';
//config/config.json에서 데이터 설정을 불러옴.
// '/../config/config.json':경로에서 / 있고 없고 차이남. 없을 때 에러남.
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
//new Sequelize를 통해 MySQL 연결 객체를 생성.
const sequelize = new Sequelize(config.database, config.username, config.password, config);
//연결 객체를 재사용하기 위해 db.sequelize에 넣어둠.
db.sequelize = sequelize;
//db라는 객체에 User과 Comment 모델을 담음.
//앞으로 db객체를 require하여 모델에 접근 가능.
db.User = User;
db.Comment = Comment;
//User.init은 모델의 static.init() 호출.
User.init(sequelize);
Comment.init(sequelize);
//associate():다른 테이블과의 관계를 연결.
User.associate(db);
Comment.associate(db);
module.exports = db;
2.models[폴더]-2.user.js
//MySQL에서 정의한 테이블을 시퀄라이즈에서도 정의.
//MySQL의 테이블은 시퀄라이즈의 모델과 대응.
//시퀄라이즈는 모델과 MySQL 테이블을 연결해주는 역할.
//User모델 만들기
const Sequelize = require('sequelize');
//모델을 만들고 모듈로 exports함.
module.exports = class User extends Sequelize.Model {
//static init() : 테이블에대한 설정.
static init(sequelize) {
//super.init(첫번째인수,두번째인수)
//첫번째 인수 : 테이블 칼럼에 대한 설정.
//두번째 인수 : 테이블 자체에 대한 설정.
//시퀄라이즈는 알아서 id를 기본키로 설정하므로 id컬럼은 적을 필요가 없다.
return super.init({
name: {
type: Sequelize.STRING(20),
allowNull: false,
unique: true,
},
age: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
},
married: {
type: Sequelize.BOOLEAN,
allowNull: false,
},
comment: {
type: Sequelize.TEXT,
allowNull: true,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
}, {
sequelize,
timestamps: false,
underscored: false,
modelName: 'User',
tableName: 'users',
paranoid: false,
charset: 'utf8',
collate: 'utf8_general_ci',
});
}
static associate(db) {
//hasMany: 1대N의 관계
//User 테이블의 로우 하나를 불러 올 때, 연결된 comments 테이블의 로우들도 같이 불러옴.
//외래키를 따로 지정하지 않는다면, 모델명+기본키 로 시퀄라이즈가 임의생성함.
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id' });
}
};
//static init() : 테이블에대한 설정.
//static associate() : 다른 모델과의 관계.
//시퀄라이즈와 MySQL의 자료형이 다름.
/*
테이블 옵션
1. sequelize : static init 매개변수와 연결되는 옵션, db.sequelize 객체를 넣어야 함.
timestamps: ture인 경우에 시퀄라이즈는 createdAt과 updatedAt 컬럼을 추가한다. 자동으로 시간 입력.
underscored: 스네이크케이스로 바꿈.기본값 캐멀케이스.
modelName: 모델 이름 설정. 노드 프로젝트에서 사용.
tableName: 실제 데이터베이스의 테이블 이름. 소문자 및 복수형함.
paranoid: true인 경우에 deletedAt 컬럼이 생성됨. 로우를 삭제할 때 완전히 지워지지않고 삭제된 시간이 기록됨.
-로우조회 명령시 deletedAt 값이 null인 로우(삭제되지 않았다는 뜻)를 조회. 나중에 복원하기 위해서
charset: 'utf8', 한글 만약 이모티콘까지 입력할수 있게 하려면 utf8mb4
collate: 'utf8_general_ci', 한글 만약 이모티콘까지 입력할수 있게 하려면 utf8mb4_general_ci
*/
2.models[폴더]-3.comment.js
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,
defaultValue: Sequelize.NOW,
},
}, {
sequelize,
timestamps: false,
modelName: 'Comment',
tableName: 'comments',
paranoid: false,
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci',
});
}
static associate(db) {
//belongsTo : 다른 모델의 정보가 들어가는 테이블에 사용.
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id' });
}
};
3.public[폴더]-sequelize.js
//사용자 이름을 눌렀을 때 댓글 로딩
document.querySelectorAll('#user-list tr').forEach((el) => {
el.addEventListener('click', function () {
//textContent : 모든 텍스트를 그대로
const id = el.querySelector('td').textContent;
//댓글조회
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.log(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(`/comments/${comment.id}`);
getComment(id);
} catch (err) {
console.log(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.checked;
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.checked = 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 = '';
})
4.views[폴더]-1.error.html
<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>
4..views[폴더]-2.sequelize.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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>
<!-- 버튼들을 눌렀을 때, 서버의 라우터로 AJAX요청을 보내는 코드 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="/sequelize.js"></script>
</body>
</html>
5.routes[폴더]-1.index.js
//sequelize.js에 나오는 GET,POST,PUT,DELETE 요청에 해당하는 라우터를 만든다.
//시퀄라이즈는 프로미스를 기본적으로 지원
// async/await와 try/catch문을 사용하여 각각 조회 성공시와 실패 시의 정보를 얻을 수 있음.
const express = require('express');
const User = require('../models/user');
const router = express.Router();
//GET /로 접속했을 때의 라우터
router.get('/', async (req, res, next) => {
try {
//User.findAll 메서드로 모든 사용자를 찾음.
const users = await User.findAll();
//sequelize.html을 렌더링 할 때 결괏값인 users를 넣는다.
res.render('sequelize', { users });
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
5.routes[폴더]-2.users.js
const express = require('express');
const User = require('../models/user');
const Comment = require('../models/comment');
const router = express.Router();
//router.route로 같은 라우트 경로를 하나로 묶음.
router.route('/')
//GET /users 요청
//사용자를 조회하는 요청
//GET / 와의 차이점 : 데이터를 JSON형식으로 반환.
.get(async (req, res, next) => {
try {
const users = await User.findAll();
res.json(users);
} catch (err) {
console.error(err);
next(err);
}
})
//POST /users 요청
//사용자를 등록하는 요청
.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.error(err);
next(err);
}
});
// :id 는 라우터 매개변수로 req.params.id로 값을 가져올 수 있음.
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);
next(err);
}
});
module.exports = router;
5.routes[폴더]-3.comments.js
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({
//사용자 id를 넣어 사용자와 댓글을 연결
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;
'기록 > node.js 교과서 따라하기' 카테고리의 다른 글
210925 [8장] 8.5 몽고디비 CRUD 작업하기 (0) | 2021.09.25 |
---|---|
210926 [8장] 8.4 몽고디비 데이터베이스 및 컬렉션 생성하기 (0) | 2021.09.25 |
210917 [7장] 7.5 CRUD 작업하기 (0) | 2021.09.17 |
210917 [7장] 7.4 데이터베이스 및 테이블 생성하기 (0) | 2021.09.17 |
210917 [7장] 7.1-7.3 MySQL (0) | 2021.09.17 |