엘리스 ai트랙

Mongo DB & Mongoose

라리라밤 2022. 2. 21. 12:39

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