MongoDB 핵심 정리

MongoDB 핵심 정리

MongoDB는 대표적인 NoSQL 데이터베이스다. 관계형 데이터베이스처럼 테이블과 행을 중심으로 데이터를 관리하는 대신 Document와 Collection을 중심으로 데이터를 저장한다.

관계형 데이터베이스가 데이터를 다음과 같이 관리한다면,

Database -> Table -> Row

MongoDB는 다음과 같은 구조를 사용한다.

Database -> Collection -> Document

Document는 BSON(Binary JSON) 형태로 저장되며 JSON과 비슷한 구조를 가지고 있다.

{
  "name": "Joo",
  "level": 30,
  "items": [
    "sword",
    "potion"
  ]
}

MongoDB를 이해할 때 중요한 부분은 단순히 “NoSQL이라서 스키마가 없다”는 것이 아니다.

데이터를 어떻게 Document 단위로 모델링하고, 어떤 기준으로 데이터를 포함하거나 분리할 것인지가 MongoDB 설계에서 중요하다.


Document와 Collection

MongoDB에서 데이터를 저장하는 기본 단위는 Document다.

예를 들어 게임 캐릭터 정보를 저장한다면:

{
  "_id": "player-100",
  "name": "Joo",
  "level": 30,
  "class": "Knight"
}

여러 Document는 하나의 Collection에 저장된다.

Collection -> Document -> Document -> Document

관계형 데이터베이스의 Table과 비슷한 역할을 하지만 구조적으로는 차이가 있다.

하나의 Collection에 있는 Document가 반드시 완전히 동일한 필드를 가져야 하는 것은 아니다.

{
  "name": "Joo",
  "level": 30
}
{
  "name": "Kim",
  "level": 20,
  "guild": "Knights"
}

이런 구조가 가능하다.

다만 이것이 아무런 규칙 없이 데이터를 저장해도 된다는 의미는 아니다.

MongoDB의 유연한 구조와 별개로 애플리케이션에서 사용할 데이터 형태는 일관되게 관리하는 것이 좋다.


BSON

MongoDB는 JSON과 비슷한 형태로 데이터를 다루지만 실제 저장 형식은 BSON이다.

BSON은 Binary JSON을 의미하며 JSON보다 다양한 데이터 타입을 지원한다.

예를 들어 날짜나 ObjectId 같은 타입을 사용할 수 있다.

{
  "_id": ObjectId("..."),
  "createdAt": ISODate("2026-08-26T00:00:00Z")
}

MongoDB에서 _id는 Document를 식별하기 위한 필드다.

별도로 지정하지 않으면 MongoDB가 기본적으로 _id를 생성하며 일반적으로 ObjectId가 사용된다.

데이터의 식별 관계는 다음과 같이 볼 수 있다.

Document -> _id -> Unique Identifier

CRUD

MongoDB의 기본적인 데이터 처리는 CRUD로 나눌 수 있다.

Create -> Read -> Update -> Delete

Document를 추가하려면:

db.users.insertOne({
    name: "Joo",
    level: 30
});

조회:

db.users.find({
    level: 30
});

수정:

db.users.updateOne(
    { name: "Joo" },
    { $set: { level: 31 } }
);

삭제:

db.users.deleteOne({
    name: "Joo"
});

MongoDB의 Query는 JSON과 비슷한 형태로 조건을 표현한다.

예를 들어:

db.users.find({
    level: { $gte: 30 }
});

level이 30 이상인 Document를 찾는다.

기본적인 데이터 처리 흐름은 다음과 같다.

Application -> MongoDB Query -> Collection -> Document -> Result

MongoDB에서 Schema를 어떻게 보는가

MongoDB는 흔히 “Schema-less Database”라고 설명한다.

하지만 실무에서 Schema가 필요 없다는 의미로 받아들이면 곤란하다.

예를 들어 애플리케이션이 다음 데이터를 기대한다고 하자.

{
  "name": "Joo",
  "level": 30,
  "class": "Knight"
}

그런데 같은 Collection에 다음과 같은 데이터가 계속 들어오면:

{
  "username": "Kim",
  "lv": 20
}

애플리케이션에서 데이터를 처리하기가 오히려 어려워진다.

MongoDB가 자유로운 Document 구조를 제공하더라도 애플리케이션 수준에서 데이터 구조를 관리해야 한다.

MongoDB 자체에서도 Schema Validation을 이용해 저장되는 데이터의 구조를 제한할 수 있다.

따라서 MongoDB의 Schema는 다음과 같이 이해하는 편이 적절하다.

Schema 요구사항 -> Application Schema 관리 -> 필요하면 MongoDB Schema Validation 적용

MongoDB는 Schema가 없는 것이 아니라 Schema를 강제하는 방식이 관계형 데이터베이스보다 유연한 것에 가깝다.


Embedded Document

MongoDB에서 중요한 데이터 모델링 방식 중 하나가 Embedded Document다.

관련 데이터가 항상 함께 조회된다면 하나의 Document 안에 포함할 수 있다.

예를 들어 플레이어와 프로필이 항상 같이 사용된다면:

{
  "_id": 100,
  "name": "Joo",
  "profile": {
    "level": 30,
    "class": "Knight"
  }
}

구조는 다음과 같다.

Player Document -> Profile Document

배열도 Document 내부에 포함할 수 있다.

{
  "_id": 100,
  "name": "Joo",
  "items": [
    {
      "id": 1,
      "name": "Sword",
      "count": 1
    },
    {
      "id": 2,
      "name": "Potion",
      "count": 10
    }
  ]
}

이 경우 구조는 다음과 같다.

Player Document -> Items Array -> Item Document

관련 데이터가 함께 사용되는 경우 하나의 Document에 포함함으로써 필요한 데이터를 한 번에 조회할 수 있다.

Embedded 설계의 기본 흐름은 다음과 같다.

데이터 관계 확인 -> 함께 조회되는지 확인 -> Embedded Document 설계

Reference

반대로 데이터가 크거나 독립적으로 관리되어야 한다면 Document를 분리할 수 있다.

예를 들어:

{
    "_id": 100,
    "name": "Joo"
}

그리고 별도의 Collection에:

{
    "_id": 1,
    "ownerId": 100,
    "name": "Sword"
}

를 저장할 수 있다.

이 경우 관계는 다음과 같이 표현할 수 있다.

Player Document -> ownerId -> Item Document

관계형 데이터베이스에서는 Table을 분리하고 JOIN하는 방식이 일반적이지만 MongoDB에서는 애플리케이션의 조회 패턴을 기준으로 Embedded와 Reference 중 어떤 방식을 사용할지 결정하는 경우가 많다.

따라서 데이터 모델링 과정은 다음과 같이 볼 수 있다.

데이터 관계 -> 조회 패턴 분석 -> 데이터 크기 확인 -> Embedded / Reference 선택

MongoDB에서 JOIN은 없는가

MongoDB에도 다른 Collection의 데이터를 연결해서 조회하는 기능이 있다.

대표적으로 $lookup이 있다.

db.orders.aggregate([
    {
        $lookup: {
            from: "users",
            localField: "userId",
            foreignField: "_id",
            as: "user"
        }
    }
]);

따라서 MongoDB에 JOIN과 비슷한 기능이 전혀 없는 것은 아니다.

데이터 연결 과정은 다음과 같이 볼 수 있다.

Collection A -> $lookup -> Collection B -> Combined Result

다만 MongoDB의 데이터 모델링에서는 관계형 데이터베이스처럼 모든 데이터를 무조건 정규화하고 JOIN하는 방식보다 조회 패턴에 맞게 데이터를 묶어 저장하는 것을 중요하게 본다.


Index

데이터가 많아지면 Query 성능을 위해 Index가 필요하다.

예를 들어 email로 사용자를 자주 조회한다면 해당 필드에 Index를 만들 수 있다.

db.users.createIndex({
    email: 1
});

조회 과정은 개념적으로 다음과 같다.

Query -> Index -> Target Document

Index는 무조건 많이 만든다고 좋은 것이 아니다.

Index 자체도 저장 공간을 사용하고 Insert와 Update 같은 쓰기 작업에서는 Index를 함께 관리해야 한다.

따라서 실제 Query 패턴을 기준으로 Index를 설계해야 한다.

Query Pattern -> Index 설계 -> Read 성능 향상

동시에 다음과 같은 비용이 발생한다.

Index 추가 -> Storage 증가 -> Write 비용 증가

결국 Index 설계는 조회 성능과 저장 및 쓰기 비용 사이의 균형 문제다.


Compound Index

여러 필드를 함께 검색하는 경우에는 Compound Index를 사용할 수 있다.

db.users.createIndex({
    level: 1,
    class: 1
});

예를 들어:

db.users.find({
    level: 30,
    class: "Knight"
});

같은 Query를 자주 사용하는 경우 Compound Index를 고려할 수 있다.

설계 과정은 다음과 같다.

Query 조건 -> 정렬 조건 -> 필드 순서 결정 -> Compound Index 설계

Compound Index는 필드의 순서가 중요하기 때문에 단순히 검색에 사용되는 필드를 모두 넣는 방식으로 설계해서는 안 된다.

실제 Query 패턴을 기준으로 Index를 구성해야 한다.


Aggregation

MongoDB에서 데이터를 집계하고 변환할 때는 Aggregation Pipeline을 사용한다.

예를 들어 게임에서 직업별 평균 레벨을 계산한다고 하자.

Collection -> $match -> $group -> $sort -> $project -> Result

MongoDB에서는 다음과 같은 Pipeline을 만들 수 있다.

db.players.aggregate([
    {
        $group: {
            _id: "$class",
            averageLevel: {
                $avg: "$level"
            }
        }
    }
]);

Aggregation은 여러 Stage를 연결해서 데이터를 처리한다.

Document -> $match -> $group -> $sort -> $project -> Result

각 Stage에서 데이터를 필터링하고 그룹화하고 계산하고 원하는 형태로 변환할 수 있다.

따라서 Aggregation은 단순 조회보다 복잡한 데이터 가공이나 통계에 적합하다.


Transaction

MongoDB도 Transaction을 지원한다.

여러 작업을 하나의 논리적인 작업으로 묶어야 하는 경우 사용할 수 있다.

예를 들어 게임에서 아이템을 다른 플레이어에게 이동한다고 하자.

Player A -> Item Remove
Player B -> Item Add

두 작업이 반드시 함께 처리되어야 한다면 Transaction을 사용할 수 있다.

Transaction Begin -> Player A Item Remove -> Player B Item Add -> Commit

중간에 문제가 발생하면:

Transaction Begin -> Player A Item Remove -> Error -> Rollback

이런 방식으로 여러 작업을 하나의 원자적인 작업으로 처리할 수 있다.

다만 MongoDB의 Document 모델에서는 가능한 경우 관련 데이터를 하나의 Document에 포함시켜 Transaction의 필요성을 줄이는 설계도 고려할 수 있다.


Replica Set

MongoDB를 운영 환경에서 사용한다면 장애 대응을 위해 Replica Set을 구성할 수 있다.

기본 구조는 다음과 같다.

Primary -> Secondary
Primary -> Secondary
Primary -> Secondary

Primary가 쓰기 작업을 처리하고 Secondary가 데이터를 복제한다.

장애가 발생하면 다음과 같은 흐름으로 새로운 Primary를 구성할 수 있다.

Primary 장애 -> Secondary 선출 -> New Primary -> Service Continue

따라서 Replica Set은 MongoDB의 고가용성을 구성하는 핵심 요소다.

Replica Set -> Data Replication -> Failover -> High Availability

Sharding

데이터가 매우 커져 하나의 서버에서 처리하기 어려워지면 Sharding을 사용할 수 있다.

Sharding은 데이터를 여러 서버에 분산시키는 방식이다.

MongoDB Cluster -> Shard 1
MongoDB Cluster -> Shard 2
MongoDB Cluster -> Shard 3

어떤 데이터를 어느 Shard에 저장할지는 Shard Key를 기준으로 결정한다.

Document -> Shard Key -> Target Shard -> Data Storage

Sharding의 목적은 데이터를 여러 서버에 분산해 저장하고 처리하는 것이다.

Large Dataset -> Sharding -> Distributed Data -> Horizontal Scaling

다만 Sharding은 서버를 여러 대 추가하면 끝나는 기능이 아니다.

Shard Key를 잘못 선택하면 특정 Shard에 데이터가 집중되는 문제가 발생할 수 있고 Query 성능에도 영향을 줄 수 있다.

따라서 대규모 MongoDB에서는 다음과 같은 흐름으로 설계해야 한다.

Data Distribution 분석 -> Query Pattern 분석 -> Shard Key 설계 -> Sharding 구성

MongoDB의 장점과 한계

MongoDB의 가장 큰 특징은 Document 구조가 애플리케이션의 데이터 모델과 잘 맞는 경우가 많다는 것이다.

중첩된 객체나 배열을 하나의 Document에 자연스럽게 저장할 수 있고, 데이터 구조의 변경에도 비교적 유연하게 대응할 수 있다.

특히 다음과 같은 데이터와 잘 맞는다.

User Profile
Game Character Data
Product Data
Content Data
Event Log
Document Data

반대로 복잡한 관계와 강한 정합성이 중심인 시스템이라면 관계형 데이터베이스가 더 적합할 수 있다.

예를 들어 금융 거래처럼 여러 데이터의 관계와 Transaction이 매우 중요한 시스템에서는 PostgreSQL이나 MySQL 같은 관계형 DB를 우선적으로 고려하는 경우가 많다.

DB 선택은 다음과 같은 기준으로 결정할 수 있다.

Data Structure -> Access Pattern -> Consistency Requirement -> Scalability Requirement -> Database Selection

MongoDB가 관계형 DB보다 상위호환인 것은 아니다.

문제의 특성에 따라 적합한 데이터베이스가 달라진다.


MongoDB와 Redis의 차이

MongoDB와 Redis는 둘 다 NoSQL 계열로 분류되지만 목적이 상당히 다르다.

MongoDB는 주로 Document를 영구적으로 저장하는 데이터베이스로 사용한다.

Redis는 메모리 기반 Key-Value 저장소로 Cache, Session, 실시간 데이터 등에 많이 사용한다.

MongoDB -> Persistent Document Storage
Redis -> In-Memory Key-Value Storage

하나의 서버에서 두 시스템을 함께 사용하는 경우 다음과 같은 구조가 가능하다.

Application -> MongoDB -> User Data / Game Data
Application -> Redis -> Cache / Session / Ranking

두 데이터베이스를 함께 사용하는 경우 역할을 분리할 수 있다.

Application -> MongoDB -> Persistent Data
Application -> Redis -> Fast Access Data

따라서 MongoDB와 Redis는 반드시 서로 대체하는 관계가 아니라 목적에 따라 함께 사용할 수도 있다.


MongoDB를 서버에서 사용하는 구조

Node.js나 Java 같은 서버 애플리케이션에서 MongoDB를 사용할 수 있다.

일반적인 요청 흐름은 다음과 같다.

Client -> HTTP -> Server -> MongoDB Driver / ODM -> MongoDB

서버 내부에서는 다음과 같은 계층 구조를 사용할 수 있다.

Request -> Controller -> Service -> Repository -> MongoDB

Node.js에서는 MongoDB Driver나 Mongoose 같은 ODM을 사용할 수 있고, Java에서는 MongoDB Java Driver나 Spring Data MongoDB 등을 사용할 수 있다.

MongoDB를 사용한다고 해서 서버 아키텍처 자체가 달라지는 것은 아니다.

데이터 접근 계층에서 MongoDB에 맞는 저장 방식과 Query를 사용하는 것이다.


MongoDB를 설계할 때 중요한 것

MongoDB에서 가장 중요한 것은 Document를 어떻게 구성할 것인가다.

관계형 DB에서는 보통 정규화부터 생각하는 경우가 많지만 MongoDB에서는 애플리케이션이 데이터를 어떻게 읽는지부터 보는 편이 좋다.

예를 들어 플레이어 정보를 조회할 때 항상 장비 정보가 필요하다면:

Player Data -> Equipment Data -> Embedded 고려

반대로 장비가 독립적으로 관리되고 수가 매우 많거나 다른 시스템에서도 별도로 조회한다면:

Player Data -> Equipment Data -> Reference 고려

결국 Document 설계는 다음과 같은 흐름으로 판단할 수 있다.

Data Relationship -> Access Pattern -> Data Size -> Write Frequency -> Consistency Requirement -> Document Model

그 결과에 따라:

Document Model -> Embedded

또는:

Document Model -> Reference

를 선택한다.

이후 실제 Query 패턴에 따라 Index를 설계한다.

Document Model -> Query Pattern -> Index Design -> Query Performance

MongoDB의 유연함은 아무렇게나 저장해도 된다는 의미가 아니다.

오히려 Document 구조를 잘못 잡으면 데이터가 지나치게 커지거나 Query가 복잡해지고 Index 설계도 어려워질 수 있다.


전체 구조

MongoDB를 서버 개발 관점에서 하나로 묶으면 다음과 같다.

Client -> Application Server -> MongoDB Driver / ODM -> MongoDB Cluster

MongoDB 내부의 기본 데이터 구조는 다음과 같다.

MongoDB Cluster -> Database -> Collection -> Document -> Field

Document 내부에는 필요한 경우 다음과 같은 데이터 구조를 사용할 수 있다.

Document -> Embedded Document
Document -> Embedded Array
Document -> Reference ID -> Other Document

조회는 다음과 같이 연결된다.

Application -> Query -> Index -> Document -> Result

데이터 집계는 다음과 같이 연결된다.

Collection -> Aggregation Pipeline -> Result

여러 작업을 하나의 논리적 작업으로 처리해야 한다면:

Transaction -> Operation -> Commit / Rollback

고가용성이 필요한 경우:

MongoDB Cluster -> Replica Set -> Primary / Secondary -> Failover

수평 확장이 필요한 경우:

MongoDB Cluster -> Sharding -> Shard Key -> Shard -> Distributed Data

전체 구조를 하나로 연결하면 다음과 같이 볼 수 있다.

Client -> Application Server -> Driver / ODM -> MongoDB Cluster -> Database -> Collection -> Document

운영 환경에서는 다음 구조가 추가된다.

MongoDB Cluster -> Replica Set -> Primary / Secondary -> High Availability
MongoDB Cluster -> Sharding -> Shard Key -> Distributed Data -> Horizontal Scaling

정리

MongoDB에서 가장 기본적인 구조는 다음과 같다.

Database -> Collection -> Document -> Field

Document 모델링은 다음과 같이 이해할 수 있다.

Data Relationship -> Access Pattern -> Embedded / Reference

조회 성능은 다음과 같이 연결된다.

Query Pattern -> Index -> Query Performance

복잡한 데이터 가공은:

Collection -> Aggregation Pipeline -> Result

여러 작업의 원자적인 처리는:

Transaction -> Operations -> Commit / Rollback

고가용성은:

Replica Set -> Replication -> Failover -> High Availability

수평 확장은:

Sharding -> Shard Key -> Distributed Data -> Horizontal Scaling

서버 애플리케이션에서의 전체적인 접근 구조는:

Client -> Server -> Driver / ODM -> MongoDB

MongoDB는 단순히 “테이블이 없는 데이터베이스”가 아니다.

Document를 중심으로 데이터를 모델링하고, 애플리케이션의 조회 패턴에 맞춰 데이터를 저장하는 데이터베이스라는 점이 핵심이다.

MongoDB를 공부할 때 CRUD 문법만 외우는 것보다 Embedded와 Reference를 언제 선택하는지, Index를 왜 만드는지, Aggregation을 언제 사용하는지, Transaction이 필요한 상황은 무엇인지, Replica Set과 Sharding이 어떤 문제를 해결하는지를 같이 이해하는 편이 실제 서버 개발에 더 도움이 된다.

결국 MongoDB 설계의 핵심 흐름은 다음과 같이 정리할 수 있다.

Requirements -> Access Pattern -> Document Modeling -> Embedded / Reference -> Index Design -> Query / Aggregation -> Replica Set / Sharding

MongoDB를 제대로 이해한다는 것은 MongoDB 명령어를 많이 아는 것이 아니다.

애플리케이션의 데이터 접근 패턴을 보고 적절한 Document 구조와 저장 전략을 설계할 수 있는 것이 핵심이다.