PostgreSQL 19 새로운 기능 정리: REPACK부터 브레이킹 체인지까지

목차

PostgreSQL 19는 테이블 재구성, autovacuum, 논리적 복제를 개선한 메이저 릴리스다. 이 버전은 RADIUS 인증 제거와 standard_conforming_strings 강제 ON 같은 하위 호환 파괴 변경도 함께 담고 있다. PostgreSQL 19 새로운 기능 정리에서 가장 먼저 다룰 문제는 Beta 1 시점에 공개된 기능 목록과 실제 GA에 들어갈 목록이 서로 다르다는 사실이다.

2026-09-24에 공개된 Beta 4에서 SQL/PGQ, 온라인 데이터 체크섬 전환, 파티션 병합·분할 같은 기능이 한꺼번에 리버트되었다. Beta 1 기준으로 작성된 자료를 믿고 업그레이드 계획을 세우면 존재하지 않는 기능을 전제로 설계하게 된다. 이 글은 2026-09-28 기준 공개 자료를 바탕으로 작성했으며, GA 전이므로 최종 목록은 달라질 수 있다.

구성은 문제 해결 관점을 따른다. 업그레이드 직후 나타나는 증상을 먼저 정의하고, 원인을 유형별로 나눈 뒤, 원인마다 PostgreSQL 19가 제공하는 해결책과 기존 버전의 대안을 비교하는 방식이다. NestJS 같은 애플리케이션 서버에서 PostgreSQL을 쓰는 경우 DB 모듈 설정과 마이그레이션 스크립트에 영향을 주는 항목도 함께 짚는다.

Beta 4 리버트: PostgreSQL 19 GA에서 빠지는 기능

증상: 문서에 있던 구문이 동작하지 않는다

reverted-features-torn-blueprint

PostgreSQL 19를 검증하는 과정에서 흔히 보이는 증상은 “블로그나 발표 자료에 있던 구문이 최신 베타에서 문법 오류를 낸다”는 것이다. 원인은 단순하다. PostgreSQL 19 Beta 4 릴리스 공지에 따르면 다음 기능이 모두 리버트되었고, GA에 포함되지 않은 채 향후 메이저 릴리스로 연기되었다.

기능성격PostgreSQL 19 GA
SQL/PGQ (Property Graph Query)속성 그래프 질의 표준 구문미포함, 향후 릴리스로 연기
온라인 데이터 체크섬 활성화/비활성화서버 운영 중 체크섬 전환미포함, 향후 릴리스로 연기
FOR PORTION OF 절temporal 테이블의 기간 단위 갱신 구문미포함, 향후 릴리스로 연기
ALTER TABLE MERGE/SPLIT PARTITIONS파티션 병합·분할 DDL미포함, 향후 릴리스로 연기
DDL 생성 함수 (pg_get_role_ddl 등)객체 정의를 DDL 문자열로 반환미포함, 향후 릴리스로 연기

원인: Beta 1 기준 자료의 확산

outdated-beta-notices-spreading

Beta 1이 공개되면 기능 소개 글이 짧은 기간에 대량으로 생산된다. 이후 베타 단계에서 안정성이나 설계 문제로 기능이 빠져도, 초기 글은 대부분 갱신되지 않는 편이다. 검색 결과 상위에 Beta 1 기준 글이 남아 있기 때문에 “PostgreSQL 19에서 파티션을 합칠 수 있다”는 식의 잘못된 전제가 퍼지는 셈이다.

또 다른 원인은 베타 환경에서 작성한 테스트 코드다. Beta 1~3 버전으로 PoC를 진행하면서 ALTER TABLE ... MERGE PARTITIONS나 DDL 생성 함수를 쓴 스크립트가 있다면, Beta 4 이상에서는 실행 단계에서 실패한다.

베타 기반 스크립트 점검
Beta 1~3에서 검증한 마이그레이션 스크립트나 운영 도구가 리버트된 구문을 쓰는지 확인해야 한다. 파티션 병합·분할, FOR PORTION OF, SQL/PGQ, DDL 생성 함수가 대상이다. 이 구문은 PostgreSQL 19 GA에서 존재하지 않으므로 기존 방식으로 되돌려야 한다.
### 해결: 최종 릴리스 노트를 기준 문서로 삼기

기준 문서는 PostgreSQL 19 공식 릴리스 노트 하나로 고정하는 것이 안전하다. 베타 공지는 시점별 변경을 보여주지만, 최종 상태는 GA 릴리스 노트에서 확정된다. 사내 위키나 README에 “PostgreSQL 19 기능 목록”을 적어둔 경우, 출처가 된 베타 버전을 함께 기록해두면 이후 변경을 추적하기 쉬워진다.

리버트된 기능 가운데 파티션 병합·분할은 운영 수요가 큰 편이다. 이 작업은 PostgreSQL 18 이하와 마찬가지로 새 파티션을 만들고 데이터를 옮긴 뒤 기존 파티션을 분리하는 수동 절차로 처리해야 한다.

PostgreSQL 19 브레이킹 체인지: 증상별 원인과 해결

업그레이드 직후 장애로 이어지는 변경은 네 가지다. 증상, 원인, 해결책을 먼저 표로 정리하고 각 항목을 H3에서 풀어 설명한다.

증상원인해결
특정 사용자 접속 인증 실패RADIUS 인증 지원 제거pg_hba.conf의 RADIUS 항목을 다른 인증 방식으로 교체
덤프 파일 복원 실패standard_conforming_strings 강제 ONPostgreSQL 19 이상의 pg_dump로 덤프 재생성
분석 쿼리 실행 시간 변화JIT 기본 비활성화워크로드 측정 후 필요한 경우에만 JIT 활성화
락 관련 메모리 사용량 증가max_locks_per_transaction 기본값 64 → 128명시 설정 여부 확인, 메모리 계획 재검토

RADIUS 인증 제거

PostgreSQL 19부터 RADIUS 인증 지원이 완전히 제거되었다. PostgreSQL 18 이하에서는 pg_hba.conf에 RADIUS 방식을 지정해 외부 RADIUS 서버로 인증을 위임할 수 있었다. 릴리스 노트에 따르면 제거 사유는 UDP 전용 구현이 보안적으로 수정 불가능하다고 판단되었기 때문이다.

증상은 해당 인증 경로를 쓰던 계정의 접속 실패로 나타난다. 사내 VPN이나 네트워크 장비 계정 체계와 DB 계정을 RADIUS로 묶어둔 조직에서 영향이 크다. 애플리케이션 서비스 계정보다 운영자 개인 계정에서 먼저 드러나는 경우가 많은데, 운영자 계정만 중앙 인증에 연결해둔 구성이 흔하기 때문이다.

해결책은 업그레이드 전에 pg_hba.conf에서 RADIUS를 쓰는 줄을 모두 찾아 다른 인증 방식으로 옮기는 것이다. PostgreSQL이 기본 제공하는 SCRAM 기반 비밀번호 인증, LDAP, 클라이언트 인증서 인증이 후보가 된다. 어떤 방식이 적합한지는 기존 계정 관리 체계에 달려 있다. 중앙 디렉터리가 이미 있다면 LDAP가, 계정 수가 적고 비밀번호 관리가 가능하다면 SCRAM이 전환 비용이 낮은 편이다.

RADIUS 인증의 보안 위험
RADIUS 제거는 단순한 기능 정리가 아니라 UDP 전용 구현의 보안 결함 때문에 이루어졌다. PostgreSQL 18 이하를 계속 쓰더라도 RADIUS 인증 경로는 수정 불가능한 위험을 안고 있는 셈이다. 업그레이드 일정과 무관하게 SCRAM, LDAP, 인증서 인증 중 하나로 먼저 전환하는 것이 안전하다.
### standard_conforming_strings 강제 ON과 pg_dump 복원 실패

PostgreSQL 19부터 standard_conforming_strings는 항상 ON으로 고정된다. PostgreSQL 18 이하에서는 이 값을 off로 바꿀 수 있었고, off 상태에서는 일반 문자열 리터럴 안의 백슬래시가 이스케이프 문자로 해석되었다.

이 변경이 가장 직접적으로 드러나는 곳은 덤프 복원이다. 릴리스 노트에 따르면 PostgreSQL 19 이전 버전의 pg_dump 출력 가운데 standard_conforming_strings = off 설정을 포함한 파일은 PostgreSQL 19에 로드되지 않는다. 따라서 덤프를 떠서 옮기는 방식으로 업그레이드한다면, 대상 서버 버전인 PostgreSQL 19 이상의 pg_dump로 구 서버에 접속해 덤프를 새로 만들어야 한다.

# PostgreSQL 19 클라이언트 바이너리로 구 버전 서버를 덤프
/usr/lib/postgresql/19/bin/pg_dump -h old-db.internal -U app -Fc -f app.dump appdb

바이너리 경로는 배포판과 설치 방식에 따라 다르다. 중요한 것은 구 서버에 설치된 pg_dump가 아니라 PostgreSQL 19 버전의 pg_dump를 쓰는 부분이다.

애플리케이션 코드에도 영향이 있다. 백슬래시 이스케이프에 의존하는 SQL 문자열이 있다면 PostgreSQL 19에서는 백슬래시가 문자 그대로 저장된다. 이스케이프가 필요한 리터럴은 E'...' 형식의 이스케이프 문자열로 명시하거나, 문자열을 직접 이어 붙이는 대신 파라미터 바인딩으로 바꾸는 방식이 해결책이다. NestJS 서비스에서 ORM의 raw query 기능으로 SQL을 조합하는 코드가 주요 점검 대상이 된다.

JIT 기본 비활성화

PostgreSQL 19부터 JIT(Just-in-time 컴파일)가 기본 비활성화 상태로 바뀌었다. PostgreSQL 18 이하에서는 JIT가 기본으로 켜져 있어, 비용이 큰 쿼리에 대해 플래너가 자동으로 JIT 컴파일을 적용했다.

증상은 두 방향으로 나타날 수 있다. 짧은 OLTP 쿼리 위주의 서비스에서는 JIT 컴파일 오버헤드가 사라져 오히려 응답 시간이 안정되는 편이다. 반대로 대량 집계나 복잡한 표현식을 많이 평가하는 분석 쿼리는 JIT의 이점을 잃어 느려질 가능성이 있다.

해결의 출발점은 측정이다. 업그레이드 전후로 대표 분석 쿼리를 EXPLAIN (ANALYZE)로 실행해 실행 계획 출력에 JIT 항목이 있었는지, 전체 시간에서 JIT 컴파일이 차지한 비중이 어느 정도였는지 확인한다. 이득이 확인된 워크로드에서만 JIT를 다시 켜는 편이 합리적이다. 서버 전체가 아니라 분석용 롤이나 세션 단위로 켜면 OLTP 쿼리에 미치는 영향을 줄이게 된다.

max_locks_per_transaction 기본값 64 → 128

max_locks_per_transaction 기본값이 PostgreSQL 19부터 64에서 128로 두 배가 되었다. 이 값은 공유 메모리의 락 테이블 크기를 결정한다. 파티션이 많은 테이블을 한 트랜잭션에서 조회하거나 다수 테이블을 한꺼번에 다루는 작업에서 기존 기본값 64로는 다음 오류가 발생하는 경우가 있었다.

ERROR:  out of shared memory
HINT:  You might need to increase max_locks_per_transaction.

기본값 상향은 이 오류를 줄이는 방향의 변경이다. 다만 두 가지를 확인해야 한다. 첫째, postgresql.conf에 64를 명시해둔 서버는 업그레이드 후에도 64가 유지되므로 새 기본값의 혜택을 받지 못한다. 둘째, 락 테이블이 커지면 공유 메모리 사용량도 늘어나기 때문에 메모리 여유가 적은 소형 인스턴스라면 전체 메모리 계획을 다시 계산해야 한다.

테이블 bloat 문제: PostgreSQL 19 REPACK CONCURRENTLY로 해결

증상과 원인

대량 UPDATE나 DELETE가 반복되는 테이블은 실제 행 수에 비해 디스크 사용량이 계속 커진다. 이 현상은 bloat라고 부르는 것이다. PostgreSQL의 MVCC 구조에서는 갱신·삭제된 행이 즉시 사라지지 않고 dead tuple로 남는다. 일반 VACUUM은 이 공간을 재사용 가능하게 표시할 뿐, 파일 끝부분의 빈 페이지를 제외하면 운영체제에 공간을 돌려주지 않는다.

그 결과 순차 스캔 범위가 넓어지고, 캐시에 올라가는 페이지 중 쓸모없는 비율이 늘어난다. 인덱스도 비슷하게 부풀어 조회 성능이 떨어지는 편이다. 증상 확인에는 테이블 전체 크기를 주기적으로 기록하는 쿼리가 쓰인다.

-- 테이블과 인덱스, TOAST를 포함한 전체 크기
SELECT pg_size_pretty(pg_total_relation_size('orders'));

행 수는 비슷한데 이 값이 계속 증가한다면 bloat가 원인일 가능성이 높다.

PostgreSQL 18 이하의 선택지와 한계

PostgreSQL 18 이하에서 디스크 공간을 실제로 회수하는 기본 명령은 VACUUM FULL과 CLUSTER 두 가지였다. VACUUM FULL은 테이블을 새 파일로 다시 쓰면서 dead tuple을 제거한다. CLUSTER는 여기에 더해 지정한 인덱스 순서대로 행을 재배치한다.

두 명령 모두 작업 동안 테이블에 배타적 락을 잡는다는 것이 한계다. 재작성 시간 동안 해당 테이블의 읽기와 쓰기가 모두 막히기 때문에, 주문이나 결제처럼 서비스 중단이 허용되지 않는 테이블에는 적용하기 어려웠다. 결국 점검 시간대를 따로 잡거나, 서비스 중단 없이 재구성하는 외부 확장 도구를 추가로 설치해 운영하는 방식이 쓰였다.

PostgreSQL 19의 REPACK 명령어

PostgreSQL 19는 REPACK 명령어를 새로 도입했다. PostgreSQL 19 릴리스 노트의 REPACK 항목에 따르면 REPACK은 VACUUM FULL과 CLUSTER의 기능을 하나로 통합한다. 핵심은 CONCURRENTLY 옵션이다. 이 옵션을 붙이면 테이블에 대한 읽기와 쓰기를 차단하지 않은 채 디스크 공간을 회수하고 테이블을 재구성한다.

REPACK table_name CONCURRENTLY;

운영 환경에서는 대상 테이블 크기를 작업 전후로 기록해 효과를 확인하는 편이 좋다. 앞의 크기 조회 쿼리와 묶어 psql 스크립트로 만들면 반복 작업이 단순해진다.

\echo '--- before'
SELECT pg_size_pretty(pg_total_relation_size('orders'));

REPACK orders CONCURRENTLY;

\echo '--- after'
SELECT pg_size_pretty(pg_total_relation_size('orders'));

CLUSTER처럼 특정 인덱스 순서로 재정렬하는 옵션의 구체적인 문법은 릴리스 노트와 REPACK 레퍼런스 페이지에서 확인해야 한다. 이 글에서는 공식 문서로 확인된 CONCURRENTLY 형태만 다룬다.

REPACK CONCURRENTLY 적용 전 확인할 점

CONCURRENTLY가 읽기/쓰기를 막지 않는다고 해서 비용이 없는 것은 아니다. 테이블 전체를 다시 쓰는 작업은 디스크 I/O와 WAL 생성량을 늘리고, 복제 지연으로 이어질 여지가 있다. 대형 테이블은 트래픽이 적은 시간대에 실행하고, 복제본의 지연 지표를 함께 관찰하는 방식이 안전하다.

성능 수치에 대해서는 신중해야 한다. REPACK CONCURRENTLY의 소요 시간이나 기존 VACUUM FULL 대비 처리 속도에 대한 벤치마크는 공식 문서에 없다. 자체 스테이징 환경에서 실제 데이터 크기로 측정한 값을 근거로 운영 일정을 잡는 것이 현실적인 방법이다.

디스크 여유 공간 먼저 확보
`VACUUM FULL`과 `CLUSTER`는 테이블을 새 파일로 다시 쓰기 때문에 작업 중 추가 디스크 공간이 필요했다. `REPACK`의 정확한 공간 요구량은 공식 문서에 수치로 명시되어 있지 않다. 대상 테이블과 인덱스 크기 이상의 여유 공간을 확보한 뒤 실행하는 편이 안전하다.
## autovacuum 지연 문제: PostgreSQL 19 autovacuum 병렬 처리

증상: 대형 테이블 vacuum이 끝나지 않는다

인덱스가 많은 대형 테이블에서는 autovacuum 한 번이 오래 걸리는 경우가 있다. 그 사이 dead tuple이 계속 쌓이고, 다른 테이블은 autovacuum 워커를 할당받지 못해 순서를 기다리게 된다. 결국 bloat가 늘고 통계가 오래되어 실행 계획이 나빠지는 연쇄 증상으로 번진다.

원인: 테이블 단위 직렬 처리와 단순한 우선순위

PostgreSQL 18 이하의 autovacuum은 한 테이블의 인덱스를 하나의 워커 프로세스가 순서대로 처리했다. 수동 VACUUM에는 PostgreSQL 13부터 PARALLEL 옵션이 있어 인덱스를 병렬로 처리할 수 있었지만, 자동 실행 경로에는 이 방식이 적용되지 않았다. 인덱스가 10개인 테이블이라면 10개를 한 프로세스가 차례로 훑는 구조다.

우선순위 문제도 있다. 어떤 테이블을 먼저 처리할지 판단하는 기준이 단순해서, vacuum이나 analyze가 급한 테이블이 뒤로 밀리는 상황이 생겼다.

해결: 병렬 워커와 테이블별 파라미터

PostgreSQL 19부터 autovacuum도 병렬 워커 프로세스로 테이블의 인덱스를 vacuum할 수 있다. 설정은 두 단계로 나뉜다. 서버 전체 상한은 autovacuum_max_parallel_workers로, 테이블별 병렬 수준은 autovacuum_parallel_workers 스토리지 파라미터로 지정한다.

# postgresql.conf (예시 값)
autovacuum_max_parallel_workers = 4
-- 인덱스가 많은 대형 테이블에만 병렬 수준 지정 (예시 값)
ALTER TABLE orders SET (autovacuum_parallel_workers = 4);

위 숫자는 설명을 위한 예시다. 두 파라미터의 기본값과, 일반 쿼리용 병렬 워커 상한과의 관계는 릴리스 노트와 설정 레퍼런스에서 확인해야 한다. 병렬 워커는 CPU와 I/O를 추가로 쓰므로, 모든 테이블에 일괄 적용하기보다 인덱스가 많고 vacuum 시간이 긴 테이블부터 지정하는 편이 부작용이 적다.

새 스코어링 시스템과 pg_stat_autovacuum_scores

PostgreSQL 19에는 vacuum이나 analyze가 가장 필요한 테이블을 우선 처리하는 새로운 스코어링 시스템이 들어갔다. 테이블별 점수는 pg_stat_autovacuum_scores 뷰로 조회한다.

SELECT * FROM pg_stat_autovacuum_scores;

뷰의 개별 컬럼 의미는 공식 문서의 뷰 정의를 참고해야 한다. 운영 관점에서 쓸모 있는 지점은 “왜 이 테이블이 아직 vacuum되지 않았는가”라는 질문에 근거를 준다는 것이다. 점수가 높은데 처리가 늦다면 워커 수 부족이, 점수 자체가 낮다면 임계값 설정이 원인일 가능성이 높다. 이 뷰를 모니터링 대시보드에 주기적으로 수집해두면 autovacuum 튜닝 전후 변화를 비교하는 기준이 생긴다.

논리적 복제 시퀀스 누락: PostgreSQL 19 ALL SEQUENCES 지원

증상: 전환 직후 duplicate key 오류

논리적 복제로 새 서버에 데이터를 옮긴 뒤 애플리케이션 트래픽을 전환하면, 첫 INSERT부터 다음 오류가 나는 경우가 있다.

ERROR:  duplicate key value violates unique constraint "orders_pkey"

PostgreSQL 18 이하의 논리적 복제는 테이블 데이터만 복제하고 시퀀스 값은 복제하지 않았다. 구독 측 시퀀스는 초기값에 머물러 있으므로, 전환 후 새로 발급되는 ID가 이미 복제된 행의 ID와 겹치게 된다. 기존 해결책은 전환 직전에 발행 측 시퀀스 값을 조회해 구독 측에서 setval()로 맞추는 수동 작업이었고, 시퀀스가 많은 스키마에서는 누락 위험이 컸다.

해결: CREATE PUBLICATION의 ALL SEQUENCES와 EXCEPT

PostgreSQL 19 Beta 1 릴리스 공지에 따르면 PostgreSQL 19부터 논리적 복제가 시퀀스 값을 복제한다. CREATE PUBLICATION에 ALL SEQUENCES 절과 EXCEPT 절이 추가되어, 전체 테이블과 시퀀스를 발행하면서 특정 테이블만 제외하는 구성이 가능해졌다.

CREATE PUBLICATION my_pub FOR ALL TABLES, ALL SEQUENCES EXCEPT TABLE secret_table;

구독 측에서는 ALTER SUBSCRIPTION ... REFRESH SEQUENCES로 시퀀스를 동기화한다. 트래픽 전환 직전에 이 명령을 실행하면 시퀀스 값을 발행 측과 맞춘 상태로 전환하게 된다.

-- 구독 측에서 실행
ALTER SUBSCRIPTION my_sub REFRESH SEQUENCES;

전환 절차를 흐름으로 나타내면 다음과 같다.

flowchart LR
  A[발행 측 쓰기 중지] --> B[구독 측 복제 따라잡기 확인]
  B --> C[REFRESH SEQUENCES 실행]
  C --> D[애플리케이션 연결 전환]
  D --> E[신규 INSERT 검증]

wal_level 변경 없이 논리적 복제 활성화

기존 버전에서 논리적 복제를 시작하려면 wal_level을 logical로 바꾸고 서버를 재시작해야 했다. 운영 중인 서버라면 이 재시작 때문에 점검 시간을 따로 잡아야 했다. PostgreSQL 19부터는 wal_level이 replica로 설정되어 있어도 서버 재시작 없이 논리적 복제를 활성화할 수 있다. 활성화가 어떤 내부 절차로 이루어지는지는 릴리스 노트를 확인해야 한다.

현재 실제로 적용 중인 WAL 레벨은 읽기 전용 파라미터 effective_wal_level로 확인한다.

SHOW wal_level;
SHOW effective_wal_level;
wal_level과 effective_wal_level의 차이
`wal_level`은 설정 파일에 적힌 값이고, `effective_wal_level`은 서버가 현재 실제로 적용 중인 값이다. PostgreSQL 19에서는 재시작 없이 논리적 복제가 활성화될 수 있기 때문에 두 값이 다를 때가 있다. 복제 구성 점검 스크립트는 `effective_wal_level`을 기준으로 판단하는 편이 정확하다.
## 해결책 비교와 PostgreSQL 19 마이그레이션 권장 순서

PostgreSQL 19 vs 18 차이점 비교

앞에서 다룬 문제를 버전별 해결 방식으로 나란히 놓으면 다음과 같다. facts로 확인된 항목만 포함했다.

문제PostgreSQL 18 이하PostgreSQL 19주의점
테이블 bloat 회수VACUUM FULL, CLUSTER (읽기/쓰기 차단)REPACK ... CONCURRENTLY (차단 없음)벤치마크 수치 미공개, 디스크 여유 필요
대형 테이블 autovacuum테이블당 단일 프로세스인덱스 병렬 vacuum, 스코어링병렬 워커의 CPU·I/O 사용 증가
복제 전환 시 시퀀스수동 setval() 동기화ALL SEQUENCES, REFRESH SEQUENCES전환 직전 REFRESH 실행 필요
논리적 복제 활성화wal_level = logical 후 재시작replica에서 재시작 없이 활성화effective_wal_level로 확인
RADIUS 인증지원제거사전 인증 방식 전환
standard_conforming_stringsoff 설정 가능항상 ON19 이상 pg_dump로 덤프
JIT기본 활성화기본 비활성화분석 쿼리 성능 재측정
max_locks_per_transaction기본값 64기본값 128명시 설정 여부 확인

표에서 드러나듯 신규 기능은 “운영 중단 없이 처리하는 범위를 넓힌” 변경이 대부분이다. 반대로 브레이킹 체인지는 업그레이드 당일 접속 실패나 복원 실패로 곧바로 드러나기 때문에, 대응 순서에서 앞에 두어야 한다.

권장 순서

마이그레이션은 “실패하면 서비스가 멈추는 항목”을 먼저, “켜면 좋아지는 항목”을 나중에 처리하는 순서가 안전하다.

flowchart TD
  A[1. 리버트 기능 의존성 점검] --> B[2. RADIUS 인증 전환]
  B --> C[3. 문자열 리터럴·덤프 점검]
  C --> D[4. 설정값 재검토: JIT, 락]
  D --> E[5. 업그레이드 실행]
  E --> F[6. REPACK·병렬 autovacuum 적용]
  F --> G[7. 시퀀스 복제 기반 전환 절차 정비]

1단계에서는 Beta 기간에 작성한 스크립트와 문서에서 리버트된 구문을 찾는다. 2단계의 RADIUS 전환은 업그레이드와 분리해 먼저 끝내는 것이 좋다. 구 버전에서 인증 방식을 바꿔두면 업그레이드 당일의 변경 범위가 줄어든다.

3단계는 애플리케이션 코드까지 포함한다. NestJS 프로젝트라면 DB 모듈에서 쓰는 raw query와 마이그레이션 파일을 대상으로 백슬래시가 들어간 문자열 리터럴을 검색해야 한다. 덤프 기반 이관을 계획했다면 PostgreSQL 19 클라이언트 바이너리를 작업 서버에 미리 설치해둔다.

4단계에서는 postgresql.conf의 명시 설정을 확인한다. max_locks_per_transaction을 64로 적어둔 줄이 있다면 새 기본값을 쓸지 결정하고, JIT에 의존하던 분석 쿼리의 실행 시간을 기록해둔다.

업그레이드 작업용 스크립트는 단계별로 나눠두면 재실행과 검토가 쉬워진다.

db-upgrade/
├── 01-precheck.sql        ← pg_hba RADIUS 항목, 명시 설정값 점검
├── 02-dump/               ← PostgreSQL 19 pg_dump로 생성한 덤프
├── 03-post-upgrade.sql    ← REPACK, autovacuum_parallel_workers 지정
└── 04-verify.sql          ← effective_wal_level, 테이블 크기, 점수 뷰 확인

6단계부터는 개선 기능을 켜는 작업이다. bloat가 심한 테이블부터 REPACK ... CONCURRENTLY를 적용하고, vacuum 시간이 긴 테이블에 병렬 파라미터를 지정한다. 7단계는 다음 이관이나 장애 대비를 위한 준비로, 논리적 복제 발행을 ALL SEQUENCES 포함 구성으로 바꿔두면 향후 전환 때 시퀀스 수동 동기화가 사라진다.

공식 마이그레이션 가이드 부재
PostgreSQL 18에서 19로의 상세 마이그레이션 가이드는 2026-09-28 기준 별도 페이지로 정리되어 있지 않다. 위 순서는 릴리스 노트의 변경 사항을 바탕으로 구성한 것이다. GA 공개 후 릴리스 노트의 “Migration to Version 19” 계열 항목이 갱신되면 그 내용을 우선 기준으로 삼아야 한다.
## 자주 묻는 질문

PostgreSQL 19 릴리스 날짜는 확정되었나?

2026-09-28 기준 GA는 아직 출시되지 않았다. 2026-09-24에 Beta 4가 공개된 상태이며, 정식 릴리스 시점은 아직 공식적으로 확정되지 않았으며, 최종 기능 목록과 함께 변경될 수 있다. 운영 일정은 공식 뉴스 페이지의 GA 공지를 확인한 뒤 확정하는 편이 안전하다.

PostgreSQL 18에서 만든 pg_dump 파일을 19에 그대로 복원할 수 있나?

standard_conforming_strings = off 설정을 포함한 PostgreSQL 19 이전 버전의 덤프는 로드되지 않는다. 가장 확실한 방법은 PostgreSQL 19 이상의 pg_dump로 구 서버에 접속해 덤프를 다시 생성하는 것이다. 이미 보관 중인 백업 덤프도 이 조건에 해당하는지 점검해야 한다.

REPACK CONCURRENTLY는 VACUUM FULL을 완전히 대체하나?

REPACK은 VACUUM FULL과 CLUSTER의 기능을 통합한 명령이고, CONCURRENTLY를 붙이면 읽기/쓰기를 차단하지 않는다. 기능 면에서는 대체 경로가 생긴 셈이다. 다만 처리 속도와 자원 사용량 비교 데이터가 공식 문서에 없으므로, 대형 테이블은 스테이징에서 먼저 측정하는 것이 좋다.

PostgreSQL 19 JIT 기본값 변경 후 JIT를 다시 켜야 하나?

워크로드에 따라 다르다. 짧은 OLTP 쿼리 중심이라면 기본 비활성화 상태가 유리할 때가 있다. 대량 집계 쿼리에서 업그레이드 전후 실행 시간이 늘었다면 해당 롤이나 세션에 한정해 켜는 방식이 부작용이 적다.

pg_plan_advice 확장은 어떻게 쓰나?

pg_plan_advice와 pg_stash_advice 확장은 실전 사용 예제가 공식 문서에 부족하다. 이 글에서는 확인된 사용법이 없는 상태로 설정 예시를 제시하지 않는다. GA 문서가 갱신되면 확장 레퍼런스 페이지를 기준으로 검토해야 한다.

PostgreSQL 19 핵심 요약

PostgreSQL 19 GA에는 Beta 4에서 리버트된 SQL/PGQ, 파티션 병합·분할, DDL 생성 함수 등이 포함되지 않는다.

RADIUS 인증 제거와 standard_conforming_strings 강제 ON은 업그레이드 전에 처리해야 접속·복원 실패를 막는다.

REPACK CONCURRENTLY, 병렬 autovacuum, 시퀀스 논리적 복제는 운영 중단 없이 처리하는 범위를 넓히는 변경이다.

이후 검토할 주제로는 PostgreSQL 19 논리적 복제 시퀀스 기능을 활용한 무중단 메이저 업그레이드 절차가 우선이다. autovacuum 병렬 설정을 모니터링 지표와 연결해 pg_stat_autovacuum_scores 변화를 추적하는 방법도 운영 단계에서 이어지는 주제다. NestJS 애플리케이션 쪽에서는 DB 모듈의 연결 설정과 ORM 마이그레이션 파일을 PostgreSQL 19 브레이킹 체인지 기준으로 점검하는 작업이 다음 순서가 된다.

이 글 공유하기