/개발/MongoDB bad auth Authentication failed (code 18) 진단표로 즉시 해결
개발MongoDB인증에러

MongoDB bad auth Authentication failed (code 18) 진단표로 즉시 해결

MongoDB 'bad auth : Authentication failed (code 18)'를 30초 진단표로 원인 특정하고 복붙 명령으로 복구하세요. authSource 누락, 비밀번호 URL 인코딩, SCRAM mismatch를 mongosh·Node·pymongo 예제로 해결합니다.

MongoDB bad auth Authentication failed (code 18) 진단표로 즉시 해결

MongoServerError bad auth Authentication failed (code 18) 30초 진단표로 즉시 해결

"비밀번호 맞게 쳤는데 왜 bad auth가 뜨죠?"

mongosh나 백엔드 앱에서 이 메시지를 만나고 이 글에 도착하셨을 겁니다.

CODE
MongoServerError: bad auth : Authentication failed.

또는 드라이버 로그에서:

CODE
MongoServerError: Authentication failed. (code 18)

99%의 개발자가 여기서 비밀번호를 세 번쯤 다시 칩니다. 하지만 MongoDB 인증의 핵심은 비밀번호가 아니라 "이 유저가 어느 DB에 주민등록(authSource)되어 있는가" 입니다. MySQL이나 PostgreSQL에서 넘어온 분들이 특히 여기서 막힙니다. 유저는 admin에 살고 있는데, 여러분은 mydb에 대고 로그인을 시도하니 "그런 사람 없는데요"라며 인증이 튕기는 겁니다.

이 글은 처음부터 끝까지 읽는 글이 아닙니다. 30초 안에 5가지 원인 중 하나를 특정하고, 복붙 명령으로 접속을 복구하는 진단 도구입니다.

30초 진단표: 내 에러는 5가지 중 무엇인가

가장 먼저 감별할 것은 인증 실패인지 권한 부족인지입니다. 이 둘은 완전히 다른 문제입니다.

증상(에러 메시지)code확인 명령원인바로가기
bad auth : Authentication failed18접속 URI에 ?authSource=admin 있는지 확인유저가 소속된 DB(authSource) 불일치Case 1
bad auth (비번에 @ # : / 포함)18connection string 원문 확인비밀번호 특수문자 URL 인코딩 누락Case 2
Authentication failed (신규 클라이언트)18db.getUser(...,{showCredentials:true})SCRAM-SHA-1 vs 256 mismatchCase 3
not authorized on X to execute command13db.getUser("user") roles 확인인증은 성공, 권한 부족Case 4
bad auth (막 --auth 켠 신규 서버)18system.users 비어있는지 확인admin 유저 미생성Case 5

핵심 감별점 두 가지만 기억하세요.

  1. code 18 (bad auth) = 인증 실패입니다. 유저·비번·authSource 중 하나가 틀렸습니다.
  2. code 13 (not authorized) = 인증은 성공했으나 명령을 실행할 권한이 없습니다. 비밀번호는 맞았다는 뜻이니 절대 비번을 다시 치지 마세요.

유저가 어디에 등록됐는지 한 방에 보는 원라이너:

JavaScript
// 관리자로 접속한 상태에서 실행
db.getSiblingDB("admin").system.users.find({}, {user:1, db:1, "credentials":1})

여기서 나오는 db 필드가 바로 그 유저의 authSource입니다. 대부분 admin으로 찍힙니다. 그럼 접속할 때도 authSource=admin을 붙여야 합니다.

케이스별 원인 진단 + 복붙 복구 명령

Case 1. authSource 미지정 (가장 흔함)

admin이 아닌 DB에 유저를 만들었거나(또는 Docker 루트 유저처럼 admin에 있는데), 접속 시 authSource를 빼먹은 경우입니다.

진단:

JavaScript
db.getSiblingDB("admin").system.users.find({user:"myuser"}, {user:1, db:1})
// db: "admin" 으로 나오면 → 접속 시 authSource=admin 필요

복구 — mongosh:

Bash
mongosh "mongodb://myuser:mypass@localhost:27017/mydb?authSource=admin"

Node.js (공식 mongodb 드라이버):

JavaScript
const { MongoClient } = require("mongodb");
const uri = "mongodb://myuser:mypass@localhost:27017/mydb?authSource=admin";
const client = new MongoClient(uri);
await client.connect();

Python (pymongo):

Python
from pymongo import MongoClient
client = MongoClient(
    "localhost", 27017,
    username="myuser", password="mypass",
    authSource="admin"
)
# 또는 URI 형태
client = MongoClient("mongodb://myuser:mypass@localhost:27017/mydb?authSource=admin")

Case 2. 비밀번호 특수문자 URL 인코딩

비밀번호에 @ : / ? # % 같은 문자가 있으면 connection string 파서가 URI 구조로 오인해 깨집니다. 예를 들어 비번이 p@ss:w0rd라면 @를 호스트 구분자로 읽어버립니다.

URL 인코딩 대응표:

문자인코딩문자인코딩
@%40#%23
:%3A%%25
/%2F?%3F

Node.js 자동 인코딩:

JavaScript
const user = encodeURIComponent("myuser");
const pass = encodeURIComponent("p@ss:w0rd");
const uri = `mongodb://${user}:${pass}@localhost:27017/mydb?authSource=admin`;

Python 자동 인코딩:

Python
from urllib.parse import quote_plus
from pymongo import MongoClient
uri = "mongodb://%s:%s@localhost:27017/mydb?authSource=admin" % (
    quote_plus("myuser"), quote_plus("p@ss:w0rd"))
client = MongoClient(uri)

실무 팁: 저는 아예 팀 컨벤션으로 "비밀번호는 URI에 직접 넣지 말고 드라이버의 username/password 파라미터로 분리한다"를 못 박아뒀습니다. pymongo나 Node 드라이버 모두 파라미터로 넘기면 인코딩 함정을 원천 차단할 수 있어서, 이 방식으로 바꾼 뒤 Case 2 문의가 사라졌습니다.

Case 3. SCRAM-SHA-1 vs SCRAM-SHA-256 mismatch

MongoDB 6.x/7.x의 기본 메커니즘은 SCRAM-SHA-256입니다. 오래된 스크립트로 만든 유저가 SHA-1만 갖고 있거나, 클라이언트가 협상에 실패하면 인증이 튕깁니다.

진단 — 저장된 메커니즘 확인:

JavaScript
db.getUser("myuser", { showCredentials: true })
// credentials 객체에 SCRAM-SHA-1 / SCRAM-SHA-256 키가 보인다

복구 — 메커니즘 재설정(양쪽 다 부여):

JavaScript
db.updateUser("myuser", {
  mechanisms: ["SCRAM-SHA-256", "SCRAM-SHA-1"],
  pwd: "mypass"   // 메커니즘 갱신 시 비번 재설정 필요
})

클라이언트에서 명시적으로 지정:

JavaScript
// Node.js
const uri = "mongodb://myuser:mypass@localhost:27017/mydb"
          + "?authSource=admin&authMechanism=SCRAM-SHA-256";
Python
# pymongo
client = MongoClient(
    "localhost", 27017,
    username="myuser", password="mypass",
    authSource="admin", authMechanism="SCRAM-SHA-256"
)

Case 4. 유저는 있으나 roles 부족 (bad auth 아님!)

not authorized on mydb to execute command(code 13)가 떴다면 로그인은 성공한 겁니다. 역할만 붙여주면 됩니다.

진단:

JavaScript
db.getUser("myuser")   // roles 배열이 비었거나 부족한지 확인

복구:

JavaScript
db.getSiblingDB("admin").grantRolesToUser("myuser", [
  { role: "readWrite", db: "mydb" }
])

Case 5. --auth 활성화 후 최초 admin 유저 미생성

--auth를 켰는데 admin 유저를 안 만들었다면, 아무도 로그인할 수 없는 잠긴 상태가 됩니다. localhost exception으로 부트스트랩합니다.

안전한 순서 (self-hosted):

Bash
# 1) auth 없이 재기동 (또는 localhost exception 이용)
mongod --dbpath /data/db --bind_ip localhost

# 2) localhost에서 접속 후 admin 유저 생성
mongosh
JavaScript
use admin
db.createUser({
  user: "root",
  pwd: "strongPass123!",
  roles: [{ role: "root", db: "admin" }]
})
Bash
# 3) --auth 켜고 재기동
mongod --dbpath /data/db --auth

Docker라면 환경변수 한 방:

Bash
docker run -d --name mongo \
  -e MONGO_INITDB_ROOT_USERNAME=root \
  -e MONGO_INITDB_ROOT_PASSWORD=strongPass123! \
  -p 27017:27017 mongo:7

이렇게 뜬 루트 유저는 admin DB에 생성됩니다. 따라서 접속 시 반드시 authSource=admin이 필요합니다(Case 1 함정과 직결).

연결 문자열 레퍼런스 & Docker 체크리스트

정상 접속 문자열 완성형을 한자리에 모았습니다.

Bash
# mongosh
mongosh "mongodb://root:strongPass123!@localhost:27017/?authSource=admin&authMechanism=SCRAM-SHA-256"
JavaScript
// Node.js
const uri = "mongodb://root:strongPass123!@localhost:27017/mydb"
          + "?authSource=admin&authMechanism=SCRAM-SHA-256";
Python
# pymongo (URI)
client = MongoClient(
  "mongodb://root:strongPass123!@localhost:27017/mydb?authSource=admin&authMechanism=SCRAM-SHA-256")

Docker 체크리스트

  • MONGO_INITDB_ROOT_* 유저는 항상 admin에 생김 → authSource=admin 필수
  • 최초 기동 시점에만 초기화 스크립트 실행됨 (볼륨이 이미 있으면 무시)
  • 앱 전용 유저는 mydb에 만들되, 접속 URI에는 그 유저의 authSource를 정확히 명시

결론: 다음에 또 막히지 않으려면

한 줄 요약: code 18이면 authSource·비번·SCRAM을 의심하고, code 13이면 roles를 부여하세요.

재발 방지 원칙 두 가지만 지키면 됩니다.

  1. 항상 authSource를 명시한다 (특히 Docker 루트 유저는 무조건 admin).
  2. 비밀번호는 반드시 인코딩 함수를 통과시키거나 드라이버 파라미터로 분리한다.

MySQL의 ERROR 1698(auth_socket/mysql_native_password)이나 PostgreSQL의 password authentication failed(pg_hba.conf) 런북을 찾아온 분이라면 헷갈릴 수 있는데, MongoDB에는 그들에게 없는 두 가지 고유 개념이 있습니다. 바로 **유저가 소속된 인증 DB(authSource)**와 SCRAM 메커니즘 협상입니다. MySQL은 user@host 기준, PostgreSQL은 pg_hba.conf의 접속 규칙 기준으로 인증하지만, MongoDB는 "유저가 어느 DB에 등록됐는가"가 인증의 전제 조건이라는 점이 결정적으로 다릅니다.

자주 묻는 질문 (FAQ)

Q. authSource를 안 쓰고 접속할 수는 없나요? A. 유저가 접속 대상 DB와 동일한 DB에 생성돼 있다면 생략 가능합니다. 하지만 Docker 루트 유저나 공용 관리 유저는 admin에 있으므로 사실상 authSource=admin이 필수입니다. 헷갈리지 않게 항상 명시하는 걸 권장합니다.

Q. 비밀번호는 맞는데 계속 bad auth가 떠요. A. 십중팔구 authSource 불일치(Case 1) 또는 비밀번호 특수문자 인코딩(Case 2)입니다. db.getSiblingDB("admin").system.users.find()로 유저의 실제 db 필드를 먼저 확인하세요.

Q. not authorized도 비밀번호 문제인가요? A. 아닙니다. code 13은 인증에 성공한 뒤 권한이 부족한 상태입니다. grantRolesToUser로 역할만 부여하면 됩니다(Case 4).

Q. mongo 셸로 접속하던 스크립트가 안 돼요. A. 구형 mongo 셸은 폐기됐고 현재 표준은 mongosh입니다. 명령 문법과 기본 SCRAM-SHA-256 협상 방식이 달라졌으니 mongosh 기준 예제로 교체하세요.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

초안 · AI (Content Reviewer)·검토 · Nodelog 편집자·발행 ·
관련 공식 문서MongoDB 공식 매뉴얼

댓글

첫 번째 댓글을 남겨보세요.