← Documents

MongoDB · NoSQL 문서 DB

🍃

한 줄 소개 — MongoDB 는 행·열의 테이블이 아니라 JSON 형태의 문서(Document) 로 데이터를 저장하는 NoSQL 문서 DB 다. 스키마가 유연하고 중첩 구조를 그대로 담을 수 있다. 이 글은 데이터 구조 · 기본 쿼리 · 고가용성 replicaSet 을, KT 에서 API 트래픽 통계·게이트웨이 저장소로 MongoDB 를 운영하며 정리한 실무 예시와 함께 다룬다.

1 · MongoDB 란? (RDB vs Document)

관계형 DB(RDB)는 데이터를 테이블(행·열) 로 정규화해 나누고 JOIN 으로 잇는다. MongoDB 는 연관된 데이터를 하나의 문서 안에 중첩(임베딩) 해 담는다 — 스키마가 고정되지 않아 유연하고, 한 번의 조회로 연관 데이터를 함께 읽을 수 있다.

RDB — 정규화된 테이블 idnameteam_id 1Lee10 2Kim10 team_idname10Backend 테이블을 나누고 JOIN (FK) MongoDB — 임베딩 문서 { "_id": ObjectId("…"), "name": "Lee", "team": { "name": "Backend", "members": ["Lee","Kim"] } } 연관 데이터를 한 문서에 중첩
RDB 는 테이블로 쪼개 JOIN, MongoDB 는 연관 데이터를 하나의 문서에 임베딩한다

2 · 데이터 구조 — Database · Collection · Document

MongoDB 의 계층은 Database > Collection > Document 다. RDB 에 대응시키면 Collection ≈ Table, Document ≈ Row 이지만, Document 는 BSON(Binary JSON)으로 자유로운 중첩 구조를 가진다. 모든 문서엔 고유 _id(ObjectId)가 자동 부여된다.

Databaseconfig, test … Collection≈ Table (admUser) Document (BSON)≈ Row · _id(ObjectId)중첩 객체·배열 가능 Database 안에 여러 Collection, Collection 안에 여러 Document
Database > Collection > Document 계층. Document 는 BSON 으로 중첩 객체·배열을 그대로 담는다

실제 운영했던 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 MongoDBMongoRepository 를 선언하면 위 쿼리들이 메서드로 자동 매핑되고, 저장 시 _class 필드에 자바 타입이 기록된다.

4 · 고가용성 — replicaSet

DB 서버 하나만 있으면 그게 죽는 순간 서비스 전체가 멈춘다. replicaSet 은 같은 데이터를 여러 노드에 복제해 고가용성 을 확보한다(클러스터의 3대 목표: 고가용성·병렬처리·성능). 데이터를 여러 서버에 분산 저장 하는 sharded cluster(수평 확장)와는 목적이 다르다.

  • Primary쓰기(write) 를 담당. 모든 변경을 oplog(operation log) 에 기록한다.
  • Secondary — Primary 의 oplog 를 비동기로 복제 해 같은 데이터를 유지한다.
  • Election — Primary 가 죽으면 남은 노드들이 투표 해 새 Primary 를 뽑는다(과반수 필요).
replicaSet — Primary / Secondary PRIMARYwrite · oplog SECONDARYread · replicate SECONDARYread · replicate oplog 복제(async) Primary 장애 시 → election (과반수 투표)
Primary 가 쓰기·oplog 를 담당하고 Secondary 들이 비동기 복제. 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 & Restoremongodump 로 데이터 디렉토리를 덤프하고 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 에서 보기 ↗