배꼽파지 않도록 잘 개발해요

[엘리스sw] 6주차 5일 - MongoDB와 Mongoose 본문

교육/엘리스 SW 학습 내용

[엘리스sw] 6주차 5일 - MongoDB와 Mongoose

꼽파 2024. 2. 4. 22:02


◆  MongoDB

◆  MongoDB ODM

◆  MongoDB ODM 사용하기

◆  Sequelize ORM


MongoDB

MongoDB란?
· 대표적인 NoSQL, Document DB
· Mongo는 Humongous에서 따온 말로, 엄청나게 큰 DB라는 의미
→ 대용량 데이터를 처리하기 좋게 만들어짐.

 

RDB와 NoSQL

RDB NoSQL
Relational Database Non SQL 또는 Not Only SQL
관계형 데이터베이스 구조화된 질의어를 사용하지 않는 데이터베이스
자료들의 관계를 주요하게 다룸. 자료 간의 관계에 초점을 두지 않음.
SQL 질의어를 사용하기 위해 데이터를 구조화해야 함. 데이터를 구조화하지 않고, 유연하게 저장함.


NoSQL을 사용하는 이유

  • SQL을 사용하기 위해서는 데이터를 구조화하는 것이 필수 (DDL)
    → 스키마에 정의된 데이터가 아니면 저장할 수 없는 제약이 따름.
  • NoSQL을 사용하면 사전작업 없이 데이터베이스를 사용할 수 있음.
    → 데이터베이스 작업에 크게 관여하지 않고 프로젝트를 빠르게 진행할 수 있음.

NoSQL과 Document DB
· NoSQL은 다양한 종류가 있지만, 대표적으로 자료를 Document(문서)로 저장하는 Document DB가 일반적
· 이외에 key-value, Graph, large collection 등의 NoSQL DB가 존재

 

MongoDB 기본 개념

  • Database > Collection > Document
Database 하나 이상의 collection을 가질 수 있는 저장소
SQL에서의 database와 유사함
Collection 하나 이상의 Document가 저장되는 공간
SQL에서의 table과 유사
하지만 collection이 document의 구조를 정의하지 않음.
Document MongoDB에 저장되는 자료, SQL에서 row와 유사하지만 구조제약 없이 유연하게 저장 가능함.
JSON과 유사한, BSON을 사용하여 다양한 자료형을 지원함.

 

MongoDB 기본 개념 - Document - ObjectID

  • Document > ObjectID

· 각 document의 유일한 키 값, SQL의 primary key와 유사함.

· 하나씩 증가하는 값이 아닌 document를 생성할 때 자동으로 생성되는 값

* timestamp + random value + auto increment

 

MongoDB 사용 방법

MongoDB를 직접 설치하거나 Cloud 서비스를 사용할 수 있음.

  • 직접 설치 : 귀찮고 어렵지만 원하는 만큼 얼마든지 데이터를 사용할 수 있음.
  • Cloud를 사용 : 쉽고 빠르게 시작 가능하지만, 사용량에 따라 요금이 부과됨.

직접 MongoDB 설치하기

· 직접 모든 데이터베이스 관련 설정을 해야함.

· Sharding이나 Replication 등의 작업이 필요할 때 운영지식과 노하우가 요구됨.

· 무료로 사용할 수 있는 Community Version을 제공함.

 

MongoDB Cloud 이용하기

· 모든 데이터베이스 관련 기능을 웹에서 관리 가능

· 특별한 노하우 없이 데이터베이스 운용 가능

· 사용량에 따라 비용이 발생하지만, 512MB까지는 평생 무료로 사용 가능

 

MongoDB 사용방법 - MongoDB Compass

· MongoDB에 접속하여 Database, Collection, Document 등을 시각화하여 관리할 수 있게 도와주는 도구

· MySQL을 사용할 때 MySQL Workbench와 유사함.

 

컴퓨터가 실행되면 항상 MongoDB 서버가 실행된다는 의미임.

설정 변경 가능함.

 

https://www.mongodb.com/try/download/community


MongoDB ODM

 

MongoDB ODM이란?

Object Data Modeling

MongoDB의 collection에 집중하여 관리하도록 도와주는 패키지

Collection을 모델화하여, 관련 기능들을 쉽게 사용할 수 있도록 도와줌.

 

Mongoose ODM을 사용하는 이유

연결관리

MongoDB의 기본 Node.js 드라이버는 연결상태를 관리하기 어려움.

Mongoose를 사용하면 간단하게 데이터베이스와의 연결상태를 관리해줌.

 

스키마 관리

스키마를 정의하지 않고 데이터를 사용할 수 있는 것은 NoSQL의 장점이지만, 

데이터 형식을 미리 정의해야 코드 작성과 프로젝트 관리에 유용함.

Mongoose는 Code-Level에서 스키마를 정의하고 관리할 수 있게 해줌.

 

Populate

MongoDB는 기본적으로 Join을 제공하지 않음.

Join과 유사한 기능을 사용하기 위해선 aggregate라는 복잡한 쿼리를 해야하지만,

Mongoose는 populate를 사용하여 간단하게 구현할 수 있음.


MongoDB ODM 사용하기

Mongoose ODM 사용 순서

1. 스키마 정의

2. 모델 만들기

3. 데이터베이스 연결

4. 모델 사용

 

스키마 정의하기

  • Collection에 저장될 Document의 스키마를 Code-Level에서 관리할 수 있도록 Schema를 작성할 수 있음.
  • 다양한 형식을 미리 지정하여, 생성, 수정 작업 시 데이터 형식을 체크해주는 기능을 제공함.
  • timestamps 옵션을 사용하면 생성, 수정 시간을 자동으로 기록해 줌.
// ./models/schemas/board.js

const { Schema } = require('mongoose');

const PostSchema = new Schema({
	title: String,
    content: String.
}, {
	timestamps: true,
});

module.exports = PostSchema;

 

모델 만들기

  • 작성된 스키마를 mongoose에서 사용할 수 있는 모델로 만들어야 함.
  • 모델의 이름을 지정하여 Populate 등에서 해당 이름으로 모델을 호출할 수 있음.
// ./models/index.js

const { Schema } = require('mongoose');

const PostSchema = new Schema({
	title: String,
    content: String.
}, {
	timestamps: true,
});

module.exports = PostSchema;

 

데이터베이스 연결하기

  • connect 함수를 이용하여 간단하게 데이터베이스에 연결할 수 있음.
  • mongoose는 자동으로 연결을 관리해 주어 직접 연결 상태를 체크하지 않아도 모델 사용 시 연결 상태를 확인하여 사용이 가능할 때 작업을 실행함.
// index.js

const mongoose = require('mongoose');

const { Post } = require('./mogdels');

mongoose.connect('mongodb://localhost:27017/myapp');

// Post 바로 사용 가능

 

모델 사용하기 - 간단한 CRUD

작성된 모델을 이용하여 CRUD를 수행할 수 있음.

  • CREATE : create
  • READ : find, findById, findOne
  • UPDATE : updateOne, updateMany, findByIdAndUpdate, findOneAndUpdate
  • DELETE : deleteOne, deleteMany, findByIdAndDelete, findOneAndDelete

 

간단한 CRUD - CREATE

  • create 함수를 사용하여 Document 생성
  • create 함수에는 Document Object(단일 Document 생성)나 Document Object의 Array(복수 Document 생성) 전달 가능
  • create는 생성된 Document를 반환해 줌.
// index.js

const { Post } = require('./models');

async function main() {
	const created = await Post.create({
    	title: 'first title',
        content: 'second title',
    });
    
    const multipleCreated = await Post.create([
    	item1,
    	item2
    ]);
}

 

간단한 CRUD - FIND (READ)

  • find 관련 함수를 사용하여 Document를 검색
  • query를 사용하여 검색하거나 findById를 사용하면 ObjectID로 Document를 검색할 수 있음.
// index.js

const { Post } = require(./models');

async function main() {
	const listPost = await Post.find(query);
    const onePost = await Post.findOne(query);
    const postById = await Post.findById(id);
}

 

Query

  • MongoDB에도 SQL의 where와 유사한 조건절 사용 가능
  • MongoDB의 query는 BSON 형식으로, 기본 문법 그대로 mongoose에서도 사용 가능

 

자주 사용되는 query

  • { key: value }로 exact match
  • $It(less than), $Ite(less than equal), $gt(greater than), $gte(greater than equal)를 사용하여 range query 작성 가능
  • $in을 사용하여 다중 값으로 검색, $or를 사용하여 다중 조건 검색
Person.find({
	name: 'kyubum',
    age: {
    	$It: 20,
    	$gte: 10,
    },
    languages: {
    	$in: ['ko','en'],
    },
    $or: [
    	{ status: 'ACTIVE' },
        { isFresh: true },
    ],
});

 

Mongoose ODM - $in

Mongoose는 쿼리 값으로 배열이 주어지면 자동으로 $in 쿼리를 생성해 줌.

Person.find({ name: ['elice', 'bob'] });
// { name: { $in: ['elice', 'bob'] }}

 

MongoDB Query Operators

MongoDB 홈페이지에서 다양한 Query Operator들 확인 가능

https://www.mongodb.com/docs/manual/reference/operator/query/

 

Query and Projection Operators — MongoDB Manual

Docs Home → MongoDB Manual For details on a specific operator, including syntax and examples, click on the link to the operator's reference page.You can use query and projection operators for deployments hosted in the following environments:MongoDB Atlas

www.mongodb.com

 

간단한 CRUD - UPDATE

  • update 관련 함수를 사용하여 Document 수정
  • find~ 함수들은 검색된 Document 를 업데이트를 반영하여 반환해 줌.
  • mongoose의 update는 기본적으로 $set operator를 사용하여, Document를 통째로 변경하지 않음.
// index.js
async function main() {
	const updateResult = await Post.updateOne(query, {
    	...
    });
    const updateResults = await Post.updateMany(query, {
    	...
    });
    const postById = await Post.findByIdAndUpdate(id, {
    	...
    });
    const onePost = await Post.findOneAndUpdate(query, {
    	...
    });
}

 

간단한 CRUD - DELETE

  • delete 관련 함수를 사용하여 Document 삭제
  • find~ 함수들은 검색된 Document를 반환해 줌.
// index.js
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);
}

 

populate

  • 참조 필드의 값을 실제로 가져오기 위해 다른 컬렉션을 조인하는 역할을 함.
  • Document 안에 Document를 담지 않고, ObjectID를 가지고 reference하여 사용할 수 있는 방법을 제공함.
  • 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']);
    
// post.user.name, post.comments[0].content

 

populate 활용 파일 구조 이해하기

  • models 안
    - schemas 안에 post, user 스키마 생성
    - index.js : post스키마로 post 모델, user 스키마로 user 모델 생성
  • index.js : post 스키마만 참조함. (post 스키마가 이미 user 모델 참조해서 populate 가능)
  author: {
      type: Schema.Types.ObjectId,  // 이거 변경 X 고정임
      ref: 'User',  // 따옴표 꼭 쓰기
      required: true,
  }

populate() 메소드를 사용하면 Post 컬렉션에서 조회한 문서들의 author 필드가 실제 User 컬렉션에서 해당 사용자의 정보로 대체되어 반환되는 것임.


Express.js + Mongoose

Express.js에서 Mongoose ODM 사용하기

Express.js는 프로젝트 구조를 자유롭게 구성할 수 있기 때문에 어느 부분에 Mongoose ODM을 위치시키면 좋을지 적절한 위치를 결정하는 것이 중요함.

 

Mongoose ODM 위치 정하기

  • 일반적으로 models 디렉터리에 Schema와 Model을 같이 위치
  • app 객체는 어플리케이션 시작을 의미하는 부분이므로 해당 부분에 데이터베이스 연결을 명시하는 mongoose.connect를 위치함.

출처 : 엘리스SW트랙 강의

 

Mongoose ODM 커넥션 이벤트

Express.js 어플리케이션은 종료되지 않고 동작하기 때문에 계속해서 데이터베이스가 정상적으로 동작하는지를 파악하기 위해 동작중에 발생하는 데이터베이스 연결 관련 이벤트에 대한 처리를 하는 것이 좋음.

  • connected : 연결 완료
  • disconnected : 연결이 끊김
  • reconnected : 재연결 완료
  • reconnectFailed : 재연결 시도 횟수 초과
mongoose.connect('----');

mongoose.connection.on('connected', () => {
});

mongoose.connection.on('disconnected', () => {
});

mongoose.connection.on('reconnected', () => {
});

mongoose.connection.on('reconnectFailed', () => {
});

Sequelize ORM

Sequelize ORM

  • Object-Relational Mapping
  • MySQL, PostgreSQL 등의 RDBMS를 이용하는 간단한 방법
  • ODM이 단순히 모델에 집중하여 관리하는 것에 비해,
  • ORM은 테이블 관계와 쿼리 등의 기능을 더욱 단순화하는 용도로 주요 사용

Sequelize ORM 사용하기

DB 연결

  • sequelize도 연결을 관리하는 간단한 방법을 제공
  • mongoose가 MongoDB만 연결이 가능한 데에 비해, 
  • sequelize는 MySQL, PostgreSQL, SQLite 등 다양한 RDBMS에 연결 가능함.
const sequelize = new Sequelize('database', 'username', 'password', {
	host: 'localhost',
    dialect: 'mysql'
});

 

스키마 작성

  • sequelize는 define을 통해 schema를 생성
  • mongoose.Schema와 유사하지만 sequelize는 schema가 DDL도 생성해 줌.
const User = sequelize.define('User', {
	name: {
    	type: DataTypes.STRING(10),
        allowNull: false
    },
    age: {
    	type: DataTypes.Integer,
    }
}, {});

 

스키마 작성 - 관계 정의

  • sequelize을 이용하면 테이블 간의 관계Code-Level로 관리할 수 있음.
  • 이를 이용하면 외래키 설정제약조건까지 DDL로 생성해 줌.
  • 또한 다대다 관계 설정을 통해 relation table로 자동으로 생성해 줌.
User.hasMany(Post);
Post.belongsTo(User);
Foo.belongsToMany(Bar);
Bar.belongsToMany(Foo);

 

쿼리

  • Operator를 이용해 SQL 쿼리를 코드로 작성 가능
  • 스키마의 관계 설정을 한 경우, include를 사용하여 자동으로 join 쿼리 생성 가능
User.findAll({
	where: {
    	name: 'elice',
        age: {
        	[Op.lt]: 20,
            [Op.gte]: 10,
        },
    },
    include: User,
});

 

Synchronization

definde된 model 데이터를 바탕으로 DDL을 자동으로 실행해 줌.

→ 직접 데이터베이스에 접속하여 테이블 생성 및 관리를 할 필요가 없음.

→ 자동으로 생성된 DDL을 따르지 않으면 테이블 관리가 어려워짐.

sequelize.sync();

 

Sequelize ORM 정리

  • Sequelize ORM을 사용하면 DB에 직접 DDL을 하지 않고 JS 코드로 테이블 및 관계를 관리할 수 있음.
  • 또한 RDB의 어려운 점 중 하나인 join을 간단하게 사용할 수 있음.
728x90