본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로데이터 › 데이터베이스 › MongoDB

1. MongoDB -기본

목차

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 타입.

같은 카테고리의 글