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

25006
2026년 08월 28일 | DBMS Error 가이드

이 글에서 다루는 내용

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

25006 read only sql transaction 는?

PostgreSQL 에러 코드 25006(READ_ONLY_SQL_TRANSACTION)은 읽기 전용(Read-Only)으로 설정된 트랜잭션 내에서 데이터를 변경(INSERT, UPDATE, DELETE, DDL 등)하려고 시도할 때 발생합니다. 트랜잭션이 읽기 전용 모드로 시작되었거나, 세션 또는 데이터베이스 자체가 읽기 전용으로 설정된 경우 이 에러가 트리거됩니다. 특히 스탠바이(Standby) 서버, 논리적 복제 환경, 또는 명시적으로 SET TRANSACTION READ ONLY를 선언한 경우에 자주 마주치게 됩니다.


주요 발생 원인

1. 트랜잭션을 명시적으로 READ ONLY로 선언한 경우

가장 흔한 원인 중 하나로, 개발자가 의도적으로 또는 실수로 BEGIN 이후 SET TRANSACTION READ ONLY를 선언한 상태에서 DML을 실행하는 경우입니다. 레거시 코드나 ORM 설정에서 읽기 전용 트랜잭션을 기본값으로 사용하는 경우 특히 발생하기 쉽습니다. 이 경우 해당 트랜잭션 내에서는 어떠한 데이터 변경도 허용되지 않습니다.

2. PostgreSQL 핫 스탠바이(Hot Standby) 서버에 쓰기 시도

스트리밍 복제(Streaming Replication) 환경에서 슬레이브(스탠바이) 서버는 기본적으로 읽기 전용 상태로 운영됩니다. 애플리케이션이 커넥션 풀이나 로드 밸런서 설정 오류로 인해 스탠바이 서버로 쓰기 쿼리를 보냈을 때 이 에러가 발생합니다. 이는 HA(High Availability) 구성에서 매우 빈번히 발생하는 실수이며, 운영 장애로 이어질 수 있습니다.

3. 세션 또는 데이터베이스 수준에서 default_transaction_read_only 설정

PostgreSQL의 default_transaction_read_only 파라미터가 on으로 설정된 경우, 해당 세션이나 데이터베이스에서 시작되는 모든 트랜잭션은 자동으로 읽기 전용이 됩니다. DBA가 의도적으로 특정 사용자 또는 데이터베이스를 읽기 전용으로 제한했거나, postgresql.conf 설정이 잘못 변경된 경우에 발생할 수 있습니다. 이를 인지하지 못한 채 쓰기 작업을 시도하면 25006 에러가 발생합니다.


해결 방법

원인 1 해결: 트랜잭션 모드 확인 및 변경

현재 트랜잭션이 읽기 전용인지 확인하고, 필요하다면 READ WRITE 모드로 변경합니다.

-- 현재 트랜잭션 읽기 전용 여부 확인
SHOW transaction_read_only;

-- 읽기 전용 트랜잭션에서 발생하는 에러 재현
BEGIN;
SET TRANSACTION READ ONLY;
INSERT INTO employees (name, department) VALUES ('홍길동', '개발팀');
-- ERROR:  cannot execute INSERT in a read-only transaction

ROLLBACK;

-- 올바른 방법: READ WRITE 모드로 트랜잭션 시작
BEGIN;
SET TRANSACTION READ WRITE;
INSERT INTO employees (name, department) VALUES ('홍길동', '개발팀');
COMMIT;

-- 또는 BEGIN 시점에 명시적으로 선언
BEGIN READ WRITE;
UPDATE employees SET department = '인프라팀' WHERE id = 1;
COMMIT;

원인 2 해결: 스탠바이 서버 확인 및 마스터로 연결 전환

현재 연결된 서버가 스탠바이인지 확인하고, 쓰기 작업은 마스터 서버로 전달합니다.

-- 현재 서버가 스탠바이(복제본)인지 확인
SELECT pg_is_in_recovery();
-- 결과가 true이면 스탠바이 서버 (쓰기 불가)
-- 결과가 false이면 마스터 서버 (쓰기 가능)

-- 복제 상태 상세 확인
SELECT * FROM pg_stat_replication;

-- 스탠바이에서 마스터 서버 주소 확인 (복제 설정 확인)
SELECT * FROM pg_stat_wal_receiver;

애플리케이션 레벨에서는 커넥션 풀 설정을 분리하여 쓰기 전용 커넥션은 반드시 마스터(Primary)로 연결되도록 구성하세요. PgBouncer나 HAProxy를 사용하는 경우 읽기/쓰기 라우팅 규칙을 명확히 설정해야 합니다.

원인 3 해결: default_transaction_read_only 설정 변경

세션, 사용자, 데이터베이스 수준의 설정을 확인하고 수정합니다.

-- 현재 세션의 default_transaction_read_only 확인
SHOW default_transaction_read_only;

-- 현재 세션에서만 임시로 변경 (세션 종료 시 초기화)
SET default_transaction_read_only = off;

-- 특정 사용자의 기본 설정 변경 (슈퍼유저 권한 필요)
ALTER USER app_user SET default_transaction_read_only = off;

-- 특정 데이터베이스의 기본 설정 변경
ALTER DATABASE mydb SET default_transaction_read_only = off;

-- 변경 후 확인
SELECT rolname, rolconfig 
FROM pg_roles 
WHERE rolname = 'app_user';

-- postgresql.conf 수준에서 전역 설정 확인
SELECT name, setting, source 
FROM pg_settings 
WHERE name = 'default_transaction_read_only';

예방 방법

1. 커넥션 풀 및 애플리케이션에서 읽기/쓰기 역할 명확히 분리

애플리케이션 설계 단계부터 읽기(SELECT) 전용 커넥션과 쓰기(DML/DDL) 전용 커넥션을 분리하여 관리하는 것이 핵심입니다. PgBouncer, pgpool-II, 또는 애플리케이션 레벨의 DataSource 설정을 통해 쓰기 트랜잭션은 반드시 Primary 서버로만, 읽기 트랜잭션은 Replica로 라우팅하도록 구성하세요. 이를 통해 실수로 스탠바이에 쓰기를 시도하는 상황을 원천적으로 차단할 수 있습니다.

-- 모니터링: 읽기 전용 세션 목록 주기적으로 확인
SELECT pid, usename, application_name, state, query
FROM pg_stat_activity
WHERE query NOT LIKE '%pg_stat_activity%'
  AND state = 'active';

-- 데이터베이스별 트랜잭션 설정 현황 점검
SELECT datname, datconfig 
FROM pg_database 
WHERE datname NOT IN ('template0', 'template1');

2. CI/CD 파이프라인에서 DB 연결 설정 자동 검증

배포 파이프라인에서 데이터베이스 연결 대상 서버가 마스터인지 스탠바이인지를 자동으로 검증하는 헬스체크 스크립트를 포함시키세요. pg_is_in_recovery() 함수를 활용한 간단한 쿼리를 배포 전 실행하여, 잘못된 서버에 쓰기 연결이 맺어지는 것을 사전에 방지할 수 있습니다. 또한 ORM(JPA, SQLAlchemy 등) 설정 파일의 readOnly 옵션을 코드 리뷰 체크리스트에 포함하여 실수를 예방하세요.


관련 에러

  • 25001 (ACTIVE_SQL_TRANSACTION): 이미 활성화된 트랜잭션 내에서 허용되지 않는 명령을 실행할 때 발생합니다.
  • 25P02 (IN_FAILED_SQL_TRANSACTION): 이전에 에러가 발생하여 롤백 대기 중인 트랜잭션에서 추가 명령을 실행할 때 발생합니다.
  • 55000 (OBJECT_NOT_IN_PREREQUISITE_STATE): 데이터베이스 오브젝트가 요청된 작업을 수행할 수 없는 상태일 때 발생하며, 스탠바이 관련 작업에서 함께 나타나기도 합니다.
  • 57P01 (ADMIN_SHUTDOWN): 복제 환경에서 페일오버(Failover) 발생 시 연결이 끊기며 나타날 수 있으며, 25006과 연계하여 발생하는 경우가 있습니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기