MongoDB · NoSQL 문서 DB
한 줄 소개 — MongoDB 는 행·열의 테이블이 아니라 JSON 형태의 문서(Document) 로 데이터를 저장하는 NoSQL 문서 DB 다. 스키마가 유연하고 중첩 구조를 그대로 담을 수 있다. 이 글은 데이터 구조 · 기본 쿼리 · 고가용성 replicaSet 을, KT 에서 API 트래픽 통계·게이트웨이 저장소로 MongoDB 를 운영하며 정리한 실무 예시와 함께 다룬다.
1 · MongoDB 란? (RDB vs Document)
관계형 DB(RDB)는 데이터를 테이블(행·열) 로 정규화해 나누고 JOIN 으로 잇는다. MongoDB 는 연관된 데이터를 하나의 문서 안에 중첩(임베딩) 해 담는다 — 스키마가 고정되지 않아 유연하고, 한 번의 조회로 연관 데이터를 함께 읽을 수 있다.
2 · 데이터 구조 — Database · Collection · Document
MongoDB 의 계층은 Database > Collection > Document 다. RDB 에 대응시키면 Collection ≈ Table, Document ≈ Row 이지만, Document 는 BSON(Binary JSON)으로 자유로운 중첩 구조를 가진다. 모든 문서엔 고유 _id(ObjectId)가 자동 부여된다.
실제 운영했던 API 트래픽 통계 문서 — 통계 안에 API별 → 결과별 → 에러별 통계가 배열로 중첩 되어 있다(한 번에 계층 데이터를 읽기 위함).
{
"_id": ObjectId("62aababcddda6517b7f7149a"),
"svcId": "GOOGLE",
"statBaseUnit": "MI",
"totCnt": 6, "sucesCnt": 0, "failCnt": 6,
"statApiTrafcs": [{ // 중첩 배열: API별 통계
"sysId": "CAPRI", "apiId": "API-GET", "totCnt": 6,
"statResultTrafcs": [{ // 다시 중첩: 결과별 통계
"rsltType": "F", "totCnt": 6,
"statErrorTrafcs": [{ "errCd": "AGW-E502", "totCnt": 6 }] // 또 중첩: 에러별
}]
}],
"_class": "com.ktds.act.stat.totalization.entity.StatSvcTrafc" // Spring Data 매핑 타입
}3 · 기본 쿼리 (Mongo Shell)
Mongo Shell 로 DB·컬렉션을 탐색하고 문서를 CRUD 한다.
# 탐색
show dbs # 전체 데이터베이스
use admin # 사용 DB 전환
show collections # 현재 DB 의 컬렉션(≈ 테이블) 목록# 생성 (Create)
db.admUser.insertOne({ "userId": "beast@kt.com", "userNm": "Beast", "sttus": "NRML",
"lastLoginDt": ISODate("2022-10-25T04:48:13Z") });
db.admHndlr.insertMany([ { "hndlrId": "REQ.KT-AUTH" }, { "hndlrId": "REQ.KT-AUTH1" } ]);
# 조회 (Read)
db.admUser.find({ "sttus": "NRML" }); // 조건 조회
db.admUser.find({ "loginFailCnt": { $gt: 3 } });// 연산자($gt/$lt/$in …)
# 수정 (Update) / 삭제 (Delete)
db.admUser.updateOne({ "userId": "beast@kt.com" }, { $set: { "sttus": "LOCK" } });
db.admUser.deleteOne({ "userId": "beast@kt.com" });ObjectId·ISODate 같은 BSON 타입과, 다른 문서를 가리키는 DBRef 를 쓸 수 있다. Spring 에서는 Spring Data MongoDB 로 MongoRepository 를 선언하면 위 쿼리들이 메서드로 자동 매핑되고, 저장 시 _class 필드에 자바 타입이 기록된다.
4 · 고가용성 — replicaSet
DB 서버 하나만 있으면 그게 죽는 순간 서비스 전체가 멈춘다. replicaSet 은 같은 데이터를 여러 노드에 복제해 고가용성 을 확보한다(클러스터의 3대 목표: 고가용성·병렬처리·성능). 데이터를 여러 서버에 분산 저장 하는 sharded cluster(수평 확장)와는 목적이 다르다.
- Primary — 쓰기(write) 를 담당. 모든 변경을 oplog(operation log) 에 기록한다.
- Secondary — Primary 의 oplog 를 비동기로 복제 해 같은 데이터를 유지한다.
- Election — Primary 가 죽으면 남은 노드들이 투표 해 새 Primary 를 뽑는다(과반수 필요).
# replicaSet 구성 (각 노드 27017)
rs.initiate({ _id: "replset", members: [{ _id: 0, host: "mongodb-0.mongodb:27017" }] })
rs.add({ _id: 1, host: "mongodb-1.mongodb:27017" })
rs.add({ _id: 2, host: "mongodb-2.mongodb:27017" })
rs.remove("mongodb-2.mongodb:27017")
# 강제 재구성
var config = rs.config(); config.members[0].host = "mongodb5:27022"
rs.reconfig(config, { "force": true })실무 메모 (분당·대전 이중화) — 게이트웨이·백엔드를 분당과 대전 두 데이터센터에 두고 MongoDB 도 양쪽에 replica 를 뒀다. 한쪽 센터가 통째로 죽어도 과반수(quorum) 가 유지되도록 Spare(Passive) 노드 를 추가했다 — 한 센터가 죽어도 남은 센터 + spare 로 과반을 넘겨 election 이 성립한다. 둘 다 죽는 최악도 대비해 데이터 디렉토리 덤프·백업 을 선행하고, DB 끼리 도메인(호스트) 연동으로 동기화했다.
5 · 운영 — 백업 · 파일 저장 · 설치
- Dump & Restore —
mongodump로 데이터 디렉토리를 덤프하고mongorestore로 복구한다(이중화 환경에서 백업을 선행). - 파일 업로드/다운로드 (GridFS) — 16MB 가 넘는 큰 파일은 GridFS 로 청크 분할해 저장한다(
fs.files+fs.chunks). - 설치/배포 — 로컬·EC2·Kubernetes 등에 배포하고,
db.createUser({ user, pwd, roles:["readWrite"] })로 계정·권한을 관리한다.
# 접속 (특정 노드 포트로)
mongo localhost:27018
# 사용자 생성
db.createUser({ user: "act_user", pwd: "****", roles: ["readWrite"] })
# 백업 디렉토리
mkdir ~/dump📓 2022년 제가 KT DS 에서 API 게이트웨이·트래픽 통계 시스템을 운영하며 MongoDB(데이터 구조·쿼리·분당/대전 replicaSet)를 직접 다루고 정리한 노트입니다 · 원본 Notion 에서 보기 ↗