troubleshooting2025-05-12·8·71/348

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.2

Problem

다음과 같은 연결 에러가 빈번하게 발생했습니다:

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=false

Analysis

에러 유형별 원인

에러원인해결책
ECONNREFUSEDMongoDB 서버 중지서버 시작
Authentication failed잘못된 자격 증명URI 수정
Server selection timed out네트워크 문제연결 옵션 조정
not master and slaveOk=falsereplica set 설정read preference 설정

Mongoose 연결 URI 구조

mongodb://username:password@host:port/database?options

Solution

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

  1. 연결 옵션 최적화: maxPoolSize, minPoolSize, serverSelectionTimeoutMS를 환경에 맞게 설정하세요
  2. 재연결 로직: 네트워크 불안정에 대비하여 자동 재연결 로직을 구현하세요
  3. Replica Set 활용: 프로덕션 환경에서는 replica set을 사용하여 가용성을 높이세요
  4. 연결 상태 검증: API 요청 시 연결 상태를 확인하는 미들웨어를 추가하세요
  5. 로그 모니터링: 연결 이벤트에 대한 로깅을 설정하여 문제를 조기에 발견하세요

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.