Node.js에서 MongoDB Mongoose 연결 에러 해결
Mongoose를 사용한 MongoDB 연결 시 발생하는 다양한 에러와 그 해결 방법을 다룹니다.
Introduction
Node.js에서 MongoDB를 사용할 때 Mongoose는 가장 많이 사용되는 ODM 라이브러리입니다. 하지만 연결 설정이나 네트워크 문제로 다양한 연결 에러가 발생할 수 있습니다. 이번 포스트에서는 흔한 연결 에러와 그 해결 방법을 살펴보겠습니다.
Environment
# 프로젝트 환경
node --version
# v20.11.0
npm list mongoose
# mongoose@8.0.3
# MongoDB 버전
mongosh --version
# 2.0.2Problem
다음과 같은 연결 에러가 빈번하게 발생했습니다:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/myapp');
mongoose.connection.on('error', (err) => {
console.error('MongoDB connection error:', err);
});
// 에러 로그
// MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017발생한 주요 에러들
# 1. 연결 거부
MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017
# 2. 인증 실패
MongoServerError: Authentication failed.
# 3. 타임아웃
MongooseServerSelectionError: Server selection timed out after 5000 ms
# 4. replica set 관련
MongoServerError: not master and slaveOk=falseAnalysis
에러 유형별 원인
| 에러 | 원인 | 해결책 |
|---|---|---|
| ECONNREFUSED | MongoDB 서버 중지 | 서버 시작 |
| Authentication failed | 잘못된 자격 증명 | URI 수정 |
| Server selection timed out | 네트워크 문제 | 연결 옵션 조정 |
| not master and slaveOk=false | replica set 설정 | read preference 설정 |
Mongoose 연결 URI 구조
mongodb://username:password@host:port/database?optionsSolution
1. 재연결 로직 구현
const mongoose = require('mongoose');
class DatabaseConnection {
constructor() {
this.retryCount = 0;
this.maxRetries = 5;
this.retryDelay = 5000;
}
async connect() {
const options = {
// 연결 옵션
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
connectTimeoutMS: 10000,
maxPoolSize: 10,
minPoolSize: 5,
retryWrites: true,
retryReads: true,
};
try {
await mongoose.connect(
process.env.MONGODB_URI || 'mongodb://localhost:27017/myapp',
options
);
console.log('MongoDB connected successfully');
this.retryCount = 0;
} catch (error) {
console.error('MongoDB connection error:', error.message);
await this.handleConnectionError(error);
}
this.setupEventHandlers();
}
async handleConnectionError(error) {
if (this.retryCount < this.maxRetries) {
this.retryCount++;
console.log(`Retrying connection (${this.retryCount}/${this.maxRetries})...`);
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
return this.connect();
}
console.error('Max retries reached. Exiting...');
process.exit(1);
}
setupEventHandlers() {
mongoose.connection.on('disconnected', () => {
console.log('MongoDB disconnected');
});
mongoose.connection.on('reconnected', () => {
console.log('MongoDB reconnected');
});
mongoose.connection.on('error', (err) => {
console.error('MongoDB error:', err);
});
// 프로세스 종료 시 연결 해제
process.on('SIGINT', async () => {
await mongoose.connection.close();
console.log('MongoDB connection closed through app termination');
process.exit(0);
});
}
}
const db = new DatabaseConnection();
db.connect();2. Replica Set 연결 설정
const mongoose = require('mongoose');
// Replica Set URI 형식
const replicaSetUri = 'mongodb://user:pass@mongo1:27017,mongo2:27017,mongo3:27017/myapp?replicaSet=myReplicaSet&readPreference=secondaryPreferred';
const options = {
replicaSet: 'myReplicaSet',
readPreference: 'secondaryPreferred',
readConcern: { level: 'majority' },
writeConcern: { w: 'majority', j: true },
maxPoolSize: 50,
minPoolSize: 10,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
};
mongoose.connect(replicaSetUri, options);3. 환경별 연결 설정
const config = {
development: {
uri: 'mongodb://localhost:27017/myapp_dev',
options: {
serverSelectionTimeoutMS: 5000,
},
},
production: {
uri: process.env.MONGODB_URI,
options: {
serverSelectionTimeoutMS: 10000,
maxPoolSize: 50,
minPoolSize: 10,
retryWrites: true,
retryReads: true,
readConcern: { level: 'majority' },
writeConcern: { w: 'majority', j: true },
},
},
test: {
uri: 'mongodb://localhost:27017/myapp_test',
options: {
serverSelectionTimeoutMS: 2000,
},
},
};
const env = process.env.NODE_ENV || 'development';
const dbConfig = config[env];
mongoose.connect(dbConfig.uri, dbConfig.options);4. 연결 상태 확인 미들웨어
const mongoose = require('mongoose');
function checkConnection(req, res, next) {
if (mongoose.connection.readyState !== 1) {
return res.status(503).json({
error: 'Database unavailable',
status: mongoose.connection.readyState,
states: {
0: 'disconnected',
1: 'connected',
2: 'connecting',
3: 'disconnecting',
99: 'uninitialized',
},
});
}
next();
}
// 라우터에 적용
app.use('/api', checkConnection, apiRouter);5. Mongoose 모델 초기화
const mongoose = require('mongoose');
// 모델 정의 전에 connection 확인
async function initializeModels() {
if (mongoose.connection.readyState !== 1) {
throw new Error('Database not connected');
}
// 스키마 정의
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now },
});
// 인덱스 생성
userSchema.index({ email: 1 }, { unique: true });
const User = mongoose.model('User', userSchema);
// 연결 상태 확인
console.log('Models initialized successfully');
return { User };
}
module.exports = { initializeModels };Lessons Learned
- 연결 옵션 최적화:
maxPoolSize,minPoolSize,serverSelectionTimeoutMS를 환경에 맞게 설정하세요 - 재연결 로직: 네트워크 불안정에 대비하여 자동 재연결 로직을 구현하세요
- Replica Set 활용: 프로덕션 환경에서는 replica set을 사용하여 가용성을 높이세요
- 연결 상태 검증: API 요청 시 연결 상태를 확인하는 미들웨어를 추가하세요
- 로그 모니터링: 연결 이벤트에 대한 로깅을 설정하여 문제를 조기에 발견하세요
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.