PostgreSQL 08006 오류 원인과 해결 방법 완벽 가이드

08006
2026년 10월 07일 | DBMS Error 가이드

이 글에서 다루는 내용

08006 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

08006 connection failure 는?

PostgreSQL 에러 코드 08006은 클라이언트와 서버 사이의 연결이 예기치 않게 끊어졌을 때 발생하는 Connection Failure 에러입니다. 이 에러는 단순히 연결 자체가 거부된 것이 아니라, 한번 맺어진 연결이 도중에 비정상적으로 종료되었다는 점에서 08001(Unable to Connect) 과는 구분됩니다. 네트워크 장애, 서버 과부하, 방화벽의 강제 세션 종료 등 다양한 인프라 수준의 문제가 복합적으로 얽혀 발생하는 경우가 많아, 운영 환경에서 가장 디버깅하기 까다로운 에러 중 하나입니다.


주요 발생 원인

1. 네트워크 불안정 또는 방화벽의 idle 세션 강제 종료

가장 흔한 원인입니다. 기업 방화벽이나 클라우드 환경(AWS Security Group, GCP Firewall 등)에서는 일정 시간 동안 아무런 패킷 교환이 없는 idle 세션을 강제로 끊어버리는 정책을 가지고 있습니다. 특히 커넥션 풀러(PgBouncer, HikariCP 등)를 사용하는 환경에서는 풀 내부에 보관 중인 커넥션이 방화벽에 의해 이미 종료된 상태임에도 불구하고, 애플리케이션이 해당 커넥션을 재사용하려 시도하면서 08006 에러가 발생합니다.

2. PostgreSQL 서버 측 pg_terminate_backend() 또는 서버 재시작

DBA가 수동으로 특정 백엔드 프로세스를 강제 종료하거나, pg_ctl restart, 서버 패닉(OOM Killer에 의한 postgres 프로세스 종료 등)이 발생한 경우 클라이언트는 진행 중이던 트랜잭션이 갑자기 끊어지면서 08006을 수신합니다. 특히 장기 실행 쿼리(Long-Running Query)를 수행 중인 세션이 statement_timeout 또는 관리자의 수동 개입으로 종료될 때 빈번히 나타납니다.

3. max_connections 초과 및 서버 리소스 고갈

PostgreSQL 서버의 max_connections 한계에 도달하면 신규 연결은 거부되지만, 기존에 연결된 세션도 서버의 메모리나 파일 디스크립터 고갈로 인해 불안정해질 수 있습니다. 특히 work_mem 설정이 지나치게 크게 설정된 상태에서 복잡한 쿼리가 다수 실행되면 OOM이 발생하고, 이는 postgres 백엔드 프로세스의 비정상 종료로 이어져 연결된 클라이언트에게 08006 에러를 던지게 됩니다.


해결 방법

원인 1: 방화벽 idle 세션 종료 대응 — TCP Keepalive 설정

PostgreSQL 서버와 클라이언트 양측 모두에 TCP Keepalive를 활성화하면 방화벽이 idle 커넥션을 끊지 않도록 주기적인 패킷을 유지합니다.

-- postgresql.conf 에서 서버 측 TCP Keepalive 설정 확인 및 변경
-- (단위: 초)
ALTER SYSTEM SET tcp_keepalives_idle = 60;      -- 60초 동안 idle 이면 keepalive 시작
ALTER SYSTEM SET tcp_keepalives_interval = 10;  -- 10초 간격으로 keepalive 패킷 전송
ALTER SYSTEM SET tcp_keepalives_count = 5;      -- 5회 무응답 시 연결 종료

SELECT pg_reload_conf();

-- 현재 세션에서 즉시 확인
SHOW tcp_keepalives_idle;
SHOW tcp_keepalives_interval;
SHOW tcp_keepalives_count;

PgBouncer를 사용하는 경우, pgbouncer.ini 에서도 별도로 설정해야 합니다:

-- PgBouncer의 server_idle_timeout 을 방화벽 timeout 보다 짧게 설정 권장
-- pgbouncer.ini
-- server_idle_timeout = 300   (방화벽이 600초라면 300초로 설정)

-- 현재 연결된 커넥션 상태 모니터링 (pgbouncer 콘솔에서)
-- SHOW POOLS;
-- SHOW SERVERS;

-- PostgreSQL 에서 현재 idle 세션 확인
SELECT pid,
       usename,
       application_name,
       client_addr,
       state,
       EXTRACT(EPOCH FROM (now() - state_change)) AS idle_seconds
FROM pg_stat_activity
WHERE state = 'idle'
ORDER BY idle_seconds DESC;

원인 2: 강제 종료된 백엔드 프로세스 식별 및 관리

문제가 발생한 세션을 사전에 식별하고 안전하게 관리하는 방법입니다.

-- 현재 실행 중인 장기 쿼리 확인 (5분 이상 실행 중인 쿼리)
SELECT pid,
       usename,
       application_name,
       client_addr,
       wait_event_type,
       wait_event,
       state,
       EXTRACT(EPOCH FROM (now() - query_start)) AS running_seconds,
       LEFT(query, 100) AS query_preview
FROM pg_stat_activity
WHERE state != 'idle'
  AND query_start < now() - INTERVAL '5 minutes'
ORDER BY running_seconds DESC;

-- 특정 PID의 쿼리를 안전하게 취소 (SIGINT — 쿼리만 취소, 연결 유지)
SELECT pg_cancel_backend(12345);

-- 특정 PID의 백엔드를 강제 종료 (SIGTERM — 연결 자체 종료, 08006 발생 가능)
SELECT pg_terminate_backend(12345);

-- statement_timeout 을 세션 레벨로 설정하여 장기 쿼리 자동 제어
SET statement_timeout = '300000';  -- 5분(300,000ms) 이후 자동 취소

-- 혹은 특정 역할에 대해 영구 설정
ALTER ROLE report_user SET statement_timeout = '600000';  -- 10분

원인 3: 커넥션 수 및 리소스 모니터링

-- 현재 max_connections 설정 및 사용 현황 확인
SHOW max_connections;

SELECT count(*) AS total_connections,
       max_conn,
       round(count(*) * 100.0 / max_conn, 2) AS usage_percent
FROM pg_stat_activity,
     (SELECT setting::int AS max_conn FROM pg_settings WHERE name = 'max_connections') s
GROUP BY max_conn;

-- 데이터베이스별 연결 현황
SELECT datname,
       count(*) AS connections,
       count(*) FILTER (WHERE state = 'idle') AS idle_connections,
       count(*) FILTER (WHERE state = 'active') AS active_connections
FROM pg_stat_activity
GROUP BY datname
ORDER BY connections DESC;

-- 메모리 관련 설정 확인 (OOM 방지)
SHOW work_mem;
SHOW shared_buffers;

-- work_mem 을 적절한 수준으로 조정 (전체 메모리의 1~4% 권장)
ALTER SYSTEM SET work_mem = '64MB';
SELECT pg_reload_conf();

예방 방법

1. Connection Pooling + Keepalive 이중 방어 전략 구성

단순히 커넥션 풀러를 도입하는 것에서 그치지 않고, PgBouncer의 server_idle_timeout을 네트워크 방화벽의 idle timeout보다 반드시 짧게 설정해야 합니다. 또한 애플리케이션 레벨에서 HikariCP의 keepaliveTime, connectionTimeout, maxLifetime 등의 파라미터를 조정하여 죽은 커넥션을 주기적으로 검증하고 교체하는 로직을 반드시 구성하세요. pg_stat_activity 뷰를 주기적으로 모니터링하여 idle 세션이 비정상적으로 누적되고 있지는 않은지 Grafana, Zabbix, pgBadger 등의 도구를 활용해 시각화하는 것을 강력히 권장합니다.

2. pg_hba.conf 및 서버 파라미터 정기 검토와 알림 시스템 구축

log_connections = on, log_disconnections = on 을 활성화하여 모든 연결/해제 이벤트를 로그에 기록하고, 연결 실패가 특정 임계값(예: 1분 내 10회 이상)을 초과할 경우 즉시 알림을 받을 수 있는 모니터링 파이프라인을 구축하세요. connection_failure 관련 로그 패턴을 ELK Stack이나 CloudWatch Logs Insights로 집계하면 장애 발생 전 조기 경보가 가능합니다.

-- 로그 설정 확인
SHOW log_connections;
SHOW log_disconnections;

-- 설정 변경 (postgresql.conf)
ALTER SYSTEM SET log_connections = 'on';
ALTER SYSTEM SET log_disconnections = 'on';
ALTER SYSTEM SET log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ';
SELECT pg_reload_conf();

관련 에러

  • 08001 (sqlclient_unable_to_establish_sqlconnection): 최초 연결 자체가 수립되지 못한 경우로, 08006이 연결 중 단절이라면 08001은 연결 시작 실패입니다.
  • 08003 (connection_does_not_exist): 트랜잭션 내에서 이미 닫힌 커넥션을 재사용하려 할 때 발생합니다.
  • 08007 (transaction_resolution_unknown): 연결이 끊어졌을 때 트랜잭션의 커밋/롤백 여부가 불분명한 상태로, 08006과 함께 발생하는 경우가 많습니다. 금융 시스템 등 데이터 정합성이 중요한 환경에서는 반드시 08007 핸들링 로직도 함께 구현해야 합니다.
  • 57P01 (admin_shutdown): 관리자의 명시적 서버 종료 명령으로 연결이 끊어진 경우로, 08006과 증상이 유사하여 혼동하기 쉽습니다.
  • 53300 (too_many_connections): 서버의 최대 연결 수 초과로 인한 에러로, 리소스 고갈 상황에서 08006과 함께 로그에 나타나는 경우가 있습니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기