Mongo DB & Mongoose
1. MongoDB
= 대표적인 NoSQL, Document DB
humongous의 mongo. 대용량 데이터를 처리할 수 있다.
1. NoSQL
- = Not Only SQL
- sql 사용 안함
- 데이터를 유연하게 저장
RDB
- 관계형 db
- sql 을 사용
- 데이터가 구조화되어 있음.
→ NoSQl 을 사용하는 이유 : 사전작업 없이 db를 사용할 수 있어 프로젝트 진행이 빠르다
2. Document DB : 데이터를 Document로 저장한다.
Database > Colletion > Document
Database
Collection : SQL 에서의 table.
- document 의 구조를 정의하지 않는다.
- 하나 이상의 document를 저장.
Document : mongodb에 저장되는 자료.
- BSON 자료형을 사용하고, BSON 안에 다양한 자료형을 넣을 수 있다.
ObjectID : timestamp + random value + auto increament
- 각 document의 유일한 키 값, SQL 의 primary key와 유사.
- document 를 생성할 때 자동으로 생성된다.
2. Mongoose ODM
ODM = Object Data Modeling
: MongoDB의 Collection 을 모델화( 자바스크립트의 모델 )하여 관련 기능들을 쉽게 사용하고 관리할 수 있게 해주는 패키지
① db와 연결 관리 : Mongoose 로 Mongo DB 와 연결하기가 쉽다.
- Mongo DB 의 기본 Node.js 드라이버로 연결상태를 관리하기 힘들다.
② 스키마 관리 : 스키마( 데이터 형식 )를 정의, 관리 할 수 있다.
③ Populate : join과 유사한 기능을 간단하게 구현할 수 있다.
- MongoDB는 Join을 제공하지 않고, aggregate 쿼리를 해야 한다.
3. Mongoose ODM 사용하기 : 스키마 정의 - 모델 생성 - db 연결 - 모델 사용
1. 스키마 정의
const { Schema } = require('mongoose')
const dataSchema = new Schema({
name : {type:String, required:true},
content : {type:String, required:true},
}, {
timestamps : true,
})
module.exports = dataSchema
** timestamps : 생성, 수정시간을 자동으로 기록해준다.
** schema 생성 후 export 해야함!
2. 모델 만들기 : 작성한 schema를 mongoose에서 사용할 수 있는 모델로 만들어야 함.
const mongoose = require('mongoose')
const dataSchema = require('./schemas/data')
exports.data = mongoose.model('Data', dataSchema)
** mongoose.model() 로 모델의 이름을 지정하고, schema를 줘야 함.
-> populate 에서 'Data' 에서 준 이름으로 모델을 호출함.
// 모델화 하면서 동시에 export 가능
module.exports = mongoose.model('Data', dataSchema)
3. 데이터베이스 연결
const mongoose = require('mongoose')
const { Data } = require('./models')
mongoose.connect('mongodb://localhost:27017/myproject')
** 자동으로 연결을 관리해준다. -> 모델 사용시 연결상태를 확인하여 사용가능할 때 작업을 실행한다.
4. 모델 사용 : CRUD 함수 제공
CREATE | create |
READ | find, findById, findOne |
UPDATE | updateOne, updateMany, findByIdAndUpdate, findOneAnd Update |
DELETE | deleteOne, deleteMany, findByIdAndDelete, findOneAndDelete |
1. create : 여러 document도 생성가능하다. 생성된 document를 반환한다.
const { Data } = require('./models')
async function main(){
// document object 전달
const created = await Data.create({
title: 'title',
content : 'hello'
})
// document object의 배열을 전달 -> 여러 document 생성
const multipleCreated = await Data.create([
item1,
item2
])
}
2. find
const { Data } = require('./models')
async function main() {
// 배열을 반환
const listData = await Data.find(query)
// 하나의 document를 반환
const oneData = await Data.findOne(query)
// 하나의 document를 반환, ObjectId 로 검색
const postById = await Data.findById(id)
}
3. update :
async funtion main() {
// 쿼리의 결과를 반환
const updateResult = await Data.updateOne(query, {...})
const updateResults = await Data.updateMany(query, {...})
// 검색된 Document를 업데이트하여 반환
const postById = await Data.findByIdAndUpdate(id, {...})
const onePost = await Data.findOneAndUpdate(query, {...})
}
** 기본적으로 $set operator를 사용 -> 있는 속성은 변경, 없는 속성은 추가한다. (Document를 통째로 변경하지 않는다.)
4. delete
async function main() {
const deleteResult = await Data.deleteOne(query)
const deleteResults = await Data.deleteMany(query)
const onePost = await Data.findOneAndDelete(query)
const postById = await Data.findByIdAndDelete(query)
}
5. populate : Document에 reference 되는 objectId 를 담고, 사용할 때 populate 하면 하위 Document 처럼 사용할 수 있다.
const Post = new Schema({
user:{
type : Schema.Types.ObjectId,
ref : 'User',
},
comments : [{
type : Schema.Types.ObjectId,
ref : 'Comment',
}],
})
const post = await Post.find().populate(['user', 'comments'])
populate 하면 post.user.name, post.comments[0].content 처럼 사용할 수 있다.
5. Query
: MongoDB 와 같은 BSON 형식을 mongoose에서 그대로 사용가능하다.
① exact match : { key : value }
② range query : $lt, $lte, $gt, $gte
③ $in : 다중 값으로 검색
** (mongoose) 쿼리 값으로 배열을 주면 자동으로 $in 쿼리가 적용됨.
④ $or : 다중 조건 검색
async function main() {
const posts = await Post.find({
'author' : ['a', 'b', 'c'],
'likes' : {
$gt:1,
$lte:3
},
$or : [
{category : {$exists:false}},
{category : 'notice'}
]
})
return posts
}
4. Express.js + Mongoose
1. mongoose ODM 의 위치 - express.js 에서 프로젝트 구조는 자유롭지만 적절한 위치를 정하는 것이 좋다.
myProject - app.js // mongoose.connect
- /models - index.js // mongoose.model 선언
- /schemas // mongoose.Schema 정의
2. express.js 는 종료되지 않고 동작하기 때문에 db와 연결상태를 확인하기 위해 db 연결 이벤트 처리를 하는 것이 좋다.
Mongoose ODM 커넥션 이벤트
mongoose.connect('...')
// 연결 완료
mongoose.connection.on('connected', () => {})
// 연결이 끊김
mongoose.connection.on('disconnected', () => {})
// 재연결 완료
mongoose.connection.on('reconnected', () => {})
// 재연결 시도 횟수 초과
mongoose.connection.on('reconnectFailed', () => {})
5. Sequelize ORM - node.js 에 RDB 연결할 때 사용하면 좋다.
ORM = Object Relational Mapping
: RDBMS 를 이용하는 간단한 방법
db에 직접 DDL을 하지 않고, js 코드로 테이블, 관계를 관리가능.
join 을 간단히 사용가능
1. ODM v.s ORM
ODM : 단순히 모델에 집중하여 관리.
ORM : 테이블관계, 쿼리 등의 기능을 더욱 단순화
2. db 연결
mongoDB 를 포함하여 다양한 RDBMS 에도 연결가능하다.
// db 연결
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
})
// 스키마 작성 - define
const User = sequelize.define('User', {
name : {
type: DataTypes.STRING(10),
allowNull: false
},
age:{
type: DataTypes.Integer,
}
}, {})
// 스키마 작성 - 관계정의
User.hasMany(Post)
Post.belongsTo(User)
Foo.belongsToMany(Bar)
Bar.beongsToMany(Foo)
// query
User.findAll({
where: {
name: 'elice',
age: {
[Op.lt] : 20,
[Op.gte] : 10,
},
},
// 스키마 관계설정한 경우, include로 join.
include: User,
})
// define 된 model 데이터를 바탕으로 DDL을 자동실행
sequelize.sync() // db에 접속하여 테이블 생성,관리할 필요없음, 자동 생성된 DDL을 따르지 않으면 테이블관리가 어려워진다..
6. Template Engine
: 템플릿 작성 문법, 작성된 템플릿을 HTML 로 변환하는 기능. ==> SSR 구현 방식( 서버에서 데이터를 조합하여 HTML을 작성하고 HTTP Response 로 전송하는 기능. )
① 서버에서 클라이언트로 보낼 HTML의 형태를 미리 템플릿으로 저장
② 동작할 때 템플릿에 데이터를 넣어 완성된 HTML을 보냄.
1. 종류
① EJS : html 과 유사한 문법
② Mustache : 간단한 데이터 치환정도만 제공하는 경량화된 템플릿 엔진
③ Pug : 들여쓰기 표현식을 이용한 표기, 레이아웃 제공.
2. Pug :
① 닫기 태그 없음.
② 들여쓰기 표현식 -> 가독성이 좋고 html 을 잘 몰라도 된다.
③ = 으로 전달받은 변수 사용
④ id, class 는 태그 뒤에 이어서 바로 사용
⑤ attribute는 () 로 사용.
⑥ layout, include, mixin 등 기능을 제공한다.
html
head
title = title
body
h1#greeting 안녕하세요
a.link(href='/')
3. pug - express.js 연동
// app.js
app.set('views',
path.join(__dirname, 'views'))
app.set('view engine', 'pug')
//request handler
res.render('main', {
title: 'Hello Express'
})
app.set( ) : 템플릿이 저장되는 디렉토리 지정, 엔진 지정.
res.render( 템플릿 이름, 템플릿에 전달할 값 ) : app.set 에 지정된 값을 이요해 화면을 그리는 기능.
$ express --veiw=pug myproject