일반적인 MongoDB 명령 오류 효과적으로 해결하기

mongosh에서 발생하는 일반적인 MongoDB 명령 오류(구문, 인증, 연결, 쓰기, 복제 세트 문제)를 해결합니다.

일반적인 MongoDB 명령 오류 효과적으로 해결하기

MongoDB 명령 오류는 속도를 늦추고 실패가 발생하는 위치를 식별할 때 더 쉽게 수정할 수 있습니다. mongosh가 입력한 내용을 구문 분석하지 못했나요? 사용자에게 역할이 없어 서버가 명령을 거부했나요? 클라이언트가 잘못된 데이터베이스에 연결되었나요? 프라이머리를 사용할 수 없나요? 이들은 모두 셸에서 빨간색 텍스트로 표시되더라도 서로 다른 문제입니다.

저는 세 가지 확인부터 시작하는 것을 좋아합니다: 어떤 서버에 연결되어 있는지, 어떤 데이터베이스를 사용하고 있는지, 어떤 사용자로 인증되었는지.

db.getName()
db.hello()
db.runCommand({ connectionStatus: 1 })

이 명령들은 많은 디버깅 낭비를 방지합니다. 많은 "MongoDB 명령 오류"는 실제로 컨텍스트 실수입니다: 잘못된 데이터베이스에서 관리 명령 실행, 잘못된 authSource에 대해 인증, 또는 프라이머리에 쓰려고 할 때 세컨더리에 대해 테스트하는 경우.

MongoDB 명령 오류 범주 이해

MongoDB 명령 오류는 일반적으로 몇 가지 주요 유형으로 분류할 수 있습니다:

  • 구문 오류: MongoDB 셸 또는 드라이버가 구문 분석할 수 없는 잘못된 형식의 명령.
  • 권한 오류: 필요한 사용자 권한 없이 작업을 수행하려는 시도.
  • 운영 오류: 네트워크 문제, 리소스 제한 또는 데이터 불일치와 같이 명령 실행 중에 발생하는 문제.
  • 연결 오류: MongoDB 서버에 연결을 설정하는 데 문제가 발생.

일반적인 구문 오류 및 해결 방법

구문 오류는 종종 가장 간단하게 수정할 수 있으며, 일반적으로 오타, 누락된 문자 또는 잘못된 매개변수 사용에서 비롯됩니다. MongoDB 셸(mongosh)은 일반적으로 이러한 문제에 대해 유용한 오류 메시지를 제공합니다.

1. 잘못된 필드 이름 또는 문서 구조

문서를 삽입하거나 업데이트할 때 잘못된 필드 이름이나 유효하지 않은 문서 구조를 사용하면 오류가 발생할 수 있습니다.

예시 오류:

db.users.insertOne({ "bad\u0000field": "Alice" })
// MongoServerError: The dotted field 'bad\u0000field' ... is not valid for storage

설명: MongoDB 필드 이름에는 null 문자가 포함될 수 없습니다. "email-address"와 같이 하이픈이 있는 필드 이름은 허용되지만, 따옴표로 묶어야 하므로 쿼리하기가 불편할 수 있습니다. 실제 문제는 저장할 수 없는 잘못된 문자 또는 구조입니다.

해결 방법:

가져오기, 사용자 입력 또는 직렬화 코드에서 생성된 필드 이름을 주의 깊게 검토하세요. emailAddress 또는 email_address와 같은 일관된 명명 규칙을 선택하고 데이터가 MongoDB에 도달하기 전에 유효성을 검사하세요.

> db.users.insertOne({ name: "Alice", age: 30, emailAddress: "[email protected]" })
{ acknowledged: true, insertedId: ObjectId('...') }

2. 누락되거나 추가된 쉼표

JavaScript와 유사하게 MongoDB 셸 명령은 객체와 배열 내에서 쉼표의 올바른 배치에 민감합니다.

예시 오류:

db.products.insertOne({ name: "Laptop", price: 1200,, })
// SyntaxError: Unexpected token

해결 방법:

불필요한 쉼표를 제거하세요. 가독성을 위해 일관된 형식을 유지하세요.

> db.products.insertOne({ name: "Laptop", price: 1200 })
{ acknowledged: true, insertedId: ObjectId('...') }

3. 잘못된 명령 구문 (예: find vs. findOne)

잘못된 명령을 사용하거나 인수를 잘못된 순서로 제공하면 오류가 발생할 수 있습니다.

예시:

db.inventory.find({ item: "notebook" }, { qty: 1, size: 1, _id: 0 })
// 이 명령은 find에 대해 구문적으로 올바르지만, 하나의 문서만 검색하려는 경우:

해결 방법:

단일 문서만 검색하려면 findOne을 사용하세요. find는 커서를 반환하는 반면 findOne은 문서 자체를 반환합니다.

> db.inventory.findOne({ item: "notebook" }, { qty: 1, size: 1, _id: 0 })
{
  qty: 20,
  size: { h: 14, w: 21, uom: "cm" },
  ... // 프로젝션이 제외하지 않았고 특별히 프로젝션되지 않은 경우 다른 필드
}

일반적인 권한 오류

권한 오류는 일반적으로 사용자에게 필요한 역할이나 권한이 없는 작업을 수행하려고 할 때 발생합니다.

1. 명령 실행 권한 부족

이 오류 메시지는 권한 부족에 대해 명확하게 알려줍니다.

예시 오류:

> db.adminCommand({ listDatabases: 1 })
Error: listDatabases requires authentication

설명: listDatabases는 인증과 적절한 권한이 필요한 관리 명령입니다. 사용자는 하나의 애플리케이션 데이터베이스에 대해 유효할 수 있지만 전체 배포를 검사할 수는 없습니다.

해결 방법:

  • 적절한 자격 증명으로 인증: 필요한 역할이 있는 사용자를 사용하여 MongoDB에 연결하세요. listDatabases의 경우 관리자로 연결해야 할 수 있습니다.
    mongosh "mongodb://<adminUser>:<adminPassword>@<host>:<port>/admin?authSource=admin"
    
    그런 다음 명령을 다시 시도하세요:
    db.adminCommand({ listDatabases: 1 })
    
  • 역할 부여: 데이터베이스 관리자인 경우 문제가 발생하는 사용자에게 필요한 역할을 부여하세요.
    // 예시: 'admin' 데이터베이스에서 사용자 'myUser'에게 readAnyDatabase 역할 부여
    use admin
    db.grantRolesToUser("myUser", [ { role: "readAnyDatabase", db: "admin" } ])
    

2. 쓰기 작업 거부

쓰기 권한 없이 컬렉션이나 데이터베이스에 문서를 삽입, 업데이트 또는 삭제하려고 시도합니다.

예시 오류:

> db.myCollection.insertOne({ name: "Test" })
WriteError: Not enough privileges to execute on "myCollection" with operation "insert"

해결 방법:

  • 대상 데이터베이스/컬렉션에 대한 쓰기 권한이 있는 사용자로 인증하세요.
  • 사용자에게 쓰기 역할 (예: readWrite, dbOwner)을 부여하세요.
  • 프롬프트의 데이터베이스를 확인하세요. appdbreadWrite가 부여된 사용자는 컬렉션 이름이 같더라도 test에 자동으로 쓰기 액세스 권한이 없습니다.

인증 및 authSource 실수

인증 오류는 MongoDB 사용자가 특정 인증 데이터베이스에 속하기 때문에 종종 혼란스럽게 보입니다. 많은 애플리케이션 사용자가 애플리케이션 데이터베이스에 생성됩니다. 관리 사용자는 종종 admin에 생성됩니다.

다음과 같은 메시지가 표시되면:

MongoServerError: Authentication failed.

비밀번호를 변경하기 전에 연결 문자열을 확인하세요:

mongosh "mongodb://appUser:secret@db01:27017/appdb?authSource=appdb"

사용자가 admin에 생성된 경우 다음을 사용하세요:

mongosh "mongodb://adminUser:secret@db01:27017/appdb?authSource=admin"

호스트 뒤의 데이터베이스 경로(/appdb)는 셸이 열리는 기본 데이터베이스입니다. authSource는 MongoDB가 사용자 계정을 찾는 위치입니다. 이 둘을 혼동하는 것은 로컬 개발 데이터베이스에서 보안 서버로 이동한 후 로그인 실패의 일반적인 원인입니다.

다음을 사용하여 현재 인증된 사용자를 확인할 수 있습니다:

db.runCommand({ connectionStatus: 1 })

일반적인 운영 오류 및 해결 방법

운영 오류는 더 복잡할 수 있으며, 종종 MongoDB 배포 상태, 네트워크 문제 또는 리소스 제약과 관련됩니다.

1. 네트워크 시간 초과 또는 연결 거부

이러한 오류는 클라이언트가 MongoDB 서버와 연결을 설정하거나 유지할 수 없음을 나타냅니다.

예시 오류 (클라이언트 측):

Error: connect ECONNREFUSED 127.0.0.1:27017

설명: 클라이언트가 지정된 호스트 및 포트에 연결을 시도했지만 연결이 거부되었습니다. 이는 MongoDB 서버가 실행 중이 아니거나, 다른 포트에서 실행 중이거나, 방화벽이 연결을 차단하고 있음을 의미할 수 있습니다.

해결 방법:

  • MongoDB 서버 상태 확인: 서버에서 mongod 프로세스가 실행 중인지 확인하세요.
    • Linux: sudo systemctl status mongod 또는 sudo service mongod status
    • macOS (Homebrew 사용): brew services list
    • Windows: 서비스 애플리케이션을 확인하세요.
  • MongoDB 구성 확인: mongod가 올바른 IP 주소와 포트(기본값은 27017)에서 수신 대기하도록 구성되었는지 확인하세요. mongod.conf 파일을 확인하세요.
  • 방화벽 규칙: 서버 수준 또는 네트워크 방화벽이 MongoDB 포트의 트래픽을 차단하지 않는지 확인하세요.
  • 올바른 연결 문자열: 호스트 및 포트의 오타에 대해 연결 문자열을 다시 확인하세요.

복제 세트의 경우 드라이버가 예상할 때 복제 세트 이름을 포함하세요:

mongosh "mongodb://db01:27017,db02:27017,db03:27017/appdb?replicaSet=rs0"

DNS가 관련된 경우 클라이언트 시스템에서 정확한 호스트 이름을 테스트하세요:

nc -vz db01.example.com 27017

데이터베이스 서버 자체에서의 성공적인 연결은 앱 서버, CI 실행기 또는 배스천 호스트가 연결할 수 있음을 증명하지 않습니다.

2. 문서 크기 제한 초과

MongoDB 문서에는 최대 BSON 크기 제한(현재 16MB)이 있습니다.

예시 오류:

> db.largeDocs.insertOne({ data: "... 매우 큰 문자열 ..." })
Error: BSONObj size: 17000000 bytes is too large, max 16777216 bytes

해결 방법:

  • 큰 문서 분할: 큰 문서를 더 작은 관련 문서로 나누세요. 참조(예: ObjectId)를 사용하여 연결하세요.
  • GridFS 사용: 문서 크기 제한을 초과하는 큰 바이너리 파일(예: 이미지 또는 비디오)을 저장하려면 MongoDB의 GridFS 사양을 사용하세요.
  • 무제한 배열 피하기: 모든 이벤트가 영원히 포함된 하나의 사용자 문서와 같이 계속 성장하는 문서는 결국 느려지거나 제한에 도달할 수 있습니다. 볼륨이 큰 하위 레코드는 자체 컬렉션에 저장하세요.

3. 쓰기 고려(Write Concern) 오류

쓰기 고려는 MongoDB가 쓰기 작업에 대해 요구하는 승인 보장을 지정합니다. 이러한 보장이 시간 초과 내에 충족되지 않으면 쓰기 고려 오류가 발생합니다.

예시:

// 특정 쓰기 고려를 사용한 쓰기 작업 예시
db.myCollection.insertOne({ name: "Item" }, { writeConcern: { w: "majority", wtimeout: 1000 } });

잠재적 오류:

WriteConcernError: waiting for replication timed out

설명: 필요한 노드 수(이 경우 majority)가 지정된 wtimeout(1000ms) 내에 쓰기를 승인하지 않아 쓰기 작업이 실패했습니다.

해결 방법:

  • 복제 세트 상태 조사: MongoDB 복제 세트의 상태를 확인하세요. 노드가 지연되고 있나요? 노드 간 네트워크 문제가 있나요?
  • wtimeout 증가: 일시적인 네트워크 지연 또는 복제 지연이 원인인 경우 wtimeout 값을 증가시키는 것을 고려할 수 있지만, 이는 근본적인 문제를 가릴 수 있으므로 신중하게 수행해야 합니다.
  • 쓰기 고려 검토: 쓰기 고려 수준(w)이 애플리케이션 요구 사항에 적합한지 확인하세요. w: 1(기본값)은 프라이머리만 승인하면 되므로 시간 초과 문제가 덜 발생하지만 내구성 보장이 낮습니다.

4. 프라이머리가 아님 또는 읽기 기본 설정 오류

쓰기는 복제 세트의 프라이머리로 가야 합니다. 셸 또는 애플리케이션이 세컨더리에 연결되어 있고 쓰기를 시도하면 다음과 유사한 오류가 표시될 수 있습니다:

MongoServerError: not primary

연결된 위치를 확인하세요:

db.hello()

isWritablePrimary가 false인 경우 적절한 복제 세트 URI를 통해 연결하거나 쓰기를 프라이머리로 라우팅하세요. 읽기의 경우 애플리케이션에 세컨더리 읽기가 허용되는지 결정하세요. 세컨더리 읽기는 오래된 데이터일 수 있으므로, 해당 사용 사례에 대해 오래된 읽기가 안전하지 않은 경우 오류를 무시하기 위해 활성화하지 마세요.

5. 중복 키 오류

중복 키 오류는 일반적으로 고유 인덱스가 제 역할을 하고 있음을 의미합니다.

E11000 duplicate key error collection: app.users index: email_1 dup key

고유성 규칙이 실제로 잘못된 경우가 아니라면 고유 인덱스를 삭제하여 "수정"하지 마세요. 먼저 인덱스를 식별하세요:

db.users.getIndexes()

그런 다음 애플리케이션이 기존 문서를 업데이트해야 하는지, 중복을 거부해야 하는지, 또는 upsert를 사용해야 하는지 결정하세요:

db.users.updateOne(
  { email: "[email protected]" },
  { $set: { lastLoginAt: new Date() } },
  { upsert: true }
)

Upsert를 사용할 때 필터가 관심 있는 고유 키와 일치하는지 확인하세요. 느슨한 필터는 여전히 다른 고유 필드에 중복을 생성하여 실패할 수 있습니다.

6. 쿼리 연산자 실수

일부 오류는 업데이트 또는 쿼리 연산자를 잘못된 위치에 사용하여 발생합니다.

다음은 교체 스타일 업데이트에 대해 잘못된 경우입니다:

db.users.updateOne(
  { email: "[email protected]" },
  { lastLoginAt: new Date() }
)

MongoDB는 두 번째 문서를 교체 문서로 취급합니다. 하나의 필드를 변경하려는 경우 $set을 사용하세요:

db.users.updateOne(
  { email: "[email protected]" },
  { $set: { lastLoginAt: new Date() } }
)

이 차이는 교체 업데이트가 교체 문서에 포함되지 않은 필드를 제거할 수 있기 때문에 중요합니다.

실용적인 디버깅 루틴

명령이 실패하면 정확한 오류를 캡처한 다음 다음 질문에 순서대로 답하세요:

  1. mongosh가 예상된 호스트에 연결되어 있나요?

    db.hello().me
    
  2. 예상된 데이터베이스를 사용하고 있나요?

    db.getName()
    
  3. 인증되었으며 어떤 역할을 가지고 있나요?

    db.runCommand({ connectionStatus: 1 })
    
  4. 구문 분석 오류, 서버 오류 또는 쓰기 고려 오류인가요?

    구문 분석 오류는 일반적으로 명령이 서버에 도달하기 전에 나타납니다. 권한, 중복 키, 프라이머리가 아님, 유효성 검사 및 쓰기 고려 오류는 MongoDB가 명령을 평가한 후에 발생합니다.

  5. 최소 문서로 동일한 명령이 작동하나요?

    작은 삽입이 작동하지만 실제 삽입이 실패하면 문서 모양, 크기, 필드 이름, 스키마 유효성 검사 및 고유 인덱스를 검사하세요.

  6. 실패가 노드에 따라 달라지나요?

    복제 세트 및 샤드 클러스터 문제는 특정 노드 또는 특정 라우터를 통해서만 나타날 수 있습니다. 샤드 클러스터의 경우 애플리케이션 트래픽은 일반적으로 mongos를 통해 이동해야 하며 샤드 멤버로 직접 이동해서는 안 됩니다.

추측 없이 로그 읽기

MongoDB 로그는 종종 셸 출력보다 서버 측 이유를 더 명확하게 설명합니다. Linux 패키지 설치의 경우 서비스 로그를 확인하세요:

journalctl -u mongod --since "30 minutes ago"

또는 mongod.conf에서 구성된 MongoDB 로그 경로를 확인하세요.

실패한 명령과 동일한 타임스탬프 주변의 메시지를 찾으세요. 유용한 단서로는 인증 실패, 연결 재설정, 느린 작업, 복제 상태 변경, 디스크 전체 오류 및 스키마 유효성 검사 실패가 있습니다.

느리거나 실패하는 쿼리의 경우 explain()이 추측보다 더 유용합니다:

db.orders.find({ customerId: 123, status: "open" }).explain("executionStats")

쿼리가 적은 결과를 반환하기 위해 많은 수의 문서를 스캔하는 경우 명령은 구문적으로 올바르지만 운영 비용이 많이 들 수 있습니다. 이는 셸 문제가 아니라 인덱싱 또는 쿼리 형태 문제입니다.

명령 오류 방지를 위한 모범 사례

  • mongosh 및 해당 기능 사용: 최신 MongoDB 셸에서 제공하는 탭 완성, 명령 기록 및 명확한 오류 메시지를 활용하세요.
  • 데이터 모델 이해: 과도하게 큰 문서나 비효율적인 쿼리와 같은 문제를 피하기 위해 스키마와 문서 구조를 신중하게 설계하세요.
  • 적절한 인증 및 권한 부여 구현: 역할에 필요한 최소 권한으로 사용자를 정의하세요.
  • 배포 모니터링: 잠재적인 문제를 사전에 식별하기 위해 정기적으로 MongoDB 로그, 성능 메트릭 및 복제 세트 상태를 확인하세요.
  • 명령 테스트: 복잡한 명령이나 변경 사항을 프로덕션에 배포하기 전에 개발 또는 스테이징 환경에서 철저히 테스트하세요.
  • 계획된 일정에 따라 MongoDB 업데이트 유지: 최신 버전에는 버그 수정 및 동작 변경이 포함될 수 있습니다. 릴리스 노트를 읽고 프로덕션 롤아웃 전에 업그레이드를 테스트하세요.

대부분의 MongoDB 명령 오류는 셸 구문, 인증 컨텍스트, 권한 부여, 토폴로지 및 데이터 형태 문제를 분리하면 관리하기 쉬워집니다. 연결된 위치와 자신이 누구인지 증명하는 것부터 시작하세요. 그런 다음 정확한 오류 메시지가 명령을 무작위로 다시 작성하는 대신 다음 계층으로 안내하도록 하세요.