목차
MongoDB
문서 지향 오픈소스 NoSQL 데이터베이스로 키와 연결된 값의 집합으로 이루어진 Documents 이다
특징
-
JSON과 닮은 BSON 도큐먼트로 저장한다. BSON은 날짜·ObjectId 등 JSON에 없는 타입도 표현한다.
-
필드, 범위 기반, 문자열 패턴 일치 등 다양한 쿼리를 지원
-
확장성이 뛰어나며 데이터 파티셔닝 기능이 내장
-
샤딩을 통해 여러 서버에 워크로드를 분산하여 대용량 데이터를 효율적으로 관리
MongoDB의 주요 구성 요소와 기능
-
Collection(컬렉션): Document Group으로 RDB에서는 테이블에 해당하며, 동적 스키마를 가진다.
-
Document(도큐먼트) : 관계형 데이터베이스에서는 행에 대응
-
Mongo Shell(몽고 쉘) : MongoDB 인스턴스와 상호작용하는 JavaScript 쉘
-
Sharding(샤딩) : 데이터를 여러 서버로 분산하는 프로세스
-
인덱싱, 집계, 파일 스토리지 등의 기능을 제공
-
MongoDB는 대규모 데이터 처리, 빠른 확장이 필요한 경우, 또는 데이터 구조가 자주 변경되는 애플리케이션에 적합
기본 사용 방법
1. 데이터베이스 선택 use db_name
use library;
2. 현재 DB에 할당된 데이터베이스를 확인 db 입력
db
3. 현재 데이터베이스의 컬렉션 반환 db.[collectionName]
db.books
4. DB 목록을 확인할 수 있다. show dbs
show dbs;
5. 현재 데이터베이스의 컬렉션을 확인할 수 있다. show collections
show collections;
6. 데이터베이스의 사용자 확인할 수 있다. show users
show users;
7. 현재 데이터베이스의 상태 확인 db.stats()
db.stats()
생성(create), 읽기(read), 갱신(update) 삭제(delete) 네 가지 기본적인 작업을 수행
1. 생성(create)
insertOne() : 1개의 document를 insert
book = {
"_id": ObjectId(),
"title" : "MongoDB",
"author": "DevPaik",
"publication_year": 2024
}
db.books.insertOne(book);
insertMany() : 여러 Document를 insert
-
모든 Document는 16MB 보다 작아야 한다.
-
도큐먼트 option에 ‘ordered’ 키에 true 를 지정하면 도큐먼트가 제공된 순서대로 삽입된다. false를 지정하면 MongoDB가 성능을 개선하려고 삽입을 재배열할 수 있다.(기본 값 정렬된 삽입)
bookList = [
{ "title" : "MySQL",
"author": "DevPaik",
"publication_year": 2024
},
{ "title" : "MongoDB",
"author": "DevPaik",
"publication_year": 2024
}
]
db.books.insertMany(
bookList
,{"ordered": true}
);
2. 읽기(read)
findOne() - 단일 도큐먼트 조회
db.books.findOne();
db.books.findOne({title: "MongoDB" });
find() - 여러건 조회
db.books.find({title: "MongoDB" });
3. 갱신(update)
키를 추가, 변경, 삭제할 때는 항상 $ 제한자를 사용한다.
updateOne() : 단건 업데이트
db.books.updateOne(
{ title: "MongoDB" },
{ $set: {author: "Chris"} }
);
db.books.updateOne(
{ title: "MongoDB" },
{ $set: {author: "Chris"} }
);
updateMany() : 여러 건 업데이트
db.books.updateMany(
{ "title": "MongoDB" },
{ $set: { "reviews": "재미있어용" } }
);
db.books.updateMany(
{ "title": "MongoDB" },
{ $set: { "reviews": "재미있어용" } }
);
$set은 필드 값을 설정하고 필드가 존재하지 않을 경우 새 필드가 생성된다. (스키마를 갱신하거나 사용자 정의 키를 추가할 때 편리)
db.books.updateOne({title: "MongoDB"}, {$set: {date: new Date()}});
$unset으로 키와 값을 제거할 수 있다.
db.books.updateOne({title: "MongoDB"}, {$unset: {date: null}});
$inc 연산자 : 이미 존재하는 키 값을 변경하거나 새 키를 생성하는데 사용
db.books.updateOne({title: "MongoDB"}, {$inc: {loan : 1}})
갱신 요청이 동시 발생 시 서버에 먼저 도착한 요청이 적용된 후 다믕 요청이 적용되므로, 여러개의 갱신 요청이 빨리 발생하더라도 결국 마지막 요청이 최종 값으로 변경된다. (기본 동작을 원하지 않을 경우 : Document 비저닝 패턴을 고려)
ReplaceOne() : 도큐먼트를 새로운 것으로 완전히 치환 (스키마 마이그레이션 시 유용)
var book = db.books.findOne({"title" : "MongoDB"});
var book = db.books.findOne({_id : ObjectId("66dfd5bf28a9a762d47dc0ce")});
book.reviews = "재미있다!";
db.books.replaceOne({_id : ObjectId("66dfd5bf28a9a762d47dc0ce")}, book);
$push는 배열이 존재할 경우 배열 끝에 요소를 추가하고 존재하지 않을 경우 새로운 배열을 생성한다.
db.books.updateMany(
{ title: "MongoDB" },
{ $push : { "categories" : "IT"} }
);
db.books.updateMany(
{ title: "MongoDB" },
{ $push : { "categories" : "COMPUTER"} }
);
$push에 $each 연산자를 이용하면 한번에 여러 값을 추가할 수 있다.
db.books.updateMany(
{ title: "MongoDB" },
{ $push : { "categories" :{ $each : ["DB", "NO_SQL"] } } }
);
$slice와 $push를 결합하여 배열이 특정 크기 이상으로 늘어나지 않게 할 수 있다.
db.books.updateMany(
{ title: "MongoDB" },
{ $push : {
"categories" :
{ $each : ["DB", "NO_SQL"],
$slice: -10 //10 제한
}
}
}
);
$pop : 배열 요소 제거
db.books.updateMany(
{ title: "MongoDB" },
{ $pull : { "categories" :"NO_SQL" } }
);
4. 삭제(delete) - 도큐먼트를 DB에서 삭제
deleteOne() : 단건 삭제
db.books.deleteOne({title: "MongoDB"});
deleteMany() : 여러 건 삭제
db.books.deleteMany({title: "MongoDB" });
drop() : 전체 컬렉션 삭제
db.books.drop()
기본 데이터형
null : 값이 명시적으로 비어 있음을 표현한다. 필드가 아예 없는 상태와 구분한다.
{ "author": null}
Boolean : true, false 에 사용
{isDelete: true}
숫자(Number) : 64비트 부동소수점 수를 기본으로 사용
{price: 1000}
{price :1000.5}
4바이트 혹은 8바이트 부호 정수 : NumberInt, NumberLong 클래스를 사용
{price: NumberInt("1000")}
{price :NumberLong("100000")}
문자열
{"title": "MongoDB"}
날짜
1970 1월 1일부터의 시간을 1/1000초 단위로 나타내는 64비트 정수로 날짜를 저장하며, 표준시간 대는 저장하지 않는다.
{"date": new Date()}
정규표현식
{"regex": /MongoDB/i}
배열 : Set or list 를 배열로 표현
{"data": [ {"title" : "MySQL", "author": "DevPaik", "publication_year": 2024},
{"title" : "MongoDB", "author": "DevPaik", "publication_year": 2024}
]
}
내장 도큐먼트 : 도큐먼트는 부모 도큐먼트의 값으로 내장된 도큐먼테 전체를 포함가능
{data: {"title" : "MySQL", "author": "DevPaik", "publication_year": 2024}}
객체 ID : 도큐먼트용 12바이트 ID
{"_id" : ObjectId()}
코드 : 쿼리와 도큐먼트는 임의의 자바 스크립트 코드를 포함 할 수 있다.
{"script": function() { return this.title + " by " + this.author; } }
MongoDB는 하나의 컬렉션에서 모든 Document는 _id Key를 가지며 ObjectId()를 기본으로 생성한다.
도큐먼트 구조를 조회 방식에서 시작하기
books 컬렉션에 책 제목·저자·출판 연도를 넣었다고 하자. 화면에서 책과 최근 리뷰 몇 건을 항상 함께 읽는다면 리뷰의 작은 목록을 책 문서 안에 넣는 방법이 있다. 반대로 리뷰가 계속 늘어나고 별도로 페이지를 나눠 검색하거나 독립적으로 관리해야 한다면 리뷰를 다른 컬렉션에 두고 책 ID를 참조하는 편이 낫다. MongoDB는 유연한 스키마를 허용하지만 아무 구조나 넣어도 유지 관리가 쉬워지는 것은 아니다. 자주 같이 읽고 같이 갱신하는 데이터를 기준으로 내장과 참조를 결정한다. 한 도큐먼트에는 크기 제한이 있으므로 끝없이 자라는 배열을 무조건 내장하지 않는다.
use library
db.books.insertOne({
isbn: "978-example",
title: "MongoDB 실습",
author: { name: "Kim", country: "KR" },
categories: ["database", "backend"],
publication_year: 2024
})
db.books.findOne({ isbn: "978-example" })
insertOne 결과의 insertedId와 조회된 _id가 같으면 방금 넣은 문서를 찾은 것이다. 같은 isbn을 두 번 삽입하면 기본 상태에서는 중복될 수 있으므로 업무상 유일한 식별자라면 db.books.createIndex({ isbn: 1 }, { unique: true }) 같은 고유 인덱스를 검토한다. 인덱스가 생긴 뒤 기존 중복 데이터가 있으면 생성이 실패할 수 있다. 조회 성능만 보고 인덱스를 많이 만들면 쓰기 비용과 저장 공간이 늘어난다.
flowchart LR Q[화면과 API의 조회 패턴] --> M[내장 또는 참조 결정] M --> V[필수 필드·타입 검증] V --> I[자주 쓰는 필터의 인덱스] I --> E[실제 조회와 갱신 검증]
갱신과 삭제 결과 읽기
db.books.updateOne({ isbn: "978-example" }, { $inc: { loan: 1 } })
db.books.findOne({ isbn: "978-example" }, { _id: 0, isbn: 1, loan: 1 })
첫 실행이라 loan이 없었다면 $inc가 1을 만든다. updateOne 응답의 matchedCount는 조건에 맞는 문서 수이고 modifiedCount는 실제로 값이 바뀐 수다. 같은 값을 $set하면 맞는 문서가 있어도 modifiedCount가 0일 수 있다. $inc는 단일 도큐먼트 안에서 원자적으로 값을 변경하므로 여러 요청이 같은 필드를 증가시키는 예제에 적합하다. 반면 원문의 findOne 후 전체 replaceOne 패턴은 그 사이 다른 요청의 변경을 덮어쓸 수 있다. 필요한 필드만 $set하거나 버전 필드를 조건에 포함하는 방식을 고려한다.
삭제도 결과의 deletedCount를 확인한다. deleteMany({})와 drop()은 범위가 매우 넓으므로 운영 환경에서는 조건을 먼저 find로 검토하고 백업·복구 계획을 확인한다. null 값을 저장한 {author: null}과 author 필드 자체가 없는 문서는 다른 상태다. 조회 조건에 따라 둘을 함께 매칭할 수 있으므로, 존재 여부가 중요하면 $exists로 명시한다. 또한 원문의 $pop 절에는 실제로 $pull 예제가 있다. $pull은 값이 일치하는 배열 원소를 제거하고 $pop은 배열의 앞 또는 뒤 원소 하나를 제거한다.
| 증상 | 확인할 것 |
|---|---|
| 조회가 예상보다 넓음 | 빈 필터 {}, 필드 누락과 null 매칭 |
| 업데이트 후 값이 안 바뀜 | matchedCount, 필터와 자료형 |
| 중복 도큐먼트 | 업무 키의 고유 인덱스와 기존 데이터 |
| 큰 배열이 느림 | 무한 성장 여부, 내장/참조 재설계 |
참고: MongoDB 데이터 모델링, 내장 도큐먼트, CRUD 작업, BSON 타입.