* 위 내용을 정리하였음
8 몽고디비
- 자바스크립트 문법을 사용하는 NoSQL 데이터 베이스이다.
8.1 NoSQL vs. SQL
- NoSQL(Not only SQL)의 대표 주자는 몽고디비이다.
SQL(MySQL) | NoSQL(몽고디비) |
규칙에 맞는 데이터 입력 테이블 간 JOIN 지원 안정성, 일관성 용어(테이블, 로우, 컬럼) |
자유로운 데이터 입력 컬렉션 간 JOIN 미지원 확장성, 가용성 용어(컬렉션, 다큐먼트, 필드) |
- 고정된 테이블이 아니라 컬럼 개념이 없는 컬렉션을 사용한다.
- 컬렉션을 만들면 어떤 데이터든 들어갈 수 있다.
- JOIN기능이 없어 동시에 쿼리를 수행하기 어려운 단점이 있지만 확장성, 가용성, 데이터의 분산에 유리하다.
- 많은 기업이 두 데이터 베이스를 동시에 사용한다.
- 항공사 예약 시스템의 경우, 비행기 표에 관한 정보가 모든 항공사에 일관성 있게 전달되어야 하므로 예약 처리 부분의 데이터베이스는 MySQL을 사용하지만 핵심 기능 외의 빅데이터, 메시징, 세션 관리 등에는 확장성과 가용성을 위해 몽고디비를 사용할 수도 있다.
8.2 몽고디비 설치하기
8.3 컴퍼스 설치하기
- 워크벤치처럼 몽고디비는 관리 도구로 컴퍼스를 제공한다.
8.3.4 커넥션 생성하기
8.4 데이터베이스 및 컬렉션 생성하기
- 데이터베이스 생성 : use [데이터베이스명]
> use nodejs
switched to db nodejs
- 데이터베이스 목록을 확인 : show dbs
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
- 방금 생성한 nodejs가 안보인다. 데이터를 최소 한 개 이상 넣어야 목록에 표시된다.
- 현재 사용 중인 데이터베이스를 확인 : db
> db
nodejs
- 도큐먼트를 넣는 순간 컬렉션도 자동으로 생성되지만 직접할수도 있다.
- 직접 컬렉션을 생성
> db.createCollection('users')
{ "ok" : 1 }
> db.createCollection('comments')
{ "ok" : 1 }
- 생성한 컬렉션 목록을 확인
> show collections
comments
users
8.5 CRUD 작업하기
8.5.1 Create(생성)
- 컬렉션에는 아무 데이터나 넣을 수 있다.
- 기본적으로 몽고디비는 자바스크립트 문법을 사용하므로 자바스크립트의 자료형을 따른다.
- Date, Timestamp 등의 추가적인 자료형이 있다.
- Undefined와 Symbol은 몽고디비에서 자료형으로 사용하지 않는다.
- ObjectId는 도큐먼트의 고유한 값이다.
$ mongo
> use nodejs;
switched to db nodejs
> db.users.save({ name: 'zero', age: 24, married: false, comment: '안녕하세요. 간단히 몽고디비 사용 방법에 대해 알아봅시다.', createdAt: new Date() });
WriteResult({ "nInserted" : 1 })
> db.users.save({ name: 'nero', age: 32, married: true, comment: '안녕하세요. zero 친구nero입니다.', createdAt: new Date() });
WriteResult({ "nInserted" : 1 })
- db.컬렉션명.save(도큐먼트)로 도큐먼트 생성
- WriteResult({ "nInserted": 1 }) 응답은 도큐먼트 하나가 성공적으로 생성되었다는 뜻이다.
- comments 컬렉션에도 도큐먼트를 생성(데이터를 넣어)해보자
- 먼저 zero 도큐먼트의 아이디를 찾는다.
> db.users.find({ name: 'zero' }, { _id: 1 })
{ "_id" : ObjectId("5a1687007af03c3700826f70") }
- 찾은 아이디를 commenter로 해서 comments 컬렉션에 데이터를 넣는다.
- 이러면 작성자를 쉽게 찾을 수 있다.
> db.comments.save({ commenter: ObjectId('5a1687007af03c3700826f70'), comment: '안녕하세요. zero의 댓글입니다.', createdAt: new Date() });
WriteResult({ "nInserted" : 1 })
8.5.2 Read(조회)
- 생성한 다큐먼트들을 조회해보자.
$ mongo
> db.users.find({});
{ "_id" : ObjectId("5a1687007af03c3700826f70"), "name" : "zero", "age" : 24, "married" : false, "comment" : "안녕하세요. 간단히 몽고디비 사용 방법을 알아봅시다.", "createdAt" : ISODate("2017-11-23T00:00:00Z") }
{ "_id" : ObjectId("5a16877b7af03c3700826f71"), "name" : "nero", "age" : 32, "married" : true, "comment" : "안녕하세요. zero 친구 nero입니다.", "createdAt" : ISODate("2017-11-23T01:00:00Z") }
> db.comments.find({})
{ "_id" : ObjectId("5a1687e67af03c3700826f73"), "commenter" : ObjectId("5a1687007af03c3700826f70"), "comment" : "안녕하세요. zero의 댓글입니다.", "createdAt" : ISODate("2017-11-23T02:00:00Z") }
- find({})는 컬렉션 내의 모든 도큐먼트를 조회하라는 뜻이다.
- 아래 처럼 특정 필드만 조회할 수 있다.
$ mongo
> db.users.find({}, { _id: 0, name: 1, married: 1 });
{ "name" : "zero", "married" : false }
{ "name" : "nero", "married" : true }
- 1 또는 true로 표시한 필드만 가져온다.
- 아래 예시는 조회시 조건을 부여한다.
> db.users.find({ age: { $gt: 30 }, married: true }, { _id: 0, name: 1, age: 1 });
{ "name" : "nero", "age" : 32 }
- 자바스크립트 객체를 사용해서 쿼리 명령을 생성한다.
- $gt(초과), $gte(이상), $lt(미만), $lte(이하), $ne(같지 않음), $or(또는), $in(배열 요소 중 하나) 등이 있다.
- 아래는 OR 연산을 사용한 예시이다.
- age가 30 초과이거나 married가 false인 도큐먼트를 조회한다.
> db.users.find({ $or: [{ age: { $gt: 30 } }, { married: false }] }, { _id: 0, name: 1, age: 1});
{ "name" : "zero", "age" : 24 }
{ "name" : "nero", "age" : 32 }
- 도큐먼트 조회시 정렬도 가능하다.
> db.users.find({}, { _id: 0, name: 1, age: 1 }).sort({ age: -1 })
{ "name" : "nero", "age" : 32 }
{ "name" : "zero", "age" : 24 }
- limit로 조회할 도큐먼트 개수를 설정할 수 있다.
> db.users.find({}, { _id: 0, name: 1, age: 1}).sort({ age: -1 }).limit(1)
{ "name" : "nero", "age" : 32 }
- skip을 통해 몇 개를 건너뛸고 조회할지 설정할 수 있다.
> db.users.find({}, { _id: 0, name: 1, age: 1 }).sort({ age: -1 }).limit(1).skip(1)
{ "name" : "zero", "age" : 24 }
8.5.3 Update(수정)
$ mongo
> db.users.update({ name: 'nero' }, { $set: { comment: '안녕하세요. 이 필드를 바꿔보겠습니다!' } });
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
- 첫 번째 객체는 수정할 도큐먼트, 두 번째 객체는 수정할 내용을 넣는다.
- $set이라는 연산자로 어떤 필드를 수정할지 정한다.
- $set을 사용하지 않고 일반 객체를 넣으면 도큐먼트가 통째로 교체된다.
- 응답으로 해당하는 다큐먼트 수(nMatched)와 수정된 다큐먼트 수(nModified)가 나온다.
8.5.4 Delete(삭제)
$ mongo
> db.users.remove({ name: 'nero' })
WriteResult({ 'nRemoved': 1 })
- 삭제할 도큐먼트에 대한 정보가 담긴 객체를 첫 번째 인수로 제공한다.
8.6 몽구스 사용하기
- MySQL에 시퀄라이즈가 있다면 몽고디비에는 몽구스(Mongoose)가 있다.
- MySQL은 relation을 사용하기 때문에 시퀄라이즈를 ORM이라고 불렀다.
- 몽구스는 ODM(Object Document Mapping)이라고 부른다.
- 몽고디비 자체가 이미 자바스크립트 인데 굳이 몽구스로 자바스크립트 객체로 매핑하는 이유는 더 편리해지기 때문이다.
- 몽고디비는 스키마가 없어 잘못된 값의 입력을 막을 수 없는데 몽구스는 스키마가 존재하기 때문에 이를 한번 필터링 할 수 있다.
- MySQL의 JOIN기능를 poplulate라는 메서드로 일부 보완할 수 있다.
// package.json
{
"name": "learn-mongoose",
"version": "0.0.1",
"description": "몽구스를 배우자",
"main": "app.js",
"scripts": {
"start": "nodemon app"
},
"author": "ZeroCho",
"license": "MIT"
}
- 몽구스와 필요한 패키지 설치
$ npm i express morgan nunjucks mongoose
$ npm i -D nodemon
8.6.1 몽고디비 연결하기
// schemas/index.js
const mongoose = require('mongoose');
- 노드와 몽고디비를 몽구스를 통해 연결해보자
const connect = () => {
if (process.env.NODE_ENV !== 'production') {
mongoose.set('debug', true);
}
- 개발 환경일 때만 콘솔을 통해 몽구스가 생성하는 쿼리 내용을 확인할 수 있게 하는 코드이다.
mongoose.connect('mongodb://이름:비밀번호@localhost:27017/admin', {
dbName: 'nodejs',
useNewUrlParser: true,
useCreateIndex: true,
}, (error) => {
if (error) {
console.log('몽고디비 연결 에러', error);
} else {
console.log('몽고디비 연결 성공');
}
});
};
- 몽구수는 몽고디비는 주소를 사용해 연결한다.
- mongodb://[username:password@]host[:port][/[database][?options]] ([ ] 부분은 있어도 되고 없어도 됨)
- 접속을 시도하는 주소의 데이터베이스는 admin이지만, 실제로 사용할 데이터베이스는 nodejs이므로 두 번째 인수로 dbName 옵션을 줘서 nodejs 데이터베이스를 사용하게 했다.
- useNewUrlParser: true와 useCreateIndex: true는 입력하지 않아도 되지만 콘솔에 경고 메시지가 뜨므로 넣었다.
- 마지막 인수로 주어진 콜백 함수를 통해 연결 여부를 확인한다.
mongoose.connection.on('error', (error) => {
console.error('몽고디비 연결 에러', error);
});
mongoose.connection.on('disconnected', () => {
console.error('몽고디비 연결이 끊겼습니다. 연결을 재시도합니다.');
connect();
});
module.exports = connect;
- 몽구스 커넥션에 이벤트 리스너를 단다. 에러 발생 시 에러 내용을 기록하고, 연결 종료 시 재연결을 시도한다.
- app.js를 만들고 schemas/index.js와 연결한다.
const connect = require('./schemas');
connect();
// app.js
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const nunjucks = require('nunjucks');
const connect = require('./schemas');
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((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'), '번 포트에서 대기 중');
});
8.6.2 스키마 정의하기
- 몽구스 스키마를 만들어보자.
// schemas/user.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
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);
- 몽구스 모듈에서 Schema 생성자를 사용해 스키마를 만든다.
- 몽구스는 알아서 _id를 기본 키로 생성하므로 _id 필드는 적어줄 필요가 없다.
- 몽구스 스키마에서 String, Number, Date, Buffer, Boolean, Mixed, ObjectId, Array를 값으로 가질 수 있다
- name 필드의 자료형은 String이고 필수이며 고유한 값이어야 합니다. age 필드는 Number 자료형이고 필수이며, married 필드는 불 값 자료형이고 필수이다.
// schemas/comment.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const { Types: { ObjectId } } = Schema;
const commentSchema = new Schema({
commenter: {
type: ObjectId,
required: true,
ref: 'User',
},
comment: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Comment', commentSchema);
- commenter 속성의 자료형이 ObjectId이다.
- 옵션으로 ref 속성의 값이 User로 주어져 있다.
- commenter 필드에 User 스키마의 사용자 ObjectId가 들어간다는 뜻입니다. 나중에 몽구스가 JOIN과 비슷한 기능을 할 때 사용된다.
- 몽구스는 model 메서드의 첫 번째 인수를 강제로 변형해서 컬렉션 이름을 만든다.
- 위 예시에서 Comment라면 comments 라는 이름으로 컬렉션이 만들어진다.
- 만약 강제 개명이 싫다면 아래처럼 하면 된다.
mongoose.model('User', userSchema, 'user_table');
- 이러면 users 컬렉션 대신 user_table 컬렉션이 생성된다.
8.6.3 쿼리 수행하기
- views 폴더 안에 mongoose.html과 error.html 파일을 만든다.
- https://github.com/zerocho/nodejs-book에서 코드를 복사한다.
// views/mongoose.html
<!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="/mongoose.js"></script>
</body>
</html>
// views/error.html
<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>
- public 폴더 안에 mongoose.js 파일도 만든다.
// 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 = '';
});
// 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/index');
const usersRouter = 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', 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'), '번 포트에서 대기 중');
});
// routes/index.js
const express = require('express');
const User = require('../schemas/user');
const router = express.Router();
router.get('/', async (req, res, next) => {
try {
const users = await User.find({});
res.render('mongoose', { users });
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
- GET /로 접속했을 때의 라우터이다.
- User.find({}) 메서드로 모든 사용자를 찾은 뒤, mongoose.html을 렌더링할 때 users 변수로 넣는다.
// 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 {
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 {
const comments = await Comment.find({ commenter: req.params.id })
.populate('commenter');
console.log(comments);
res.json(comments);
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
- GET /users와 POST /users 주소로 요청이 들어올 때의 라우터이다.
- GET /users에서는 데이터를 JSON 형식으로 반환한다
- 사용자를 등록할 때는 먼저 모델.create 메서드로 저장. 정의한 스키마에 부합하지 않는 데이터를 넣었을 때는 몽구스가 에러를 발생하며 _id는 자동으로 생성된다.
- GET /users/:id/comments 라우터는 댓글 도큐먼트를 조회하는 라우터이다.
- 먼저 댓글을 쓴 사용자의 아이디로 댓글을 조회한 뒤 populate 메서드로 관련 있는 컬렉션의 도큐먼트를 불러온다.
- Comment 스키마 commenter 필드의 ref가 User로 되어 있으므로 알아서 users 컬렉션에서 사용자 도큐먼트를 찾아 합친다.
- commenter 필드가 사용자 도큐먼트로 치환되는 것이다.
- 이제 commenter 필드는 ObjectId가 아니라 그 ObjectId를 가진 사용자 도큐먼트가 된다.
- 즉 사용자의 아이디로 댓글들을 조회한 후에 popluate로 commenter 필드를 해당 사용자 도큐먼트로 치환한다.
// routes/comments.js
const express = require('express');
const Comment = require('../schemas/comment');
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);
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 {
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 {
const result = await Comment.remove({ _id: req.params.id });
res.json(result);
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
- POST /comments 라우터 : populate 메서드로 comment 객체에 다른 컬렉션 도큐먼트를 불러온다. path 옵션으로 어떤 필드를 합칠지 설정하면 된다.
- 합쳐진 결과를 클라이언트로 응답한다.
- PATCH /comments/:id 라우터 : 다큐먼트를 수정. update 메서드를 사용한다. 몽고디비와 다르게 $set 연산자를 사용하지 않아도 기입한 필드만 바꾼다.
- DELETE /comments/:id : remove 메서드에게 어떤 다큐먼트를 삭제할지 조건을 첫 번째 인수로 넣는다.
- 몽고디비 서버를 먼저 실행(mongod --auth)하고 콘솔을 하나 더 열어 웹 서버를 실행해보자.
$ npm start
> learn-mongoose@0.0.1 start C:\Users\zerocho\learn-mongoose
> nodemon app
[nodemon] 2.0.3
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
3002 번 포트에서 대기 중
Mongoose: users.createIndex({ name: 1 }, { unique: true, background: true })
몽고디비 연결 성공
npm start로 서버를 실행한 후 http://localhost:3002에 접속하여 확인해보자.
'ComputerScience > NodeJs' 카테고리의 다른 글
node - 9.2 익스프레스로 SNS 서비스 만들기(SQL DB) (0) | 2022.02.05 |
---|---|
node - 9.1 익스프레스로 SNS 서비스 만들기(프로젝트 구조) (0) | 2022.02.05 |
node - 7.6 시퀄라이즈 사용하기 (0) | 2022.01.31 |
node - 7 MySQL (0) | 2022.01.29 |
node - 6.5 템플릿 엔진 사용하기(nunjucks) (0) | 2022.01.28 |