🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

MongoDB 보안과 운영 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 11. 27. · 23 Views

MongoDB 보안과 운영 완벽 가이드

MongoDB를 안전하게 운영하기 위한 필수 보안 설정부터 백업, 모니터링, 성능 튜닝까지 실무에서 꼭 알아야 할 핵심 내용을 다룹니다. 초급 개발자도 따라할 수 있도록 단계별로 쉽게 설명합니다.


목차

  1. 인증 설정 (SCRAM)
  2. 역할 기반 접근 제어
  3. TLS/SSL 암호화
  4. mongodump와 mongorestore
  5. 로그 분석과 모니터링
  6. 성능 튜닝 팁

1. 인증 설정 (SCRAM)

김개발 씨가 처음으로 MongoDB 서버를 구축했습니다. 신나는 마음으로 외부에서 접속해 보니, 아무런 비밀번호 없이도 데이터베이스에 접근할 수 있었습니다.

"어, 이거 괜찮은 건가요?" 옆자리 박시니어 씨의 얼굴이 굳어졌습니다.

**SCRAM(Salted Challenge Response Authentication Mechanism)**은 MongoDB의 기본 인증 방식입니다. 마치 아파트 출입문에 비밀번호를 설정하는 것처럼, 데이터베이스에 접근하려면 반드시 유효한 사용자 이름과 비밀번호를 제시해야 합니다.

이 설정이 없으면 인터넷에 연결된 누구나 여러분의 소중한 데이터를 훔쳐볼 수 있습니다.

다음 코드를 살펴봅시다.

// 1. 관리자 계정 생성 (mongosh에서 실행)
use admin
db.createUser({
  user: "adminUser",
  pwd: "securePassword123!",
  roles: [
    { role: "userAdminAnyDatabase", db: "admin" },
    { role: "readWriteAnyDatabase", db: "admin" }
  ]
})

// 2. mongod.conf에서 인증 활성화
// security:
//   authorization: enabled

// 3. 인증 후 접속
mongosh -u adminUser -p securePassword123! --authenticationDatabase admin

김개발 씨는 입사 첫 주에 MongoDB 서버 구축을 맡았습니다. Docker로 MongoDB를 띄우고, 애플리케이션과 연결하는 것까지는 순조로웠습니다.

그런데 다음 날 출근해 보니 데이터베이스가 텅 비어 있었습니다. 대신 이상한 컬렉션 하나가 생겨 있었는데, 그 안에는 "비트코인을 보내면 데이터를 돌려주겠다"는 협박 메시지가 담겨 있었습니다.

박시니어 씨가 한숨을 쉬며 말했습니다. "인증 설정을 안 했구나.

MongoDB는 기본 설정으로 띄우면 누구나 접속할 수 있어요." 그렇다면 SCRAM이란 정확히 무엇일까요? 쉽게 비유하자면, SCRAM은 마치 은행 금고의 잠금장치와 같습니다.

금고를 열려면 올바른 비밀번호를 알아야 하고, 비밀번호를 여러 번 틀리면 접근이 차단됩니다. SCRAM은 비밀번호를 네트워크로 직접 전송하지 않고, 암호화된 "챌린지-응답" 방식으로 인증합니다.

해커가 중간에서 통신을 가로채도 실제 비밀번호를 알아낼 수 없습니다. MongoDB는 왜 기본적으로 인증이 꺼져 있을까요?

사실 이것은 개발 편의성을 위한 것입니다. 로컬 환경에서 빠르게 테스트할 때 매번 비밀번호를 입력하는 것은 번거롭기 때문입니다.

하지만 이 편의성이 운영 환경에서는 치명적인 보안 구멍이 됩니다. 2017년에는 인증 없이 노출된 MongoDB 서버 수만 대가 해킹당하는 사건이 있었습니다.

위의 코드를 살펴보겠습니다. 먼저 use admin 명령으로 admin 데이터베이스로 이동합니다.

관리자 계정은 반드시 admin 데이터베이스에 생성해야 합니다. db.createUser 함수로 새 사용자를 만드는데, roles 배열에 권한을 지정합니다.

userAdminAnyDatabase는 모든 데이터베이스의 사용자를 관리할 수 있는 권한이고, readWriteAnyDatabase는 모든 데이터베이스의 데이터를 읽고 쓸 수 있는 권한입니다. 계정을 만들었다고 끝이 아닙니다.

mongod.conf 파일에서 authorization: enabled를 설정해야 인증이 실제로 작동합니다. 이 설정 없이는 계정을 만들어도 아무런 의미가 없습니다.

설정 후 MongoDB를 재시작하면, 이제부터는 모든 접속에 사용자 인증이 필요합니다. 실무에서는 어떻게 활용할까요?

운영 환경에서는 반드시 강력한 비밀번호를 사용해야 합니다. 최소 12자 이상, 대소문자, 숫자, 특수문자를 조합하는 것이 좋습니다.

또한 애플리케이션용 계정과 관리자 계정을 분리하여, 애플리케이션이 해킹당해도 전체 시스템이 위험해지지 않도록 해야 합니다. 주의할 점이 있습니다.

비밀번호를 코드에 직접 하드코딩하는 것은 절대 금물입니다. 환경 변수나 시크릿 매니저를 사용하여 비밀번호를 안전하게 관리해야 합니다.

또한 관리자 계정의 비밀번호는 정기적으로 변경하고, 퇴사자가 생기면 즉시 비밀번호를 교체해야 합니다. 김개발 씨는 그날 이후로 모든 데이터베이스에 가장 먼저 인증을 설정하게 되었습니다.

"보안은 나중에가 아니라 처음부터"라는 교훈을 뼈저리게 배웠기 때문입니다.

실전 팁

💡 - 비밀번호는 환경 변수로 관리하고, 코드에 직접 작성하지 마세요

  • 운영 서버는 반드시 인증 활성화 후 배포하세요
  • 관리자 계정과 애플리케이션 계정을 분리하여 사용하세요

2. 역할 기반 접근 제어

인증 설정을 마친 김개발 씨에게 새로운 과제가 주어졌습니다. 마케팅팀에서 고객 데이터를 조회하고 싶다는 요청이 온 것입니다.

"근데 마케팅팀이 데이터를 실수로 삭제하면 어떡하죠?" 박시니어 씨가 웃으며 답했습니다. "그래서 역할 기반 접근 제어가 필요한 거예요."

**RBAC(Role-Based Access Control)**은 사용자마다 다른 권한을 부여하는 시스템입니다. 마치 회사에서 직급에 따라 접근할 수 있는 문서가 다른 것처럼, 데이터베이스에서도 사용자의 역할에 따라 읽기만 가능하거나, 쓰기도 가능하거나, 관리까지 가능하도록 세밀하게 제어할 수 있습니다.

다음 코드를 살펴봅시다.

// 읽기 전용 사용자 생성 (마케팅팀용)
use customers
db.createUser({
  user: "marketingUser",
  pwd: "readOnly2024!",
  roles: [{ role: "read", db: "customers" }]
})

// 읽기/쓰기 가능한 사용자 생성 (개발팀용)
db.createUser({
  user: "devUser",
  pwd: "devWrite2024!",
  roles: [{ role: "readWrite", db: "customers" }]
})

// 특정 컬렉션만 접근 가능한 커스텀 역할 생성
db.createRole({
  role: "orderReader",
  privileges: [
    { resource: { db: "shop", collection: "orders" }, actions: ["find"] }
  ],
  roles: []
})

김개발 씨는 고민에 빠졌습니다. 마케팅팀에게 데이터베이스 접근 권한을 줘야 하는데, 모든 권한을 주기에는 위험해 보였습니다.

지난주에 인턴이 실수로 테이블을 날린 사건이 아직도 생생했기 때문입니다. 박시니어 씨가 화이트보드에 그림을 그리며 설명을 시작했습니다.

"회사 조직도를 생각해 봐요. 대표는 모든 정보에 접근할 수 있지만, 신입사원은 자기 업무에 필요한 정보만 볼 수 있잖아요.

데이터베이스도 마찬가지예요." 역할 기반 접근 제어는 바로 이 원칙을 구현한 것입니다. 마치 호텔의 키카드 시스템과 같습니다.

투숙객은 자기 방과 수영장만 열 수 있고, 청소 직원은 모든 객실을 열 수 있지만 금고실은 못 엽니다. 매니저는 모든 곳을 열 수 있습니다.

각자의 역할에 맞는 권한만 부여하는 것이죠. MongoDB에는 다양한 내장 역할이 있습니다.

read는 데이터를 조회만 할 수 있습니다. 마케팅팀처럼 분석 목적으로 데이터를 보기만 하면 되는 경우에 적합합니다.

readWrite는 조회와 함께 데이터 추가, 수정, 삭제가 가능합니다. 개발팀이나 애플리케이션 계정에 적합합니다.

dbAdmin은 인덱스 생성, 통계 조회 등 데이터베이스 관리 작업을 할 수 있습니다. userAdmin은 사용자 계정을 생성하고 관리할 수 있습니다.

하지만 내장 역할만으로는 부족할 때가 있습니다. 예를 들어 "주문 데이터는 볼 수 있지만 고객 개인정보는 못 보게" 하고 싶다면 어떻게 해야 할까요?

이럴 때 커스텀 역할을 만들 수 있습니다. 위 코드의 세 번째 예제처럼 특정 컬렉션에 대한 특정 작업만 허용하는 역할을 정의할 수 있습니다.

실무에서 RBAC를 설계할 때는 최소 권한 원칙을 따라야 합니다. 필요한 최소한의 권한만 부여하고, 나중에 더 필요하면 그때 추가하는 것이 안전합니다.

처음부터 넉넉하게 권한을 주면, 나중에 회수하기가 어렵습니다. 김개발 씨는 마케팅팀에게 read 권한만 부여했습니다.

마케팅팀 담당자가 "데이터 수정도 하고 싶은데요"라고 요청했지만, "필요한 수정 사항은 요청해 주시면 개발팀에서 처리해 드릴게요"라고 정중히 안내했습니다. 데이터의 무결성을 지키는 것이 더 중요하기 때문입니다.

실전 팁

💡 - 최소 권한 원칙을 따라 필요한 권한만 부여하세요

  • 팀별로 역할을 만들어 관리하면 사용자 추가/삭제가 편리합니다
  • 정기적으로 불필요한 계정과 권한을 정리하세요

3. TLS/SSL 암호화

김개발 씨가 만든 서비스가 잘 운영되고 있었습니다. 그런데 어느 날 보안팀에서 연락이 왔습니다.

"MongoDB 통신이 암호화되지 않아서 데이터가 평문으로 오가고 있어요." 김개발 씨는 당황했습니다. 비밀번호를 설정했는데 뭐가 문제라는 걸까요?

TLS/SSL 암호화는 MongoDB와 클라이언트 사이의 통신을 암호화하는 기술입니다. 마치 중요한 편지를 봉투에 넣고 봉인하는 것처럼, 데이터가 네트워크를 통해 이동할 때 제3자가 엿볼 수 없도록 보호합니다.

인증이 현관문 잠금이라면, TLS는 집 전체를 감싸는 보안 울타리입니다.

다음 코드를 살펴봅시다.

// mongod.conf TLS 설정
// net:
//   port: 27017
//   tls:
//     mode: requireTLS
//     certificateKeyFile: /etc/ssl/mongodb.pem
//     CAFile: /etc/ssl/ca.pem

// TLS로 MongoDB 접속
mongosh --tls \
  --host mongodb.example.com \
  --tlsCertificateKeyFile /path/to/client.pem \
  --tlsCAFile /path/to/ca.pem

// Node.js 애플리케이션에서 TLS 연결
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://user:pass@host:27017/db', {
  tls: true,
  tlsCAFile: '/path/to/ca.pem',
  tlsCertificateKeyFile: '/path/to/client.pem'
});

박시니어 씨가 노트북을 펴며 설명을 시작했습니다. "인증은 문 앞에서 신분증을 확인하는 거예요.

근데 일단 들어온 사람이 안에서 뭘 하는지는 CCTV로 봐야 하잖아요. 그런데 지금 우리 시스템은 CCTV 영상이 암호화 없이 전송되고 있는 거예요." 이것이 바로 전송 구간 암호화가 필요한 이유입니다.

인증을 설정했더라도, 실제 데이터가 네트워크를 통해 이동할 때는 누군가 중간에서 가로챌 수 있습니다. 이를 **중간자 공격(Man-in-the-Middle Attack)**이라고 합니다.

카페 와이파이처럼 공개된 네트워크에서는 특히 위험합니다. TLS는 마치 비밀 편지를 주고받는 것과 같습니다.

발신자와 수신자만 아는 암호로 편지를 작성하면, 중간에 누가 편지를 가로채도 내용을 읽을 수 없습니다. TLS는 이런 암호화를 자동으로 처리해 줍니다.

또한 인증서를 통해 내가 접속한 서버가 진짜 그 서버가 맞는지도 확인합니다. TLS 설정은 크게 세 가지 요소로 구성됩니다.

첫째, **서버 인증서(certificateKeyFile)**는 서버의 신원을 증명합니다. 둘째, **CA 인증서(CAFile)**는 인증서를 발급한 기관의 신뢰성을 검증합니다.

셋째, 클라이언트 인증서는 클라이언트의 신원을 증명하여 양방향 인증을 가능하게 합니다. 운영 환경에서는 requireTLS 모드를 사용해야 합니다.

이 모드에서는 TLS 연결만 허용하고, 암호화되지 않은 연결은 거부합니다. 개발 환경에서는 allowTLS(선택적)나 preferTLS(우선적)를 사용할 수 있지만, 운영 환경에서는 반드시 requireTLS를 사용하세요.

인증서는 어디서 구할까요? 외부에 노출되는 서비스라면 Let's Encrypt 같은 공인 인증 기관에서 무료로 발급받을 수 있습니다.

내부 서비스라면 **자체 CA(Certificate Authority)**를 구축하여 사용하는 것도 방법입니다. AWS를 사용한다면 **ACM(AWS Certificate Manager)**을 활용할 수 있습니다.

김개발 씨는 주말 동안 TLS 설정을 완료했습니다. 처음에는 인증서 경로 문제로 몇 번 삽질을 했지만, 결국 모든 MongoDB 통신이 암호화되었습니다.

보안팀에서 "이제 패킷을 캡처해도 내용을 볼 수 없네요"라며 만족해했습니다.

실전 팁

💡 - 운영 환경에서는 반드시 requireTLS 모드를 사용하세요

  • 인증서 만료일을 모니터링하고 자동 갱신을 설정하세요
  • 자체 서명 인증서보다는 공인 CA 인증서 사용을 권장합니다

4. mongodump와 mongorestore

금요일 오후, 김개발 씨가 실수로 운영 데이터베이스의 중요한 컬렉션을 삭제해 버렸습니다. 식은땀이 흐르는 순간, 박시니어 씨가 침착하게 말했습니다.

"걱정 마요. 백업해 둔 거 있으니까." 그 순간 김개발 씨는 백업의 소중함을 온몸으로 느꼈습니다.

mongodumpmongorestore는 MongoDB의 공식 백업/복구 도구입니다. mongodump는 데이터베이스를 BSON 형식의 파일로 내보내고, mongorestore는 이 파일을 데이터베이스로 복원합니다.

마치 중요한 서류를 복사해서 금고에 보관하는 것처럼, 데이터를 안전하게 백업해 두면 언제든 복구할 수 있습니다.

다음 코드를 살펴봅시다.

# 전체 데이터베이스 백업
mongodump --uri="mongodb://user:pass@localhost:27017" \
  --out=/backup/$(date +%Y%m%d)

# 특정 데이터베이스만 백업
mongodump --uri="mongodb://user:pass@localhost:27017/mydb" \
  --out=/backup/mydb_$(date +%Y%m%d)

# gzip 압축과 함께 백업
mongodump --uri="mongodb://user:pass@localhost:27017" \
  --gzip --archive=/backup/full_$(date +%Y%m%d).gz

# 백업 복원
mongorestore --uri="mongodb://user:pass@localhost:27017" \
  --gzip --archive=/backup/full_20240115.gz

# 특정 컬렉션만 복원
mongorestore --uri="mongodb://user:pass@localhost:27017" \
  --nsInclude="mydb.users" /backup/20240115

"백업 없는 데이터베이스는 언젠가 반드시 문제가 생깁니다." 박시니어 씨가 김개발 씨에게 말했습니다. 실수로 삭제하는 경우, 서버 장애, 랜섬웨어 공격, 심지어 자연재해까지.

데이터를 잃어버릴 수 있는 상황은 무궁무진합니다. 백업은 이런 상황에서 유일한 생명줄입니다.

mongodump는 MongoDB에서 제공하는 공식 백업 도구입니다. 실행하면 데이터베이스의 모든 컬렉션을 BSON 형식의 파일로 저장합니다.

BSON은 Binary JSON의 약자로, MongoDB가 내부적으로 사용하는 데이터 형식입니다. 이 파일들은 나중에 mongorestore로 복원할 수 있습니다.

백업 파일은 압축하는 것이 좋습니다. --gzip 옵션을 사용하면 백업 파일을 압축하여 저장 공간을 절약할 수 있습니다.

대용량 데이터베이스의 경우 압축률이 70-80%에 달하기도 합니다. --archive 옵션을 함께 사용하면 여러 파일 대신 하나의 아카이브 파일로 저장됩니다.

백업은 정기적으로 실행해야 합니다. cron이나 systemd timer를 사용하여 매일 자동으로 백업되도록 설정하세요.

백업 주기는 데이터의 중요도와 변경 빈도에 따라 결정합니다. 금융 데이터라면 몇 시간 단위로, 일반 서비스라면 하루 한 번 정도가 적당합니다.

백업 파일은 다른 위치에 보관해야 합니다. 서버와 같은 디스크에 백업을 저장하면, 디스크 장애 시 원본과 백업을 모두 잃게 됩니다.

AWS S3, Google Cloud Storage 같은 클라우드 스토리지에 백업을 업로드하는 것이 좋습니다. 또한 3-2-1 원칙을 기억하세요.

3개의 백업 복사본을, 2개의 다른 미디어에, 1개는 원격지에 보관합니다. 복원 테스트도 잊지 마세요.

백업이 제대로 되었는지 확인하려면 실제로 복원해 봐야 합니다. 한 달에 한 번은 테스트 환경에서 백업을 복원하여 데이터가 온전한지 확인하세요.

복원이 안 되는 백업은 백업이 아닙니다. 김개발 씨는 삭제한 컬렉션을 5분 만에 복구했습니다.

다행히 당일 새벽에 백업이 실행되어 있었기 때문입니다. 그날 이후로 김개발 씨는 백업 스크립트를 직접 작성하고, 매주 복원 테스트까지 하게 되었습니다.

실전 팁

💡 - 백업 파일은 반드시 원격지(클라우드 등)에도 저장하세요

  • 최소 한 달에 한 번은 복원 테스트를 진행하세요
  • --oplog 옵션으로 특정 시점 복구(Point-in-Time Recovery)를 구현할 수 있습니다

5. 로그 분석과 모니터링

월요일 아침, 서비스가 갑자기 느려졌습니다. 사용자 불만이 쏟아지는데, 김개발 씨는 원인을 찾을 수 없었습니다.

박시니어 씨가 물었습니다. "로그는 확인해 봤어요?" 김개발 씨는 로그가 어디 있는지도 몰랐습니다.

로그 분석과 모니터링은 MongoDB의 건강 상태를 파악하고 문제를 조기에 발견하는 핵심 활동입니다. 마치 자동차 계기판을 보며 연료와 속도를 확인하듯, MongoDB도 다양한 지표를 모니터링해야 합니다.

느린 쿼리, 연결 수, 메모리 사용량 등을 추적하면 문제가 커지기 전에 대응할 수 있습니다.

다음 코드를 살펴봅시다.

// 느린 쿼리 프로파일링 활성화 (100ms 이상)
db.setProfilingLevel(1, { slowms: 100 })

// 프로파일링 결과 조회
db.system.profile.find().sort({ ts: -1 }).limit(5).pretty()

// 서버 상태 확인
db.serverStatus()

// 현재 실행 중인 작업 확인
db.currentOp({ "active": true, "secs_running": { "$gt": 5 } })

// 컬렉션 통계 확인
db.collection.stats()

// mongod.conf 로그 설정
// systemLog:
//   destination: file
//   path: /var/log/mongodb/mongod.log
//   logAppend: true
//   verbosity: 1

시스템에 문제가 생겼을 때 가장 먼저 확인해야 할 곳이 바로 로그입니다. 박시니어 씨가 말했습니다.

"의사가 환자를 진찰할 때 먼저 맥박, 혈압, 체온을 확인하잖아요. 서버도 마찬가지예요.

로그와 모니터링 지표가 바로 서버의 바이탈 사인이에요." MongoDB는 프로파일링 기능을 제공합니다. 프로파일러를 활성화하면 지정한 시간보다 오래 걸리는 쿼리를 자동으로 기록합니다.

레벨 0은 프로파일링 비활성화, 레벨 1은 느린 쿼리만 기록, 레벨 2는 모든 쿼리를 기록합니다. 운영 환경에서는 레벨 1로 설정하고 slowms 값을 적절히 조정하는 것이 좋습니다.

system.profile 컬렉션에서 느린 쿼리를 분석할 수 있습니다. 어떤 쿼리가 얼마나 오래 걸렸는지, 몇 개의 문서를 스캔했는지, 인덱스를 사용했는지 등의 정보가 담겨 있습니다.

이 정보를 바탕으로 쿼리를 최적화하거나 인덱스를 추가할 수 있습니다. **db.serverStatus()**는 서버의 전반적인 상태를 보여줍니다.

현재 연결 수, 메모리 사용량, 네트워크 트래픽, 락 상태 등 다양한 정보를 확인할 수 있습니다. 특히 connections.currentconnections.available에 가까워지면 연결 풀이 부족하다는 신호입니다.

실시간 모니터링에는 **db.currentOp()**을 사용합니다. 현재 실행 중인 작업을 볼 수 있어서, 오래 걸리는 쿼리나 잠긴 작업을 즉시 파악할 수 있습니다.

5초 이상 실행 중인 작업이 있다면 주의 깊게 살펴봐야 합니다. 로그 파일 관리도 중요합니다.

mongod.conf에서 로그 파일 경로를 지정하고, logRotate를 설정하여 로그 파일이 무한정 커지지 않도록 해야 합니다. 로그 레벨은 평소에는 낮게 유지하다가, 문제 발생 시 높여서 상세한 정보를 수집합니다.

김개발 씨는 로그를 분석한 결과, 특정 쿼리가 인덱스 없이 전체 컬렉션을 스캔하고 있다는 것을 발견했습니다. 인덱스를 추가하자 응답 시간이 10분의 1로 줄어들었습니다.

"로그가 이렇게 중요한 거였구나!"

실전 팁

💡 - Grafana + Prometheus 조합으로 시각화된 모니터링 대시보드를 구축하세요

  • 슬랙이나 이메일로 알림을 설정하여 이상 징후를 즉시 파악하세요
  • 프로파일러는 오버헤드가 있으므로 운영 환경에서는 레벨 1만 사용하세요

6. 성능 튜닝 팁

김개발 씨의 서비스가 점점 성장하여 사용자가 10배로 늘었습니다. 하지만 MongoDB는 예전처럼 빠르지 않았습니다.

"사용자가 늘면 느려지는 건 당연한 거 아니에요?" 박시니어 씨가 고개를 저었습니다. "제대로 튜닝하면 10배는 더 버틸 수 있어요."

성능 튜닝은 MongoDB가 더 빠르고 효율적으로 동작하도록 최적화하는 작업입니다. 인덱스 설계, 쿼리 최적화, 하드웨어 리소스 조정 등 다양한 방법이 있습니다.

마치 자동차의 엔진을 튜닝하여 연비와 출력을 높이듯, 데이터베이스도 튜닝을 통해 훨씬 나은 성능을 낼 수 있습니다.

다음 코드를 살펴봅시다.

// 복합 인덱스 생성 (쿼리 패턴에 맞게)
db.orders.createIndex({
  userId: 1,
  createdAt: -1
}, { background: true })

// 쿼리 실행 계획 분석
db.orders.find({ userId: "user123" })
  .sort({ createdAt: -1 })
  .explain("executionStats")

// 커버링 인덱스로 최적화
db.products.createIndex({
  category: 1,
  price: 1,
  name: 1
})
// 이 쿼리는 인덱스만으로 결과 반환 (문서 접근 불필요)
db.products.find(
  { category: "electronics" },
  { price: 1, name: 1, _id: 0 }
).sort({ price: 1 })

// 연결 풀 설정 (Node.js)
const client = new MongoClient(uri, {
  maxPoolSize: 100,
  minPoolSize: 10,
  maxIdleTimeMS: 30000
});

성능 튜닝의 핵심은 인덱스입니다. 박시니어 씨가 말했습니다.

"도서관에서 책을 찾을 때 책장을 하나씩 뒤지면 얼마나 걸릴까요? 하지만 목록표가 있으면 바로 찾을 수 있죠.

인덱스가 바로 그 목록표예요." 인덱스 없이 쿼리를 실행하면 **컬렉션 스캔(Collection Scan)**이 발생합니다. 문서가 1000만 개라면, 원하는 데이터를 찾기 위해 1000만 개를 모두 확인해야 합니다.

하지만 적절한 인덱스가 있으면 B-트리 구조를 통해 로그 시간 복잡도로 데이터를 찾을 수 있습니다. 1000만 개 중에서 단 23번의 비교만으로 원하는 데이터에 도달합니다.

**복합 인덱스(Compound Index)**를 설계할 때는 쿼리 패턴을 고려해야 합니다. ESR(Equality, Sort, Range) 규칙을 기억하세요.

등호 조건 필드를 먼저, 정렬 필드를 그 다음, 범위 조건 필드를 마지막에 배치합니다. 예를 들어 userId로 필터링하고 createdAt으로 정렬한다면, { userId: 1, createdAt: -1 } 순서가 최적입니다.

**커버링 인덱스(Covering Index)**는 더욱 강력합니다. 쿼리에 필요한 모든 필드가 인덱스에 포함되어 있으면, MongoDB는 실제 문서에 접근하지 않고 인덱스만으로 결과를 반환합니다.

디스크 I/O를 줄여 성능이 크게 향상됩니다. **explain()**은 쿼리가 어떻게 실행되는지 보여줍니다.

COLLSCAN이 보이면 인덱스를 사용하지 않는다는 의미입니다. IXSCAN이 보여야 인덱스를 활용하는 것입니다.

executionStats 모드로 실행하면 실제로 몇 개의 문서를 스캔했는지도 확인할 수 있습니다. 연결 풀(Connection Pool) 설정도 중요합니다.

매 요청마다 새로운 연결을 만들면 오버헤드가 큽니다. 연결 풀을 사용하면 미리 만들어둔 연결을 재사용합니다.

maxPoolSize는 동시 요청 수를 고려하여 설정하고, minPoolSize는 최소 유지 연결 수입니다. 그 외에도 몇 가지 팁이 있습니다.

Working Set이 RAM보다 크면 성능이 급격히 떨어집니다. 자주 접근하는 데이터가 메모리에 올라가 있어야 빠르게 응답할 수 있습니다.

또한 Write Concern을 상황에 맞게 조정하면 쓰기 성능을 개선할 수 있습니다. 김개발 씨는 인덱스를 추가하고 연결 풀을 조정한 결과, 평균 응답 시간이 500ms에서 50ms로 줄어들었습니다.

"같은 하드웨어인데 이렇게 차이가 나다니!" 박시니어 씨가 웃으며 말했습니다. "튜닝의 힘이에요."

실전 팁

💡 - 모든 쿼리에 explain()을 실행하여 인덱스 사용 여부를 확인하세요

  • 인덱스가 많으면 쓰기 성능이 저하되므로, 꼭 필요한 인덱스만 유지하세요
  • 데이터 증가에 대비하여 정기적으로 쿼리 성능을 점검하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#MongoDB#Security#Authentication#Backup#Performance#MongoDB,Database,NoSQL

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.