티스토리 뷰
Mongoose ODM
▶ Mongoose ODM 이란?
MongDB의 Collection에 집중하여 관리하도록 도와주는 패키지
→ MongoDB의 Collection을 JavaScript의 model로써 만들어놓고
이 model을 통해 MongoDB의 데이터들에 쉽게 접근하고 관련 기능들을 쉽게 사용할 수 있도록 도와줌
* ODM (Object Data Modeling) : Data를 Object로 Model화하여 관리
▶ Mongoose를 사용하는 이유
1) 연결관리
: MongoDB에서 기본 제공하는 Node.js 드라이버는 연결상태를 관리하기가 어려움
→ Mongoose를 사용하면 간단하게 데이터배이스와의 연결상태를 관리 가능
2) 스키마 관리
: 스키마를 정의하지 않고 데이터를 사용할 수 있는 것이 NoSQL의 장점이지만 데이터 형식을 미리 정의하는 것이 코드작성과 프로젝트 관리에 유용
→ Mongoose는 Code-Level에서 스키마를 정의하고 관리 가능
3) Populate
: MongoDB는 기본적으로 Join을 제공하지 않아서 Join과 유사한 기능을 사용하기 위해 aggregate라는 복잡한 쿼리 필요
→ Mongoose는 populate를 사용하여 Join 기능을 간단하게 구현 가능
* Populate 란?
: 스키마 선언 시 Document 안에 Document를 담지 않고 ObjectID를 가지고 reference하여 사용할 수 있는 방법을 제공
→ Document에는 reference되는 ObjectID를 담고 사용할 때 populate하여 하위 Document처럼 사용 가능
const Post = new Schema({
...,
user: { // 단일 객체 reference 가능
type: Schema.Types.ObjectId,
ref: 'User' // User모델 선언 시 전달했던 모델의 이름
},
comments: [{ // array로도 reference 가능
type: Schema.Types.ObjectId,
ref: 'Comment', // Comment모델 선언 시 전달했던 모델의 이름
}],
});
// ObjectId 사용
const post = await Post.find().populate(['user', 'comments']);
// post.user.name, post.comments[0].content 처럼 사용 가능
▶ Mongoose 사용하기
1) 스키마 정의
- Collection에 저장될 Document의 스키마를 Code-Level에서 관리할 수 있도록 Schema 작성
- 다양한 형식을 미리 지정하여 생성, 수정 작업 시 데이터 형식을 체크해주는 기능 제공
- timestamps 옵션을 사용하면 생성, 수정 시간을 자동으로 기록
const { Schema } = require('mongoose');
const PostSchema = new Schema({
title: String,
content: String,
}, {
timestamps: true,
});
module.exports = PostSchema;
2) 모델 만들기
- 작성된 스키마를 mongoose에서 사용할 수 있는 모델로 생성
- 모델의 이름을 지정하여 Populate 등에서 해당 이름으로 모델 호출
const mongoose = require('mongoose');
const PostSchema = require('./schemas/board');
exports.Post = mongoose.model('Post', PostSchema);
3) 데이터베이스 연결
- connect() 를 이용하여 데이터베이스에 연결
- mongoose는 connect 함수를 사용했을 때 자동으로 연결을 관리해줌
→ 직접 연결 상태를 체크하지 않아도 모델 사용 시 연결 상태를 확인하여 사용이 가능할 때 비동기적으로 작업을 실행
const mongoose = require('mongoose');
const { Post } = require('./models');
mongoose.connect('mongodb://localhost:27017/myapp');
// Post 바로 사용 가능
4) 모델 사용
- 작성 된 모델을 이용하여 CRUD 수행
CRUD | 함수명 |
CREATE | create |
READ | find, findById, findOne |
UPDATE | updateOne, updateMany, findByIdAndUpdate, findOneAndUpdate |
DELETE | deleteOne, deleteMany, findByIdAndDelete, findOneAndDelete |
CREATE
: create() 함수를 사용하여 Document 생성
'Document Object(단일 Document 생성)'나 'Document Object의 Array(복수 Document 생성)' 전달 가능
→ create는 생성된 Document를 반환
const { Post } = require('./models');
async function main() {
const created = await Post.create({ // Document Object : 단일 Document 생성
title: 'first title',
content: 'second title',
});
const multpleCreated = await Post.create([ // Document Object의 Array : 복수 Document 생성
item1,
item2
]);
}
FIND (READ)
: find 관련 함수를 사용하여 Document 검색
query를 사용하여 검색하거나 findById를 사용하면 ObjectID로 Document를 검색 가능
const { Post } = require('./models');
async function main() {
const listPost = await Post.find(query); // 인자 : query / 반환 : array
const onePost = await Post.findOne(query); // 인자 : query / 반환 : 하나의 document
const postById = await Post.findById(id); // 인자 : objectID / 반환 : 하나의 document
}
* query 란?
- MongoDB에도 SQL의 where와 유사한 조건절 사용 가능
- MongoDB의 query는 BSON 형식으로, 기본 문법 그대로 mongoose에서도 사용 가능
≫ query 관련 MongoDB 메뉴얼
≫ MongoDB query operator
* 자주 사용되는 query
- { key: value } 로 exact match
- $lt, $lte, $gt, $gte 를 사용하여 range query 작성 가능
- $in 을 사용하여 다중 값으로 검색
- $or 를 사용하여 다중 조건으로 검색
Person.find({
name: 'kyubum',
age: {
$lt: 20,
$gte: 10,
},
languages: {
$in: ['ko', 'en'],
},
$or: [
{ status: 'ACTIVE' },
{ isFresh: true },
],
});
// 참고 : Mongoose는 쿼리 값으로 배열이 주어지면 자동으로 $in 쿼리를 생성해 줌
Person.find({ name: ['elice', 'bob'] });
// { name: { $in: ['elice', 'bob'] } }
UPDATE
: update 관련 함수를 사용하여 Document 수정
find~ 함수들은 검색된 Document에 업데이트를 반영하여 반환
mongoose의 update는 기본적으로 $set operator를 사용하여 Document를 통째로 변경하지 않음
(MongoDB의 update 기능은 해당되는 Document가 통째로 변경됨)
async function main() {
const updateResult = await Post.updateOne(query, { // update 결과 리턴
...
});
const updateResults = await Post.updateMany(query, { // update 결과 리턴
...
});
const postById = await Post.findByIdAndUpdate(id, { // 검색된 Document에 update를 반영하여 Document 리턴
...
});
const onePost = await Post.findOneAndUpdate(query, { // 검색된 Document에 update를 반영하여 Document 리턴
...
});
}
DELETE
: delete 관련 함수를 사용하여 Document 삭제
find~ 함수들은 검색된 Document를 반환
async function main() {
const deleteResult = await Post.deleteOne(query);
const deleteResults = await Post.deleteMany(query);
const onePost = await Post.findOneAndDelete(query);
const postById = await Post.findByIdAndDelete(query);
}
▶ Express.js에서 Mongoose 사용하기
Express.js는 프로젝트 구조를 자유롭게 구성할 수 있기 때문에
어느 부분에 Mongoose ODM을 위치시키면 좋을지 적절한 위치를 결정하는 것이 중요
- 일반적으로 models 디렉터리에 Schema와 Model을 같이 위치
- app객체는 어플리케이션 시작을 의미하는 부분이므로 app객체가 선언되는 부분에 데이터베이스 연결을 명시하는 mongoose.connect를 위치
Mongoose 커넥션 이벤트
: Express.js 어플리케이션은 종료되지 않고 동작하기 때문에 계속해서 데이터베이스가 정상적으로 동작하는지를 파악하기 위해 동작 중에 발생하는 데이터베이스 연결 관련 이벤트에 대한 처리를 하는 것이 좋음
connected - 연결 완료
disconnected - 연결 끊김
reconnected - 재연결 완료
reconnectFailed - 재연결 시도 횟수 초과
mongoose.connect('----');
mongoose.connection.on("connected", () => {
console.log("Successfully connected to MongoDB");
})
mongoose.connection.on('disconnected', () => {
...
});
mongoose.connection.on('reconnected', () => {
...
});
mongoose.connection.on('reconnectFailed', () => {
...
});
+ 참고) Sequelize ORM
▶ Sequelize ORM 이란?
MySQL, PostgreSQL 등의 RDBMS를 이용하는 간단한 방법
→ ODM이 단순히 모델에 집중하여 관리하는 것에 반해 ORM은 테이블 관계와 쿼리 등의 기능을 더욱 단순화하는 용도로 주로 사용
- Sequelize ORM 을 사용하면 데이터베이스에 직접 DDL을 하지 않고 JavaScript 코드로 테이블 및 관계를 관리할 수 있음
- 또한 RDB의 어려운 점 중 하나인 join을 간단하게 사용할 수 있음
* ORM (Object-Relational Mapping) : Object와 Relation을 Mapping
1) DB 연결
- sequelize도 Mongoose처럼 연결을 관리하는 간단한 방법을 제공
- mongoose가 MongoDB만 연결이 가능한 데에 반해 sequelize는 MySQL, PostgreSQL, SQLite 등 다양한 RDBMS 에 연결 가능
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
});
2) 스키마 작성
- sequelize는 define을 통해 Schema를 생성
- mongoose.Schema와 유사하지만 sequelize는 Schema가 DDL도 생성해줌
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING(10),
allowNull: false
},
age: {
type: DataTypes.Integer,
}
}, {});
3) 관계 정의
- sequelize를 이용하면 테이블 간의 관계를 Code-Level로 관리 가능
- 이를 이용하면 외래키 설정과 제약조건까지 DDL로 자동으로 생성해줌
- 또한 다대다 관계 설정을 통해 relation table도 자동으로 생성해줌
User.hasMany(Post);
Post.belongsTo(User);
Foo.belongsToMany(Bar);
Bar.belongsToMany(Foo);
4) 쿼리
- Operator를 이용해 SQL 쿼리를 코드로 작성 가능
- 스키마의 관계 설정을 한 경우, include를 사용하여 자동으로 join 쿼리 생성 가능
User.findAll({
where: {
name: 'elice',
age: {
[Op.lt]: 20,
[Op.gte]: 10,
},
},
include: User,
});
5) Synchronization
- define된 model 데이터를 바탕으로 DDL을 자동으로 실행해줌
- 직접 데이터베이스에 접속하여 테이블 생성 및 관리를 할 필요가 없음
- 자동으로 생성된 DDL을 따르지 않으면 테이블 관리가 어려워짐
sequelize.sync();
이 글은 엘리스 AI트랙 5기 강의를 들으며 정리한 내용입니다.
'개발공부 > 🟩 Node.js' 카테고리의 다른 글
[NodeJS] Express와 MongoDB로 웹서비스 만들기 - 1 (2) (0) | 2022.08.02 |
---|---|
[NodeJS] Express와 MongoDB로 웹서비스 만들기 - 1 (1) (0) | 2022.08.02 |
[NodeJS] Node.js와 MongoDB (0) | 2022.07.31 |
[NodeJS] Express.js로 REST API 구현하기 (0) | 2022.07.30 |
[NodeJS] Express.js의 Middleware (0) | 2022.07.28 |
프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그