MongoDB 완벽 가이드: NoSQL 문서 데이터베이스
이 글의 핵심
MongoDB는 유연한 스키마와 수평 확장이 가능한 NoSQL 문서 데이터베이스입니다. JSON 형태의 BSON 문서로 데이터를 저장하며, 강력한 쿼리, 집계 파이프라인, 샤딩으로 대규모 애플리케이션에 적합합니다.
MongoDB란?
MongoDB는 문서 지향(Document-Oriented) NoSQL 데이터베이스로, JSON과 유사한 BSON(Binary JSON) 형식으로 데이터를 저장합니다. 2009년 출시 이후 가장 인기있는 NoSQL 데이터베이스로 자리잡았습니다.
핵심 특징
-
문서 기반
- JSON 형태 저장
- 유연한 스키마
- 중첩 구조 지원
-
확장성
- 수평 확장 (샤딩)
- 레플리카셋
- 자동 페일오버
-
강력한 쿼리
- 풍부한 쿼리 연산자
- 집계 파이프라인
- 텍스트 검색
-
고성능
- 인덱싱
- 메모리 맵 파일
- WiredTiger 스토리지 엔진
SQL vs NoSQL 비교
| 항목 | SQL (MySQL) | NoSQL (MongoDB) |
|---|---|---|
| 데이터 모델 | 테이블 (행/열) | 문서 (JSON) |
| 스키마 | 고정 | 유연 |
| 확장 | 수직 (Scale-up) | 수평 (Scale-out) |
| JOIN | 강력 | 제한적 (Lookup) |
| 트랜잭션 | 완벽 지원 | 4.0부터 지원 |
| 사용 사례 | 금융, 재고 관리 | 소셜 미디어, IoT |
| 학습 곡선 | 중간 | 낮음 |
설치
MongoDB 설치
# macOS (Homebrew)
brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-community
# Ubuntu
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo systemctl start mongod
# Docker
docker run -d -p 27017:27017 --name mongodb mongo:7
Node.js 드라이버 설치
# MongoDB 드라이버
npm install mongodb
# Mongoose ODM (권장)
npm install mongoose
기본 CRUD
연결
// MongoDB 드라이버
import { MongoClient } from 'mongodb';
const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('myapp');
const users = db.collection('users');
// Mongoose
import mongoose from 'mongoose';
await mongoose.connect('mongodb://localhost:27017/myapp');
Create (삽입)
// 단일 문서
const result = await users.insertOne({
name: '홍길동',
email: '[email protected]',
age: 25,
tags: ['developer', 'nodejs']
});
console.log(result.insertedId);
// 여러 문서
const result = await users.insertMany([
{ name: '김철수', email: '[email protected]' },
{ name: '이영희', email: '[email protected]' }
]);
console.log(result.insertedIds);
Read (조회)
// 모든 문서
const allUsers = await users.find().toArray();
// 조건 조회
const result = await users.find({ age: { $gte: 20 } }).toArray();
// 단일 문서
const user = await users.findOne({ email: '[email protected]' });
// Projection (필드 선택)
const users = await users.find(
{ age: { $gte: 20 } },
{ projection: { name: 1, email: 1, _id: 0 } }
).toArray();
// 정렬
const users = await users.find().sort({ age: -1 }).toArray();
// 페이지네이션
const page = 1;
const limit = 10;
const users = await users.find()
.skip((page - 1) * limit)
.limit(limit)
.toArray();
Update (수정)
// 단일 문서
await users.updateOne(
{ email: '[email protected]' },
{ $set: { age: 26 } }
);
// 여러 문서
await users.updateMany(
{ age: { $lt: 20 } },
{ $set: { category: 'minor' } }
);
// Upsert (없으면 생성)
await users.updateOne(
{ email: '[email protected]' },
{ $set: { name: '신규' } },
{ upsert: true }
);
// 증가/감소
await users.updateOne(
{ _id: userId },
{ $inc: { views: 1 } }
);
// 배열 추가
await users.updateOne(
{ _id: userId },
{ $push: { tags: 'react' } }
);
Delete (삭제)
// 단일 문서
await users.deleteOne({ email: '[email protected]' });
// 여러 문서
await users.deleteMany({ age: { $lt: 18 } });
Mongoose ODM
스키마 정의
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
minlength: 2,
maxlength: 50
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
match: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/
},
age: {
type: Number,
min: 0,
max: 150
},
tags: [String],
profile: {
bio: String,
avatar: String,
social: {
twitter: String,
github: String
}
},
isActive: {
type: Boolean,
default: true
},
createdAt: {
type: Date,
default: Date.now
}
}, {
timestamps: true // createdAt, updatedAt 자동 생성
});
// 인덱스
userSchema.index({ email: 1 });
userSchema.index({ name: 'text' });
// 가상 필드
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
// 메서드
userSchema.methods.comparePassword = async function(password) {
return bcrypt.compare(password, this.password);
};
// 스태틱 메서드
userSchema.statics.findByEmail = function(email) {
return this.findOne({ email });
};
const User = mongoose.model('User', userSchema);
Mongoose CRUD
// Create
const user = new User({
name: '홍길동',
email: '[email protected]'
});
await user.save();
// 또는
const user = await User.create({
name: '홍길동',
email: '[email protected]'
});
// Read
const users = await User.find({ age: { $gte: 20 } });
const user = await User.findById(userId);
const user = await User.findOne({ email: '[email protected]' });
// Update
const user = await User.findByIdAndUpdate(
userId,
{ age: 26 },
{ new: true } // 업데이트된 문서 반환
);
// Delete
await User.findByIdAndDelete(userId);
집계 파이프라인
// 사용자 연령대별 통계
const stats = await users.aggregate([
// 필터링
{ $match: { isActive: true } },
// 그룹화
{
$group: {
_id: {
$floor: { $divide: ['$age', 10] }
},
count: { $sum: 1 },
avgAge: { $avg: '$age' }
}
},
// 정렬
{ $sort: { _id: 1 } },
// 프로젝션
{
$project: {
ageGroup: { $multiply: ['$_id', 10] },
count: 1,
avgAge: { $round: ['$avgAge', 1] }
}
}
]);
인덱싱
// 단일 필드 인덱스
await users.createIndex({ email: 1 });
// 복합 인덱스
await users.createIndex({ lastName: 1, firstName: 1 });
// 텍스트 인덱스
await users.createIndex({ bio: 'text', tags: 'text' });
// 지리 공간 인덱스
await locations.createIndex({ location: '2dsphere' });
// 인덱스 확인
const indexes = await users.indexes();
MongoDB는 유연하고 확장 가능한 NoSQL 데이터베이스입니다. 문서 기반 모델과 강력한 쿼리로 현대적인 애플리케이션 개발에 적합합니다.