[Node.js] MongoDB 기초를 배워보자

유진·2023년 9월 27일

Node.js

목록 보기
7/8

DB(Database) & DBMS(Database Management System)

DB

  • 전기적으로 저장된 데이터의 집합
  • 영속성을 갖는 데이터 저장소
  • 데이터를 각자가 정의한 구조로 저장

DBMS

  • 데이터베이스를 운영하고 관리하는 소프트웨어(시스템). 전용 언어(SQL등)를 사용해서 관리하는 시스템.
  • DB는 데이터를 체계적으로 보관하는 저장소의 개념이고, DBMS는 DB에 저장된 데이터에 체계적으로 접근하도록 하는 시스템.
  • 크게 네 가지 기능
    • 데이터 정의 : 저장하는 데이터의 구조 정의를 생성, 수정, 삭제한다.
    • 데이터 업데이트 : 데이터 삽입, 수정, 삭제한다.
    • 데이터 검색 : 저장된 데이터를 사용자가 원하는 형태로 가져온다.
    • 관리 : DBMS 유저 등록/유저 모니터링/데이터 접근 권한 관리/성능 모니터링/무결성 유지/동시성 제어/정보 복구(특정 이벤트, 의도치않은 시스템 다운)



데이터 페이스 타입

1) 관계형 데이터베이스(Relational DBMS : RDB)

  • 관계 대수를 기반으로 구현된 데이터베이스. 정형 데이터를 관리한다.
  • 스프레드시트 형태의 데이터 구조를 가지고 있다고 생각하면 이해하기 쉽다.
  • SQL로 데이터를 검색, 업데이트할 수 있다.

SQL(Structured Query Language)

  • DBMS에 데이터를 검색/업데이트(DML : Data Manipulation Language), 일련의 데이터 구조를 가진 구조체(테이블)를 생성/수정/삭제(DDL : Data Definition Language), 유저 관리(DCL : Data Control Language), DB transaction 관리(TCL : Transaction Control Language)를 지원하는 프로그래밍언어이다.
  • 사람이 읽었을 때 이해하기 쉽도록 문법이 구성 되어있다.
  • 모든 RDBMS의 SQL는 기본적으로 유사한 문법을 가지고 있다. 다만 독자적으로 가지고 있는 문법들도 있다.
  • SQL을 작성해서 DB에게 명령을 보내는 행위는 사람이 스프레드시트를 만들어서 값을 채워 넣고 수정하고 지우는 과정이랑 매우 유사하다
  • 일반적으로 DB라고 하면 RDBMS를 일컫지만 점점 달라지고 있다.
  • MySQL, Oracle DB, MariaDB, PostgreSQL, SQLite

2) NoSQL 데이터베이스(NoSQL DBMS)

  • 관계형 데이터베이스 이외의 데이터베이스(관계 대수를 엄격하게 따르지 않는다는 것)
  • 제품마다 정형/비정형 데이터를 다룬다. 저장하는 데이터 구조가 다양하다.
    • 구조 유형 : Document, key value, Object, Wide Column, Graph
      → MongoDB(Document), Redis(key-value), Cassandra(Wide column), Elasticsearch(Document), Neo4j(Graph)



MongoDB

문서 기반 DB(Document oriented DB)

  • Mongo는 Humongous 에서 따온 말로, 엄청나게 큰 DB 라는 의미 → 대용량 데이터를 처리하기 좋게 만들어졌다.
  • JSON like 문서(BSON 타입)를 지원하며 이러한 이유로 JSON형태의 데이터를 다루는 웹 생태계에서 큰 인기를 얻게 되었다.
  • RDMS대비 비교적 단순한 설계(데이터 저장 방식)를 가지고 있기 때문에 진입 장벽이 낮은 장점이 있다.
  • 간단한 CRUD기반의 서비스에 매우 적합하다.
  • Aggregation Pipeline 기능을 활용하여 DB단에서 데이터 연산을 수행시킬 수 있다. 어플리케이션 코드의 부담을 줄일 수 있다. DB단에 연산을 수행시키는 것은 RDMS도 procedure라는 것을 이용해서 가능하다. 하지만 MongoDB의 aggregation 기능이 상대적으로 단순하고 사용하기 용이하다.
  • 다양한 데이터를 저장하고 다뤄야하는 도메인에서 많이 사용한다.
  • 데이터 간의 관계가 중요한 서비스에는 적합하지 않다.

관계형 데이터베이스와의 유사 개념

  • 여러 개의 데이터베이스를 관리하고 하나의 데이터베이스는 여러 개의 컬렉션을 가지고 있다.
    ➡ SQL에서의 database와 유사
  • 컬렉션은 다큐먼트의 집합이다.
    ➡ RDBMS의 테이블과 유사
  • 다큐먼트는 MongoDB의 데이터 저장 단위이다. 사용자가 저장하는 데이터는 모두 다큐먼트로 저장된다.
    ➡ RDBMS의 레코드(테이블의 한 row)와 유사

다큐먼트

  • BSON(Binary JSON) 형식의 데이터 구조 : JSON보다 더 많은 데이터 타입을 지원한다.
  • 자바스크립트 객체랑 비슷한 형태로 필드명과 필드값을 가진다.
  • 최신 버전 기준 16개의 데이터 타입을 지원한다.
    • Text - String
    • Numeric - 32-bit integer, 64-bit integer, Double
    • Date - Date, Timestamp
    • Other - Object, Array, Binary data, ObjectId, Boolean, Null, Regular Expression, JavaScript, Min key, Max key

필드명 제한

  • "_id"는 사용할 수 없다. Primary key(고유값)로 예약되어 있는 이름이다.
  • 필드명은 null 캐릭터(UTF-8 인코딩의 \u0000)를 포함할 수 없다.
  • "$", "."을 제한적인 환경에서 포함할 수 있다.

문서 사이즈 제한

  • 최대 16MB

_id 필드

  • 모든 MongoDB에 저장된 document들은 _id 필드를 가지고 있다. 고유값으로 DB에서의 primary key 역할을 수행한다.
  • 새로운 document를 MongoDB에 생성(삽입)할 때 _id필드를 빼고 하면 MongoDB 드라이버가 알아서 _id필드를 생성해준다.

문서 필드 순서

  • JS의 객체와는 다르게 MongoDB의 document의 필드들은 순서가 보장된다.
  • 순서가 보장되어 있는 만큼 문서에 대한 데이터 검색를 할 때는 주의해야한다.
  • MongoDB의 정의에 따라 {a: 1, b: 1}와 {a: 1, b: 1}는 같지만 {a: 1, b: 1}와 {b: 1, a: 1}는 다르다.
  • MongoDB에서 document를 찾을 때는 exact match와 같은 데이터 검색(query)은 지양하는게 좋다. 조건문 검색이 좋다.
  • 항상 _id 필드는 첫번째로 배정된다.

다큐먼트 CRUD

1) CRUD - Create

db.users.insertOne (			<- collection
	{							<- document
		name : "Lee",			<- field: value 
		age : 26,		
		status : "pending"
	}
)
  • 하나의 document : db.collection.insertOne({ 필드 : 값, 필드 : 값, ... })
  • 여러 document : db.collection.insertMany([{...}, {...}, {...}])

2) CRUD - Read

db.users.find(					<- collection
	{ age : {$gt: 18} },		<- query criteria
	{ name : 1, address : 1 }	<- projection
).limit(5)						<- cursor modifier
  • db.collection.find() : 하나/다수의 ducument 찾기
  • $in : [] 을 사용하여 다중 값으로 검색, Mongoose는 쿼리 값으로 배열이 주어지면 자동으로 $in 쿼리를 생성해 줌
  • $or : [] 를 사용하여 다중 조건 검색
  • $lt, $lte, $gt, $gte 를 사용하여 range query 작성 가능

3) CRUD - Update

db.users.updateMany(					<- collection
	{ age : {$lt: 18} }, 				<- update filter
	{ $set : { status : "reject" } }	<- update action
)
  • db.collection.updateOne() : 하나의 document 수정(단, 조건에 해당되는 document가 많으면 가장 처음 발견되는 document를 업데이트한다)
  • db.collection.updateMany(): 다수의 document 수정

4) CRUD - Delete

db.users.deleteMany(		<- collection
	{ status: "reject" }	<- delete filter
)
  • db.collection.deleteOne() : 하나의 document 삭제 (단, 조건에 해당되는 document가 많으면 가장 처음 발견되는 document를 삭제한다)
  • db.collection.deleteMany(): 다수의 document 삭제
  • db.collection.deleteMany({}) : 전체 삭제


Mongoose(ODM)

ODM(Object Data Mapping)

  • Node.js 어플리케이션이 MongoDB에 데이터를 CRUD할 수 있도록 도와주는 외부 패키지(라이브러리)
  • MongoDB의 기본 Node.js 드라이버는 연결상태를 관리하기 어려운데 Mongoose를 사용하면 간단하게 데이터베이스와의 연결상태를 관리.
  • Mongoose의 장점은 데이터를 단순하게 CRUD하는 것이 아니라, 데이터 검증(validation) + document를 JS 객체 변환까지 해주는 것.
  • MongoDB에서 가져온 document 데이터를 JS 객체화한 것을 모델이라고 함.
  • 개발자는 이 모델을 가지고 데이터를 객체지향 방식으로 수정
  • 크게 검증 파트와 CRUD + document를 JS 객체 변환 파트.
  • 검증 파트는 Schema 모듈이 담당하고, CRUD + document를 JS 객체 변환은 Model 모듈이 담당.
  • Populate : MongoDB는 기본적으로 Join을 제공하지 않음. Join과 유사한 기능을 사용하기 위해선 aggregate 라는 복잡한 쿼리를 해야 하지만, Mongoose는 populate를 사용하여 간단하게 구현할 수 있음

Mongoose ODM 사용 순서

  1. 스키마 정의
  2. 모델 만들기
  3. 데이터베이스 연결
  4. 모델 사용

스키마(Schema)

  • 한 collection의 document의 구조를 명시화한 객체
  • Mongoose는 어플리케이션이 MongoDB에 데이터를 CRUD를 할 때 이 객체를 가지고 데이터 검증(각 필드 별 타입 검증)을 수행.
  • 추가적인 검증도 커스텀 함수를 추가해서 가능. ex) 나이의 범위, 이름의 글자수 등
  • 일종의 데이터 체크리스트
  • mongoose.Schema => 이 함수의 리턴값은 객체.
  • MongoDB와 데이터를 어떤 틀에 맞춰서 주고 받을지 정의하는 것
  • MongoDB는 RDB와 달리 한 collection에 저장된 document가 모두 같은 데이터 구조를 가져야하는 강제성을 갖고있지 않다.

모델(Model)

  • 스키마 객체를 사용해서 MongoDB에 있는 document 데이터를 JS의 객체 형태로 나타낼 수 있게 해줌.
  • MongoDB에 있는 모든 document들에 대한 CRUD도 책임.
  • JS 코드 상에서 실질적으로 자주 다루는 것이 모델.
  • mongoose.model => 이 함수의 리턴값은 class. 이 class로 새로운 document 객체를 생성해서 MongoDB에 저장할 수도 있고 class의 static 메소드를 사용해서 document CRUD를 할 수도 있음. CRUD의 리턴값은 이 클래스의 객체.
  • JS객체 형식으로 MongoDB와 데이터를 주고 받는 것
const mongoose = require("mongoose");
await mongoose.connect("mongodb://<your mongodb url>");

const coffeeSchema = mongoose.Schema({
 type: String,
 orderedBy: String,
});

// Coffee가 Coffee model 생성을 위한 class이다.
const Coffee = mongoose.model("Coffee", coffeeSchema);
			     			// ↑  Collection의 이름


// ./models/schemas/board.js
const { Schema } = require('mongoose');

const PostSchema = new Schema({
	title: String, 			// 다양한 형식을 미리 지정하여, 생성, 수정 작업 시 데이터 형식을 체크해주는 기능을 제공함
	content: String,
}, {
	timestamps: true,		// timestamps 옵션을 사용하면 생성, 수정 시간을 자동으로 기록해 줌
});

module.exports = PostSchema;


// ./models/index.js
const mongoose = require('mongoose');

const PostSchema = require('./schemas/board');

exports.Post = mongoose.model('Post', PostSchema);	
// 작성된 스키마를 mongoose에서 사용할 수 있는 모델로 만들어야 함
// 모델의 이름을 지정하여 Populate 등에서 해당 이름으로 모델을 호출할 수 있음


// index.js
const mongoose = require('mongoose');

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

mongoose.connect('mongodb://localhost:27017/myapp');	
// connect 함수를 이용하여 간단하게 데이터베이스에 연결할 수 있음
// Post 바로 사용 가능

connection pooling

  • 재사용할 수 있는 active connection들을 관리하는 개념
  • connection : 클라이언트와 서버 간의 특정 프로토콜을 이용한 연결이다. 한쪽이 연결을 끊으면 바로 없어진다.
  • 하나의 풀(pool)장에 여러 active connection(끊기지 않은)을 담아 놓아서 재사용하는 것을 목적으로 한다.
    재사용 = 연결을 새로 맺 는 수고(시간 및 연산 비용)를 줄일 수 있음
  • 유연한 connection 관리로 application 입장에서는 query들의 병목 현상을 줄이고(throughput ↑), DB 입장에서는 클라이언트가 connection을 남용하지 않기 때문에 connection 가용성을 확보할 수 있다.
  • 설정한 pool 사이즈 안에서 클라이언트는 connection을 마음대로 생성하고 사용하고 삭제할 수 있다.
  • mongoose의 pool 사이즈는 mongoose.connect 메소드의 minPoolSize와 maxPoolSize옵션으로 조절할 수 있으며 maxPoolSize의 default값은 100이다.
  • 현재 사용중인 connection의 갯수가 가용 가능한 connection의 갯수랑 같을 때 새로운 쿼리가 실행되면 새로운 connection을 생성한다
profile
도라에몽 암기빵

0개의 댓글