금요일

MongoDB ECONNREFUSED ::1:27017 에러 해결 — 연결 거부의 원인과 진단법

🔍 검색 키워드: MongoDB connection refused 해결, MongoDB ECONNREFUSED 27017, MongoDB connect ECONNREFUSED ::1:27017, MongoDB 연결 거부 에러, MongoDB IPv6 IPv4 연결 에러, MongoServerError connection refused, Node.js MongoDB 연결 안 됨

이 에러가 뜨는 상황

Node.js 앱을 로컬에서 실행하는데 이런 에러가 나온다.

MongoServerSelectionError: connect ECONNREFUSED ::1:27017
    at Topology.selectServer (/node_modules/mongoose/...)
    at ...

또는 Python 쪽이라면:

pymongo.errors.ServerSelectionTimeoutError: localhost:27017: [Errno 111] Connection refused

분명히 mongod를 실행했다고 생각하는데 연결이 안 된다. mongodb://localhost:27017로 접속 시도하는데 왜 ::1(IPv6)로 가는지도 모르겠다. 이게 왜 생기는지, 어떻게 잡는지 순서대로 정리한다.

핵심 원인 세 가지

1. MongoDB 서비스가 실제로 안 돌고 있음

가장 기본적인 원인인데 의외로 많다. mongod 명령어를 쳤는데 포그라운드로 떠서 터미널 닫을 때 같이 죽는 경우, 또는 서비스 등록 없이 수동 실행했다가 시스템 재시작 후 안 뜨는 경우다.

# Linux/macOS — MongoDB 상태 확인
sudo systemctl status mongod    # Linux (systemd)
brew services list | grep mongodb  # macOS (Homebrew)

# 안 떠있으면 시작
sudo systemctl start mongod     # Linux
brew services start mongodb-community  # macOS
# 실제로 27017 포트 열려있는지 확인
netstat -tlnp | grep 27017  # Linux
lsof -i :27017              # macOS

포트 리스닝이 없으면 MongoDB가 안 뜬 것이다.

2. localhost가 ::1(IPv6)로 해석되는 문제

Node.js v17부터 DNS 리졸버가 IPv6를 우선한다. localhost를 DNS로 조회하면 ::1(IPv6)를 먼저 반환하는데, MongoDB가 IPv4(127.0.0.1)에서만 리스닝 중이면 연결이 거부된다.

에러 메시지의 ::1:27017이 바로 이 경우다.

빠른 확인: MongoDB가 IPv4로 리스닝하는지 확인

netstat -tlnp | grep 27017
# 결과 예시:
# tcp  0  0  127.0.0.1:27017  0.0.0.0:*  LISTEN  (IPv4만 리스닝)
# tcp6 0  0  :::27017         :::*       LISTEN  (IPv6도 리스닝)

3. bindIp 설정이 제한되어 있음

/etc/mongod.confbindIp127.0.0.1로만 설정돼 있으면 IPv6(::1)로 오는 연결은 거부된다.

상황별 해결 방법

방법 1: 연결 문자열에서 localhost 대신 IP 직접 지정

가장 빠른 임시 해결책이다. localhost 대신 127.0.0.1을 명시한다.

// 변경 전
mongoose.connect('mongodb://localhost:27017/mydb');

// 변경 후
mongoose.connect('mongodb://127.0.0.1:27017/mydb');
# Python pymongo
from pymongo import MongoClient
# 변경 전
client = MongoClient('localhost', 27017)
# 변경 후
client = MongoClient('127.0.0.1', 27017)

localhost 대신 127.0.0.1을 쓰면 DNS 조회 없이 바로 IPv4로 연결한다. 많은 경우데 이것만으로 해결된다.

방법 2: MongoDB bindIp에 IPv6 추가

근본 해결을 원하면 MongoDB 설정에서 IPv6도 리스닝하도록 한다.

# /etc/mongod.conf
net:
  port: 27017
  bindIp: 127.0.0.1,::1  # IPv4, IPv6 모두 리스닝

설정 변경 후 재시작:

sudo systemctl restart mongod

방법 3: Docker로 MongoDB 실행 중인 경우

Docker로 MongoDB를 띄웠는데 포트 매핑을 빠뜨린 경우다.

# 잘못된 예 — 포트 매핑 없음
docker run -d --name mongo mongo:7

# 올바른 예 — 27017 포트 매핑
docker run -d --name mongo -p 27017:27017 mongo:7

Docker Compose라면:

services:
  mongo:
    image: mongo:7
    ports:
      - "27017:27017"  # 이 줄이 없으면 호스트에서 접속 불가
    volumes:
      - mongo_data:/data/db

상황별 체크리스트

체크 항목명령어기대 결과
MongoDB 프로세스 실행 중ps aux | grep mongodmongod 프로세스 보임
27017 포트 리스닝lsof -i :27017mongod 프로세스가 27017 점유
IPv4 리스닝 확인netstat -tlnp | grep 27017127.0.0.1:27017 또는 0.0.0.0:27017
Docker 포트 매핑docker ps0.0.0.0:27017->27017/tcp 확인
연결 테스트 (IPv4)nc -zv 127.0.0.1 27017Connection succeeded
연결 테스트 (IPv6)nc -zv ::1 27017succeeded/failed 여부 확인

MongoDB 로그로 원인 확인

# 로그 실시간 확인
sudo tail -f /var/log/mongodb/mongod.log

# 최근 에러만
sudo grep -i error /var/log/mongodb/mongod.log | tail -20

로그에서 bind failed 또는 exception in initAndListen 같은 메시지가 보이면 포트 충돌이나 권한 문제다.

# 27017 포트 점유 프로세스 확인 (MongoDB 아닌 다른 프로세스일 수도 있음)
sudo lsof -i :27017

MongoServerError: Authentication Failed도 같이 나온다면

연결은 됐는데 인증에서 막히는 경우는 별개 이슈다. ECONNREFUSED(연결 거부)와 헷갈리기 쉬운데, 연결 거부는 TCP 자체가 안 되는 것이고 인증 실패는 연결 후 자격증명 검증 단계다.

인증 실패 에러:

MongoServerError: Authentication failed.

이 경우는 username/password 오류이거나 authSource 설정 문제다.

// authSource 명시
mongoose.connect('mongodb://user:pass@127.0.0.1:27017/mydb?authSource=admin');

마무리

ECONNREFUSED ::1:27017 에러는 90%가 두 가지 원인 중 하나다:

  1. MongoDB가 실제로 안 떠있음
  2. localhost가 IPv6로 해석되는데 MongoDB는 IPv4만 리스닝

연결 문자열의 localhost127.0.0.1로 바꾸는 것만으로도 대부분 해결된다. Docker를 쓴다면 포트 매핑(-p 27017:27017)을 빠뜨리지 않았는지 확인하자.

DB 연결 에러 관련해서 MySQL 연결 에러는 MySQL connection error 해결, Redis 연결 에러는 Redis connection refused 해결 글을 참고하면 된다.

작성일: 2026-06-25

React useEffect 무한루프 원인과 해결 방법

🔍 검색 키워드: React useEffect 무한루프, useEffect 의존성 배열, useEffect 성능 최적화, 리액트 렌더링 최적화, useEffect 클린업 함수

React useEffect 무한루프 원인과 해결 방법

useEffect는 React의 핵심 Hook이지만, 잘못 사용하면 무한 루프에 빠져 성능 저하와 메모리 누수를 유발합니다. 특히 의존성 배열을 제대로 관리하지 않으면 예상치 못한 사이드 이펙트가 반복됩니다.

문제 증상

콘솔에서 다음과 같은 현상이 반복됩니다:

GET /api/data 200 (무한 반복)
Warning: Can't perform a React state update on an unmounted component
Memory usage: 500MB → 1.5GB (급격히 증가)

브라우저 탭이 점점 느려지고, 개발자 도구의 Network 탭에서 같은 요청이 계속 쌓입니다.

원인 분석

원인 1: 의존성 배열 누락

useEffect(() => {
  fetchData();
}, []); // ✗ 의존성 배열이 비어있음

이 경우 useEffect는 마운트 시점에만 실행되지만, fetchData 내부에서 setState를 호출하면:

  • 상태가 변경됨 → 리렌더링 → useEffect 다시 실행 (만약 의존성이 제대로 안 되어 있으면)

원인 2: 객체/배열을 의존성으로 사용

const config = { url: '/api/data' };

useEffect(() => {
  fetch(config.url);
}, [config]); // ✗ 객체는 매번 새로 생성되므로 무한루프

원인 3: 상태를 의존성에 포함 + 그 상태를 변경

const [count, setCount] = useState(0);

useEffect(() => {
  setCount(count + 1); // ✗ count가 변경 → useEffect 실행 → count 변경 → 무한루프
}, [count]);

해결 방법

해결책 1: 의존성 배열 올바르게 설정

useEffect(() => {
  fetchData();
}, []); // 마운트 시점에만 1회 실행

특정 값이 변경될 때만 실행:

const [userId, setUserId] = useState(null);

useEffect(() => {
  if (userId) {
    fetchUserData(userId);
  }
}, [userId]); // userId 변경 시에만 실행

해결책 4: 클린업 함수로 이전 요청 취소

useEffect(() => {
  const controller = new AbortController();

  fetch('/api/data', { signal: controller.signal })
    .then(res => res.json())
    .then(data => setData(data))
    .catch(err => {
      if (err.name !== 'AbortError') {
        console.error(err);
      }
    });

  return () => controller.abort();
}, []);

체크리스트 및 정리표

상황결과
마운트 시점에만 실행1회 실행 ✓
특정 값 변경 시 실행value 변경 시마다 실행 ✓

이 패턴들을 적용하면 useEffect 무한루프 문제를 대부분 해결할 수 있습니다.

🔍 검색 키워드: PostgreSQL connection pool, 데이터베이스 연결 풀, pg-pool, pgBouncer, 데이터베이스 성능 최적화, 커넥션 풀 설정

PostgreSQL Connection Pool 설정과 최적화 전략

PostgreSQL 데이터베이스에서 성능 병목의 70%는 연결 관리 문제에서 비롯됩니다. 특히 고트래픽 환경에서는 연결 수가 제한되어 있어서, 적절한 Connection Pool 설정이 없으면 요청이 대기 상태에 빠지고 타임아웃이 발생합니다.

문제 증상

다음과 같은 에러가 주기적으로 발생합니다:

Error: connect ECONNREFUSED 127.0.0.1:5432
Error: Client already has a client in it
FATAL: remaining connection slots are reserved
Error: query timeout - Client request timeout

원인 분석

원인 1: Connection Pool 미설정

const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});
// 각 쿼리마다 새 연결 생성 → 연결 고갈

원인 2: Connection Leak - 연결 반환 미흡

const client = await pool.connect();
const result = await client.query('SELECT * FROM users');
// ✗ client.release()를 호출하지 않음

해결 방법

해결책 1: Connection Pool 설정

const pool = new Pool({
  max: 20,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
  statement_timeout: '30s'
});

app.get('/user/:id', async (req, res) => {
  let client;
  try {
    client = await pool.connect();
    const result = await client.query(
      'SELECT * FROM users WHERE id = $1',
      [req.params.id]
    );
    res.json(result.rows[0]);
  } finally {
    if (client) client.release();
  }
});

해결책 2: pgBouncer 설정 (고트래픽 환경)

sudo apt-get install pgbouncer

# /etc/pgbouncer/pgbouncer.ini
[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp_prod

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
idle_in_transaction_timeout = 60

성능 최적화

항목최적값
max 연결 수CPU 코어 × 2~4
idleTimeoutMillis30~60초
statement_timeout30~60초

Connection Pool을 올바르게 설정하면 데이터베이스 성능이 2~5배 향상될 수 있습니다.