TechFeedTechFeed
Backend

PostgreSQL 17 주요 변경점과 마이그레이션 가이드

PostgreSQL 17 핵심 변경점(논리 복제, JSON_TABLE, MERGE, 증분 백업), 성능 개선, pg_upgrade 마이그레이션 절차.

한 줄 요약: PostgreSQL 17은 논리 복제 개선, JSON_TABLE, 증분 백업, COPY/MERGE 확장 등 실무 생산성에 직결되는 변경점을 다수 포함한다. 16에서 17로의 업그레이드는 pg_upgrade를 사용하면 데이터 이동 없이 진행할 수 있다.

이 글이 필요한 사람
  • PostgreSQL 16 이하를 운영 중이고 17 업그레이드를 검토하는 DBA/백엔드 개발자
  • JSON_TABLE, 증분 백업 등 17의 신기능을 실무에 바로 적용하고 싶은 경우
  • pg_upgrade 절차를 처음 진행하거나 체크리스트가 필요한 경우
  • 논리 복제 환경에서 17 변경점이 미치는 영향을 파악하고 싶은 경우

PostgreSQL 17 — 릴리스 개요와 핵심 방향

PostgreSQL 17은 2024년 9월 26일 정식 릴리스됐다. 이번 버전은 단순한 성능 개선을 넘어 대용량 환경 운영을 위한 구조적 기능이 다수 추가됐다. 공식 릴리스 노트(postgresql.org/docs/17/release-17.html)에서 전체 변경 목록을 확인할 수 있다.

이번 릴리스의 핵심 방향은 세 가지다.

  • 운영 부담 감소: 증분 백업, 논리 복제 슬롯 장애 처리 개선으로 대규모 클러스터 관리 난이도 하락
  • SQL 표준 준수 강화: JSON_TABLE, MERGE 확장, COPY FROM ... RETURNING으로 ANSI SQL 호환성 향상
  • 쿼리 성능: EXPLAIN 출력 개선, 메모리 정렬 알고리즘 교체(introsort), vacuum 처리 속도 향상

PostgreSQL 16에서 크게 달라진 부분은 기능 추가보다 운영 안정성 쪽이다. 장기 운영 환경에서 실감할 수 있는 변경이 많다.

논리 복제(Logical Replication) 개선

논리 복제에서 가장 중요한 변화는 failover slot 지원이다. PostgreSQL 16 이하에서는 Primary가 장애를 일으키면 논리 복제 슬롯이 Standby로 자동 전환되지 않아 재구성 작업이 필요했다. 17에서는 Standby에도 복제 슬롯을 동기화할 수 있다.

주요 개선 사항

  • pg_createlogicalreplicationslot()failover 옵션 추가 — failover 시 Standby가 슬롯을 승계
  • pg_logical_slot_get_changes() 성능 개선 — 대용량 트랜잭션에서 디코딩 속도 향상
  • 논리 복제 구독자(subscriber)에서 two_phase 커밋 처리 개선 — 복잡한 분산 트랜잭션 환경에서 신뢰성 증가
  • ALTER SUBSCRIPTION ... ENABLE / DISABLE 명령으로 구독 일시 중지 기능 추가

HA 구성에서 Patroni, repmgr 등을 사용 중이라면 failover slot을 즉시 활용할 수 있다. 단, Standby가 17 이상이어야 하며, 기존 슬롯을 그대로 승계하려면 슬롯 생성 시 failover = true로 설정해야 한다.

논리 복제 failover 슬롯 생성
-- failover 옵션이 있는 논리 복제 슬롯 생성 (PostgreSQL 17+) SELECT pg_create_logical_replication_slot( 'my_failover_slot', 'pgoutput', false, -- temporary true -- failover (Standby 승계 활성화) ); -- 슬롯 상태 확인 SELECT slot_name, plugin, slot_type, active, failover FROM pg_replication_slots;

JSON_TABLE과 MERGE 확장 — SQL 표준 준수

JSON_TABLE은 PostgreSQL 17의 신기능 중 가장 주목받는 변경이다. JSON 데이터를 관계형 테이블 형태로 펼쳐서 일반 SELECT처럼 쿼리할 수 있다. Oracle, MySQL 8.0에는 이미 있던 기능이며 ISO/IEC 9075 SQL 표준에 포함된다.

JSON_TABLE 기본 사용법
-- JSON 배열을 행으로 펼치기 SELECT jt.* FROM orders, JSON_TABLE( orders.line_items, '$[*]' COLUMNS ( item_id int PATH '$.id', item_name text PATH '$.name', quantity int PATH '$.qty' ) ) AS jt; -- WITH ORDINALITY로 인덱스 포함 SELECT idx, item_name FROM products, JSON_TABLE( products.tags, '$[*]' COLUMNS ( ORDINALITY FOR ORDINALITY, tag text PATH '$' ) ) AS jt(idx, item_name);

MERGE 문 확장: PostgreSQL 15에서 도입된 MERGE는 17에서 RETURNING 절을 지원한다. MERGE로 삽입/수정/삭제된 행을 바로 반환받을 수 있다.

COPY FROM ... RETURNING: COPY로 대량 적재 후 삽입된 행을 반환할 수 있다. 이전에는 COPY 후 별도 SELECT가 필요했다.

MERGE RETURNING, COPY RETURNING 예시
-- MERGE with RETURNING (PostgreSQL 17+) MERGE INTO inventory AS target USING incoming_stock AS source ON target.product_id = source.product_id WHEN MATCHED THEN UPDATE SET quantity = target.quantity + source.quantity WHEN NOT MATCHED THEN INSERT (product_id, quantity) VALUES (source.product_id, source.quantity) RETURNING target.product_id, target.quantity; -- COPY FROM with RETURNING (PostgreSQL 17+) COPY staging_table (col1, col2, col3) FROM '/data/import.csv' CSV HEADER RETURNING id, col1;

증분 백업(Incremental Backup) — 운영 부담 감소

PostgreSQL 17에서 pg_basebackup에 증분 백업 기능이 추가됐다. 이전까지는 전체 백업(full backup)만 공식 지원했으며, 증분 백업은 pgBackRest, Barman 같은 외부 도구에 의존해야 했다. 17부터는 PostgreSQL 내장 도구만으로 기본적인 증분 백업 파이프라인을 구성할 수 있다.

증분 백업 동작 방식

  • WAL 서머리(WAL summarization)를 활성화해 변경된 블록을 추적
  • pg_basebackup --incremental 옵션으로 이전 백업 이후 변경된 데이터 파일 블록만 백업
  • pg_combinebackup 명령으로 전체 백업 + 증분 백업을 병합해 복구 가능한 상태로 만듦

WAL 서머리 활성화는 필수 전제 조건이다. postgresql.conf에서 summarize_wal = on을 설정하고 서버를 재시작해야 한다. 서머리 활성화 전 발생한 변경 블록은 추적되지 않으므로, 첫 증분 백업 전에 반드시 전체 백업을 먼저 받아야 한다.

증분 백업 설정 및 실행
# postgresql.conf 설정 변경 summarize_wal = on # 서버 재시작 후 전체 백업 (증분 백업의 기준점) pg_basebackup -h localhost -U replicator -D /backup/base_20241001 -Ft -z -P # 증분 백업 실행 (전체 백업 경로 지정 필요) pg_basebackup -h localhost -U replicator -D /backup/incr_20241002 --incremental=/backup/base_20241001/backup_manifest -Ft -z -P # 복구 시 전체 + 증분 병합 pg_combinebackup /backup/base_20241001 /backup/incr_20241002 -o /restore/combined

쿼리 성능 개선과 EXPLAIN 출력 변화

메모리 정렬 알고리즘 교체: PostgreSQL 17은 내부 정렬 알고리즘을 기존 quicksort에서 introsort(introspective sort)로 교체했다. introsort는 quicksort의 최악 케이스(O(n²))를 피하기 위해 heapsort로 전환하는 하이브리드 알고리즘이다. 정렬 집약적인 쿼리(ORDER BY, GROUP BY, DISTINCT)에서 평균 성능과 최악 케이스 모두 개선된다.

Vacuum 처리 속도 향상: 17에서 vacuum의 dead tuple 처리 방식이 개선됐다. 대형 테이블에서 autovacuum이 더 빠르게 완료되며, lock 경합 시간도 줄었다. PostgreSQL 공식 팀 벤치마크 기준 일부 케이스에서 vacuum 속도가 2배 이상 향상됐다.

EXPLAIN 출력 개선: EXPLAIN (ANALYZE, BUFFERS) 출력에 I/O 타이밍 정보가 세분화됐다. 특히 병렬 쿼리 플랜에서 각 워커의 buffer hit/read 비율을 별도로 확인할 수 있어 병목 진단이 쉬워졌다.

항목PostgreSQL 16PostgreSQL 17
정렬 알고리즘quicksortintrosort (최악 케이스 O(n log n))
Vacuum dead tuple 처리기존 방식개선된 vacuum 알고리즘
EXPLAIN 병렬 워커 정보집계값만 출력워커별 I/O 세분화 출력
논리 복제 failover미지원failover slot 지원
증분 백업외부 도구 필요pg_basebackup 내장
JSON_TABLE미지원SQL 표준 JSON_TABLE 지원

PostgreSQL 16 → 17 마이그레이션 — pg_upgrade 절차

pg_upgrade는 PostgreSQL 메이저 버전 업그레이드 시 데이터 파일을 물리적으로 복사하지 않고 카탈로그만 변환하는 방식으로 동작한다. 데이터 크기와 무관하게 수십 분 내에 업그레이드를 완료할 수 있다.

업그레이드 전 체크리스트

  • 전체 백업 확보 (pg_basebackup 또는 파일 스냅샷)
  • PostgreSQL 17 바이너리 설치 (16과 공존 가능)
  • 확장 프로그램(Extension) 17 호환 여부 확인 — SELECT * FROM pg_extension;로 목록 확인 후 각 확장의 17 지원 여부 검토
  • pg_hba.conf, postgresql.conf를 17 데이터 디렉터리로 복사할 준비
  • 복제 슬롯이 있는 경우: 논리 복제 구독자(subscriber)도 함께 업그레이드 계획 수립
pg_upgrade 실행 절차 (Linux 기준)
# 1. PostgreSQL 17 설치 (PostgreSQL 16과 공존) sudo apt install postgresql-17 # Ubuntu/Debian # 또는 sudo yum install postgresql17-server # RHEL/CentOS # 2. PostgreSQL 17 데이터 클러스터 초기화 sudo -u postgres /usr/lib/postgresql/17/bin/initdb -D /var/lib/postgresql/17/main # 3. 기존 16 서비스 중지 sudo systemctl stop postgresql@16-main # 4. pg_upgrade 호환성 검사 (--check 옵션: 실제 변경 없음) sudo -u postgres /usr/lib/postgresql/17/bin/pg_upgrade -b /usr/lib/postgresql/16/bin -B /usr/lib/postgresql/17/bin -d /var/lib/postgresql/16/main -D /var/lib/postgresql/17/main --check # 5. 실제 업그레이드 실행 (--link: 데이터 파일 하드링크, 공간 절약) sudo -u postgres /usr/lib/postgresql/17/bin/pg_upgrade -b /usr/lib/postgresql/16/bin -B /usr/lib/postgresql/17/bin -d /var/lib/postgresql/16/main -D /var/lib/postgresql/17/main --link # 6. PostgreSQL 17 서비스 시작 sudo systemctl start postgresql@17-main # 7. 통계 정보 재수집 (업그레이드 후 필수) sudo -u postgres /usr/lib/postgresql/17/bin/vacuumdb --all --analyze-in-stages # 8. 이전 버전 클러스터 삭제 (검증 후) sudo -u postgres ./delete_old_cluster.sh
주의: --link 옵션 사용 시 업그레이드가 완료된 후 pg_upgrade는 이전 클러스터 데이터 파일을 직접 참조한다. 업그레이드 후 16 클러스터를 다시 시작하면 데이터가 손상될 수 있다. 반드시 17 클러스터에서 충분히 검증한 뒤 delete_old_cluster.sh를 실행할 것.

Docker/컨테이너 환경: 공식 postgres:17 이미지를 사용하는 경우 pg_upgrade보다 논리적 덤프(pg_dump + psql)가 더 현실적이다. 볼륨 마운트 경로가 달라지는 경우가 많고, pg_upgrade는 동일 호스트에 두 버전이 설치된 환경을 전제한다.

Docker 환경 — pg_dump를 사용한 마이그레이션
# PostgreSQL 16 컨테이너에서 덤프 docker exec -t pg16_container pg_dumpall -U postgres > pg16_dump.sql # PostgreSQL 17 컨테이너 시작 docker run -d --name pg17_container -e POSTGRES_PASSWORD=secret -v /data/pg17:/var/lib/postgresql/data postgres:17 # 덤프 복원 docker exec -i pg17_container psql -U postgres < pg16_dump.sql

실무 영향 큰 변경점 — 호환성 주의 사항

PostgreSQL 17에는 동작 변경(behavior change)이 포함돼 있어 기존 쿼리나 설정이 예상과 다르게 동작할 수 있다. 업그레이드 전 반드시 확인해야 하는 항목이다.

1. pg_dump 출력 형식 변경: pg_dump의 기본 출력에서 일부 옵션 기본값이 변경됐다. 스크립트로 pg_dump를 자동 실행하는 경우 출력 형식을 명시적으로 지정하는 것이 안전하다.

2. libpq 연결 문자열 처리 변경: libpq(PostgreSQL 클라이언트 라이브러리)에서 연결 문자열 파싱 방식이 일부 변경됐다. ORM이나 드라이버 라이브러리가 구버전 libpq에 의존하는 경우 연결 오류가 발생할 수 있다. psycopg2, node-postgres, JDBC 드라이버를 최신 버전으로 업데이트한 후 진행할 것.

3. 확장 프로그램 호환성: PostGIS, pg_stat_statements, pgvector 등 주요 확장의 17 호환 버전 출시 여부를 사전 확인해야 한다. 특히 native 코드가 포함된 확장(.so 파일)은 PostgreSQL 버전마다 재컴파일이 필요하다.

4. search_path 기본값 변경: 보안 강화 목적으로 함수 실행 시 search_path 처리 방식이 변경됐다. SECURITY DEFINER 함수 또는 명시적 스키마 없이 함수를 호출하는 코드가 있다면 점검이 필요하다.

업그레이드 전 확인 명령

pg_upgrade --check 실행 결과에서 incompatible_extensions.txt, incompatible_roles.txt 파일이 생성된다면 해당 항목을 먼저 처리해야 한다. --check는 실제 데이터를 수정하지 않으므로 안전하게 사전 검증에 사용할 수 있다.
PostgreSQLPostgreSQL 17pg_upgradeJSON_TABLE데이터베이스마이그레이션

관련 포스트

PostgreSQL 성능 튜닝 실전 가이드2026-02-20Supabase vs Firebase 2026 — BaaS 양강 비교2026-02-25Drizzle ORM 실전 가이드 — TypeScript 네이티브 ORM2026-03-17REST API 설계 모범 사례 20262026-02-22