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 |
29 | 30 |
Tags
- immer
- 리눅스
- 자바스크립트
- 노드
- MariaDB
- 이더리움
- 리액트를 다루는 기술
- 설치
- npm
- 시퀄라이즈
- 쉘스크립트
- 리액트
- Sequelize
- 벨로포터
- node.js 교과서 따라하기
- 우분투
- 변수
- 일반유저
- wget
- 전역설치
- 블록체인
- 머클루트
- centos
- 솔리디티
- 머클트리
- 환경변수
- 깃허브
- 라우터
- wsl
- Docker
Archives
- Today
- Total
코드코코
210926 [8장] 8.6 몽구스 사용하기 본문
1.package.json
{
"name": "learn-mongoose",
"version": "1.0.0",
"description": "몽구스를 배우자",
"main": "app.js",
"scripts": {
"start": "nodemon app"
},
"author": "codecoco",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.13"
},
"dependencies": {
"express": "^4.17.1",
"mongoose": "^6.0.7",
"morgan": "^1.10.0",
"nunjucks": "^3.2.3"
}
}
2. app.js
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const nunjucks = require('nunjucks');
const connect = require('./schemas');
//라우터연결
const indexRouter = require('./routes');
const userRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');
const app = express();
app.set('port', process.env.PORT || 3002);
app.set('view engine', 'html');
nunjucks.configure('views', {
express: app,
watch: true,
});
connect();
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', 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'), '번 포트에서 대기 중');
});
3. views/error.html
<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>
4. views/mongoose.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="/mongoose.js"></script>
</body>
</html>
4. schemas/index.js
const mongoose = require('mongoose');
//개발환경일 때만 콘솔을 통해 몽구스가 생성하는 쿼리 내용을 확인 할 수 있게 함.
const connect = () => {
if (process.env.NODE_ENV !== 'production') {
mongoose.set('debug', true);
}
//몽구스와 몽고디비를 연결
//몽고디비 주소로 접속시도 :
//mongodb://root:1234@localhost:27017/admin
mongoose.connect('mongodb://root:1234@localhost:27017/admin', {
//옵션
//dbName:사용할 데이터베이스
dbName: 'nodejs',
//아래 두 개는 입력하지 않아도 되지만, 콘솔에 경고메세지가 뜨므로 사용.
useNewUrlParser: true,
//아래옵션 활성화시 몽고디비에 연결이 안됨.
//useCreateIndex: true,
//콜백함수
}, (error) => {
if (error) {
console.log('몽고디비 연결 에러', error);
} else {
console.log('몽고디비 연결 성공');
}
});
};
//몽구스 커넥션에 이벤트리스너 부여, 에러발생시 에러 내용 기록
mongoose.connection.on('error', (error) => {
console.error('몽고디비 연결 에러', error);
});
//몽구스 커넥션에 이벤트리스너 부여, 연결종료시 재연결 시도
mongoose.connection.on('disconnected', () => {
console.log('몽고디비에 연결이 끊겼습니다. 연결을 재시도합니다.');
connect();
});
module.exports = connect;
5.shemas/user.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
//몽구스는 알아서 _id를 기본 키로 생성하므로, 별도로 필드를 적어줄 필요가 없다.
//required나 default 등의 옵션이 필요하지 않다면 간단하게 자료형만 명시해도 된다.
const userSchema = new Schema({
name: {
type: String,
required: true,
unique: true,
},
age: {
type: Number,
required: true,
},
married: {
type: Boolean,
required: true,
},
comment: String,
createdAt: {
type: Date,
default: Date.now,
},
});
//스키마와 몽고디비 컬렉션을 연결하는 모델 생성
module.exports = mongoose.model('User', userSchema);
/*
몽구스 스키마의 자료형
String
Number
Date
Buffer
Boolean
Mixed
Objectid
Array
*/
6..shemas/comment.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const { Types: { ObjectId } } = Schema;
const commentSchema = new mongoose.Schema({
commenter: {
//commenter 필드에 User 스키마의 사용자 ObjectId가 들어간다는 의미
//JOIN과 비슷한 기능을 사용할 때 이용.
type: ObjectId,
required: true,
ref: 'User',
},
comment: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Comment', commentSchema);
/*
//컬렉션 이름바꾸기
//첫글자를 소문자로 바꾼뒤 복수형으로 바꿔서 users 컬렉션을 생성함.
mongoose.model('User',userSchema,'user');
//위의 강제개명을 방지 : 세번째인수에 컬렉션명을 써준다. user_table의 컬렉션이 생성됨.
mongoose.model('User',userSchema,'user_table');
*/
7.public/mongoose.js
//사용자 이름을 클릭할 때 댓글 로딩
document.querySelectorAll('#user-list tr').forEach((el) => {
el.addEventListener('click', function () {
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.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.commenter.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.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.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 = '';
});
8. routes/index.js
const express = require('express');
//find 사용하려면 선언과 할당.
const User = require('../schemas/user');
const router = express.Router();
router.get('/', async (req, res, next) => {
try {
//User.find({}) : 모든 사용자를 찾음
//몽고디비의 db.users.find({})쿼리와 동일.
const users = await User.find({});
//mongoose.html을 렌더링 할 때, users 변수로 넣음.
res.render('mongoose', { users });
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
9. routes/users.js
const express = require('express');
const User = require('../schemas/user');
const Comment = require('../schemas/comment');
const router = express.Router();
router.route('/')
.get(async (req, res, next) => {
try {
const users = await User.find({});
res.json(users);
} catch (err) {
console.error(err);
next(err);
}
})
.post(async (req, res, next) => {
try {
//사용자 등록시 모델.create메서드로 저장
//_id는 자동저장.
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);
}
});
//댓글 다큐먼트를 조회하는 라우터
router.get('/:id/comments', async (req, res, next) => {
try {
//find({commenter: req.params.id}) : 댓글을 쓴 사용자의 아이디로 조회한 뒤
const comments = await Comment.find({ commenter: req.params.id })
//관련있는 컬렉션의 다큐먼트를 불러옴.
//Comment 스키마 commenter필드의 ref가 User로 되어 있으므로,
//알아서 users컬렉션에서 사용자 다큐먼트를 찾아서 합친다.
//commenter 필드가 사용자 다큐먼트로 치환됨.
//commenter 필드는 OnjectId가 아니라 그 OnjectId를 가진 사용자 다큐먼트가 된다.
.populate('commenter');
console.log(comments);
res.json(comments);
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
10. routes/comments.js
const express = require('express');
const Comment = require('../schemas/comment');
const router = express.Router();
//다큐먼트 등록
router.post('/', async (req, res, next) => {
try {
//Comment.create(): 댓글을 저장
const comment = await Comment.create({
commenter: req.body.id,
comment: req.body.comment,
});
console.log(comment);
//populate(): 프로미스의 결과로 반환된 comment객체에 다른 컬렉션 다큐먼트를 불러온다.
//path 옵션으로 어떤 필드를 합칠지 설정
const result = await Comment.populate(comment, { path: 'commenter' });
//합쳐진 결과를 클라이언트로 응답
res.status(201).json(result);
} catch (err) {
console.error(err);
next(err);
}
});
router.route('/:id')
//다큐먼트 수정
.patch(async (req, res, next) => {
try {
//update({어떤 다큐먼트를 수정할지를 나타낸 쿼리 객체를 제공},{수정할 필드와 값이 들어 있는 객체를 제공})
//시퀄라이즈와는 인수의 순서가 반대.
//몽고디비와 다르게 $set 연산자를 사용하지 않아도 기입한 필드만 바꿀 수 있음.
const result = await Comment.update({
_id: req.params.id,
}, {
comment: req.body.comment,
});
res.json(result);
} catch (err) {
console.error(err);
next(err);
}
})
//다큐먼트 삭제
.delete(async (req, res, next) => {
try {
//remove({어떤 다큐먼트를 삭제할지에 대한 조건})
const result = await Comment.remove({ _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 |
210918 [7장] 7.6 시퀄라이즈 사용하기 (0) | 2021.09.18 |
210917 [7장] 7.5 CRUD 작업하기 (0) | 2021.09.17 |
210917 [7장] 7.4 데이터베이스 및 테이블 생성하기 (0) | 2021.09.17 |