Today I Learned/TIL 06

2023 - 06 - 17 MongoDB, Mongoose, 몽구스, Schema 스키마, model, populate, Queries 쿼리

sangwoo_rhie 2023. 6. 17. 11:33

 
NoSQL 데이터베이스로 분류되는 MongoDB는 가장 유명한 NoSQL데이터베이스 시스템이다.
 
Mongoose

 
Mongoose는 Node.js와 MongoDB를 연결해주는 ODM이다.
**ODM(Object Document Mapping) : 객체와 문서를 1대1로 매칭하는 역할
MongoDB의 ODM은 다양하지만 Mongoose가 가장 유명하다.
 
 

패키지 설치


npm install mongoose --save
yarn add mongoose

위와 같이 노드 패키지 매니저 npm 혹은 yarn을 통해 설치할 수 있다.
 

MongoDB 연결하기 (둘중 하나)


1) require을 이용하는 방법

const mongoose = require('mongoose');
mongoose.connect("mongodb://lodcallhost/<db이름>", 
{useNewUrlParser: true});

2) import를 이용하는 방법

import mongoose from 'mongoose';

mongoose.connect('mongodb://localhost/<db이름>',
{useNewUrlParser: true});

 

 

우선 mongoose 모듈을 require(혹은 import)해준다. 그 이후 connect 메소드를 통해 MongoDB에 연결한다.
 
connect 메소드의 인자로는 mongdodb url(기본적으로는 localhost를 사용.) 과 useNewUrlParser와 같이 error발생하지 않도록 다른 인자들을 객체 형식으로 선언한다.
 
만약 본인만의 mongoDB 서버가 있으면, url인자에 'mongodb://username:password@host:port/database'  를 보내면 된다.
 
 

Schema 생성하기


Mongoose는 Schema라는 개념이 존재한다.  MongoDB의 경우 Document에 데이터를 아무거나 막 넣어도 에러가 발생하지 않는다. 이는 양날의 검과 같은데, 데이터를 막 넣을 수 있기에 편리하지만 반대로 불필요한 데이터 같은 데이터도 다 들어가며, 어떤 경우엔 원하는 자료형과 맞지 않는 데이터가 들어갈 수도 있는 문제가 있다.
 
그래서 Mongoose는 Schema를 도입한 것. 스키마는 SQL의 table과 비슷한 개념이다. 데이터를 넣을 때 schema에서 선언한 틀에 맞게 데이터를 넣을 수 있도록 해준다.
 
 

import mongoose from 'mongoose';

const imageSchema = new mongoose.Schema({
  width : Number,
  height : Number,
});

const developersSchema = new mongoose.Schema({
  name : {type : String, required: true},
  age : {type : Number, required: true},
  image : imageSchema,
  nickname : String,
  language : String,
  friends : [String],
});

 
 
스키마는 위와 같이 mongoose를 import 한 뒤, new mongoose.Schema({});를 통해 생성 가능
 
필드를 하나씩 살펴보면
name은 String타입이며, required : true를 통해 꼭 필요한 값으로 지정.
age는 Number타입이며, 이 또한 required : true를 통해 필요한 값으로 지정.
 
image필드는 다른 필드와 조금 다르게 선언되었는데, imageSchema라고 지정되어있다.
이는 image필드는 imageSchema라는 또 다른 스키마를 타입으로 가진다.
풀어서 보면
 
image : { width : Number,
            height : Number,
           } 
 
nickname은 String타입으로 선언되었다. (language도 동일!)
friends는 String타입의 배열로 선언되었다.
 
 
스키마를 만든뒤 데이터베이스 서버로 쏙 넣어준다

import mongoose from 'mongoose';

const imageSchema = new mongoose.Schema({
  width : Number,
  height : Number,
});

const developersSchema = new mongoose.Schema({
  name : {type : String, required: true},
  age : {type : Number, required: true},
  image : imageSchema,
  nickname : String,
  language : String,
  friends : [String],
});

const model = mongoose.model('Developer',developerSchema);

데이터베이스 서버로 넣어주기 위해서는 구현해놓은 schema를 model 메소드를 통해 객체로 만들어주어야 한다.
따라서 model이라는 객체를 생성한 뒤, 그 객체에 스키마 model을 넣어준다.
 
이후 각자 원하는 서버에 올리면 됨.
 
 

Populate 사용하기


 
 
 MongoDB 스키마를 만들다 보면, 필드 내에 다른 다큐먼트의 ObjectID를 쓰는 경우가 있다.
 
 

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const personSchema = Schema({
  _id: Schema.Types.ObjectId,
  name: String,
  age: Number,
  stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

const storySchema = Schema({
  author: { type: Schema.Types.ObjectId, ref: 'Person' },
  title: String,
  fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
출처 : https://mongoosejs.com/docs/populate.html

 
위의 _id와 stories를 보면 타입으로 Schema.Types.ObjectId가 지정되어있다.
MongoDB에서는 데이터가 차곡차곡 쌓일 때, 그 데이터 하나하나를 document라고 한다.
_id와 stories는 그 document를 가리키는 타입인 것.
 
 

 { _id: {$oid : 5a23c1b5d52a003c98e13f1c},
  name: 'CharmSae',
  age: 16, 
  stories : {$oid :5a23c1b5d52a003c98e13f1b},
  }

이런 형식으로 데이터가 조회되고, _id와 stories에 document id값이 저장이 되어있다.
Populate는 간단하게 말해서 이런 id값을 펼쳐서 보여주는 기능
 
사용과 그에 따른 결과는 다음과 같다.

Person.findOne({ name: 'charmsae' }).populate('stories').exec((err, data) => {
  console.log(data);
}); 


 { _id: {$oid :5a23c1b5d52a003c98e13f1c},
  name: 'CharmSae',
  age: 16, 
  stories : {
      author : {$oid :5a23c1b5d52a003c98e13f1d},
      title : 'HueMoneLabStory'
      fans : {$oid :5a23c1b5d52a003c98e13f1d},
  	},
  }

위의 결과랑은 다르게 stories에 어떠한 document가 출력이 되었다,
이렇게 populate를 활용하면 Schema.Types.ObjectId가 참조하는 다른 document를 조회할 수 있다
 
하지만 populate를 남발하는 것은 좋지 않다. populate는 $oid값을 조회한 뒤 자바스크립트에서 합쳐주는 것이지 DB 자체에서는 합쳐지지 않기에 성능이 좋은 편은 아니기 때문
 
 

Queries


데이터를 조회하는 방식인 Query
Mongoose에는 다양한 Query가 존재하는데 그중 대표적인 것들만 정리했다.

더 자세한 Query는 mongoose 공식 사이트를 참고 https://mongoosejs.com/docs/queries.html

findOne()

const query = Person.findOne({name : 'charmsae'});
//Person이라는 컬렉션 안에서 name이 charmsae인 documents를 '하나'불러옵니다.

find()

const query = Person.find({name : 'charmsae'});
//Person이라는 컬렉션 안에서 name이 charmsae인 documents를 '모두' 불러옵니다.

 
deleteOne()

const query = Person.deleteOne({name : 'charmsae'});
//Person이라는 컬렉션 안에서 name이 charmsae인 documents를 '하나' 삭제합니다.

deleteMany()

const query = Person.deleteMany({name : 'charmsae'});
//Person이라는 컬렉션 안에서 name이 charmsae인 documents를 '모두' 삭제합니다.

이때, One이 들어간 쿼리문은 가장 처음 조회되는 document를 불러온다.
 
 
sort()

const query = Person.find({name : 'charmsae'}).sort(age : -1);
//Person이라는 컬렉션 안에서 name이 charmsae인 모든 document를 age순으로 정렬합니다.
//이때, age : 1이면 오름차순, age : -1이면 내림차순으로 정렬합니다.

 
select()

const query = Person.findeOne({name : 'charmsae'}).select('age');
//Person이라는 컬렉션 안에서 name이 charmsae인 documents를 '하나' 조회한 뒤,
// age 필드만 가져옵니다.

 
 
 
 

MongoDB, Mongoose 아이디 비밀번호 설정


 
 
일명령 프롬프트에서 mongod라고 입력한다.
에러가 발생하거나 없다고 뜨시면, mongodb를 먼저 설치하기. (몽고디비 공식사이트)
 

위와 같이 뜨면 성공
 
그 뒤에는 명령 프롬포트를 하나 더 띄운 뒤 mongo라고 쓴다

위와 같이 뜨시면 이제 준비완료, 아래와 같이 작성한다.

use admin
db.createUser({ user: '이름', pwd: '비밀번호', roles: [{role : "userAdminAnyDatabase"}] })

그런 뒤, 다시 mongoose파일로 가서 

const mongoose = require('mongoose');
mongoose.connect("mongodb://lodcallhost/<db이름>", 
{useNewUrlParser: true});

와 같이 작성한 코드를 다음과 같이 수정한다

const mongoose = require('mongoose');
mongoose.connect('mongodb://아이디:비밀번호@호스트:포트/dbname'), 
{useNewUrlParser: true});