글 목록

최신 글과 검색 결과
DEVELOPMENT

PostgreSQL 메이저 버전 업그레이드, Windows에서 15→18 이전한 실전 과정

간지뽕빨리턴님

이 글의 목차

    반응형

    Windows 서버에서 운영 중이던 PostgreSQL 15를 PostgreSQL 18로 마이그레이션했다. 당시 실제 이전 대상은 PostgreSQL 18.4였고, 데이터베이스는 약 4~5GB 규모였다. 이 글은 단순히 명령어만 나열하는 설치 가이드가 아니라, pg_dump와 pg_restore를 이용해 운영 DB를 이전하면서 실제로 확인했던 설정, 만났던 오류, 복원 후 검증과 최종 전환 과정을 공식 PostgreSQL 문서와 함께 다시 정리한 기록이다.

    먼저 구분할 점 실제 작업 당시 PostgreSQL 15와 PostgreSQL 18은 모두 최종적으로 Port 5433을 사용했다. 따라서 같은 서버에서 두 서비스를 같은 Port로 동시에 기동한 것이 아니라, 기존 버전의 백업을 끝낸 뒤 서비스를 전환하는 방식으로 진행했다. 서버 주소, 비밀번호, 실제 DB명과 일부 설치 경로는 공개 글이므로 예제값으로 치환했다.
    현재 버전 정보 작업 당시 설치한 버전은 PostgreSQL 18.4였다. 2026년 8월 현재 PostgreSQL 18 계열의 최신 유지보수 버전은 18.6이다. 18.4에서 18.6처럼 같은 18.x 계열의 Minor Update는 메이저 버전 마이그레이션과 다르며, PostgreSQL 공식 릴리스 노트상 18.x에서 18.6으로 이동할 때 전체 dump/restore는 요구되지 않는다.

    1. 이번 PostgreSQL 15 → 18 마이그레이션 환경

    이번 작업의 핵심은 기존 PostgreSQL 15에서 사용하던 데이터베이스를 Windows 환경의 PostgreSQL 18로 옮기는 것이었다.

    항목 작업 환경
    운영체제 Windows
    기존 PostgreSQL PostgreSQL 15
    신규 PostgreSQL PostgreSQL 18.4
    현재 PostgreSQL 18 최신 유지보수 버전 18.6 (2026-08 기준)
    운영 Port 5433
    Database 규모 약 4~5GB
    이전 방식 pg_dump Custom Format + pg_restore

    PostgreSQL 15에서 18로의 이동은 단순 패치 업데이트가 아니라 메이저 버전 변경이다. PostgreSQL 공식 문서에서도 이전 메이저 버전에서 PostgreSQL 18로 이동하려면 dump/restore, pg_upgrade, Logical Replication 같은 마이그레이션 절차가 필요하다고 설명한다.

     

    특히 기존 PostgreSQL 15의 data 디렉터리를 PostgreSQL 18이 그대로 사용하도록 바꾸는 방식은 올바른 메이저 버전 업그레이드 방법이 아니다.

    2. 왜 pg_dump / pg_restore를 선택했나

    공식 문서에는 여러 업그레이드 방법이 있지만, 이번 환경에서는 기존 DB를 최대한 건드리지 않고 논리 백업 파일을 만든 뒤 새로운 PostgreSQL에 복원하는 방식을 선택했다.

    방법 장점 고려할 점
    pg_dump / pg_restore 백업 파일이 남고, 신규 환경을 분리해서 검증하기 쉽다. DB가 클수록 Dump와 Restore 시간이 길어진다.
    pg_upgrade 전체 Dump/Restore 없이 빠르게 메이저 버전을 올릴 수 있다. Cluster와 Extension 호환성을 사전에 확인해야 한다.
    Logical Replication 동기화 후 전환해 다운타임을 줄이기 좋다. 구성이 상대적으로 복잡하다.

    4~5GB 정도의 규모였던 이번 환경에서는 Dump/Restore에 필요한 시간을 감당할 수 있었고, 무엇보다 기존 PostgreSQL 15를 Rollback 수단으로 남겨둘 수 있다는 점이 컸다.

    실제 작업 흐름

    1. 1PostgreSQL 15 환경 확인버전, Port, Database, Extension, Service 상태를 먼저 확인했다.
    2. 2PostgreSQL 18 도구로 기존 DB 백업신규 버전의 pg_dump를 사용해 기존 PostgreSQL 15의 데이터를 Custom Format으로 저장했다.
    3. 3백업 파일 검증파일 생성 여부뿐 아니라 pg_restore가 Archive를 정상적으로 읽는지 확인했다.
    4. 4최종 쓰기 중지 및 마지막 백업운영 전환 직전에는 백업 이후 데이터가 달라지지 않도록 변경 작업을 통제했다.
    5. 5PostgreSQL 15 Service 중지두 버전이 같은 5433 Port를 사용하므로 기존 서비스를 먼저 중지했다.
    6. 6PostgreSQL 18 Service 시작신규 PostgreSQL 18을 같은 운영 Port 5433에서 기동했다.
    7. 7Database 생성 및 Restore필요한 Role과 Global Object를 확인한 뒤 pg_restore로 데이터를 복원했다.
    8. 8DB와 애플리케이션 검증테이블 건수뿐 아니라 실제 조회·등록·수정·프로시저 호출까지 확인했다.

    3. 가장 먼저 기존 PostgreSQL 15 상태부터 확인했다

    돌이켜보면 이 단계가 가장 중요했다. 새 버전을 설치하기 전에 기존 서버가 실제로 어떤 설정으로 운영되고 있는지를 먼저 알아야 했다.

    PostgreSQL 버전 확인

    SELECT version();

    현재 Database 확인

    SELECT current_database();

    실제 Port 확인

    SHOW port;

    Database 크기 확인

    SELECT pg_size_pretty(pg_database_size(current_database()));

    설치된 Extension 확인

    SELECT extname, extversion FROM pg_extension ORDER BY extname;
    이때 따로 기록해둔 것이 좋았던 정보 PostgreSQL 버전, Database 이름, DB 크기, 실제 Port, Windows Service 이름, Role, Extension, 설치 경로, 데이터 경로, 애플리케이션 Connection String이다. 마이그레이션 전후를 비교할 기준이 된다.

    4. Connection refused를 만나고 가장 먼저 확인한 것은 Port였다

    작업 중 다음과 같은 연결 오류를 만났다.

    오류 connection to server at "localhost", port 5432 failed: Connection refused

    처음 보면 PostgreSQL이 실행되지 않은 것처럼 보인다. 하지만 실제 운영 환경은 기본 Port인 5432가 아니라 5433을 사용하고 있었다.

     

    PostgreSQL은 기본값이 5432이기 때문에 Port를 생략한 명령이나 프로그램이 자동으로 5432에 접속하면서 생긴 문제였다. 이때부터는 기본값을 추측하지 않고 실제 설정을 직접 확인했다.

    SQL
    SHOW port;
    PowerShell
    Get-NetTCPConnection -LocalPort 5433
    PowerShell
    & "D:\PostgreSQL\18\bin\pg_isready.exe" -h 127.0.0.1 -p 5433
    Connection refused 확인 순서 Service가 실행 중인지 → 실제 Port가 무엇인지 → 해당 Port가 LISTEN 중인지 → 명령이나 Connection String이 같은 Port를 바라보는지 → postgresql.conf / pg_hba.conf → 방화벽 순으로 확인하면 원인을 빠르게 좁힐 수 있다.

    5. PostgreSQL 15 데이터베이스를 Custom Format으로 백업했다

    이번 작업에서 선택한 백업 형식은 Custom Format이다. PostgreSQL 공식 문서에서 -Fc로 생성한 Custom Format Archive는 pg_restore에서 선택적 복원과 재정렬이 가능하고 압축도 기본 적용되는 유연한 형식으로 설명한다.

     

    또 PostgreSQL 공식 업그레이드 문서는 메이저 버전 이전 시 가능하면 새 PostgreSQL 버전에 포함된 pg_dump / pg_dumpall 프로그램을 사용하는 것을 권장한다. 그래서 신규 PostgreSQL 18의 도구로 기존 PostgreSQL 15 서버에 접속해 백업하는 구조로 진행했다.

    백업 폴더 생성

    PowerShell
    New-Item -ItemType Directory -Path "D:\Backup\PostgreSQL" -Force

    Custom Format 백업

    PowerShell
    & "D:\PostgreSQL\18\bin\pg_dump.exe" -h 127.0.0.1 -p 5433 -U postgres -d MYDB -Fc -b -v -f "D:\Backup\PostgreSQL\MYDB_FULL.dump"
    옵션 의미
    -h 접속 Host
    -p PostgreSQL Port
    -U 접속 User
    -d 백업할 Database
    -Fc Custom Format Archive 생성
    -b Large Object 포함
    -v Verbose 로그 출력
    -f 출력 파일 지정
    스크린샷에 보였던 ^ 문자는 왜 뺐나? ^는 PostgreSQL 옵션이 아니라 Windows cmd.exe에서 한 명령을 여러 줄에 나눠 적을 때 사용하는 줄 연속 문자다. 이 글에서는 PowerShell과 cmd 문법이 섞이는 것을 피하기 위해 모든 실행 명령을 한 줄 PowerShell 형식으로 통일했다. 따라서 독자는 ^를 입력할 필요가 없다.

    6. No such file or directory 오류는 PostgreSQL 문제가 아니었다

    백업 단계에서는 다음과 비슷한 오류도 만났다.

    오류 could not open output file: No such file or directory

    처음에는 파일 권한을 의심했지만 원인은 단순했다. pg_dump의 출력 대상으로 지정한 폴더가 존재하지 않았다.

    파일명까지 정확하게 입력해도 상위 디렉터리를 pg_dump가 임의로 만들어주지는 않는다. 그래서 백업 전에 경로 존재 여부를 먼저 확인하고 필요하면 생성하도록 했다.

    PowerShell
    Test-Path "D:\Backup\PostgreSQL"
    PowerShell
    New-Item -ItemType Directory -Path "D:\Backup\PostgreSQL" -Force

    돌아보면 이번 마이그레이션에서 시간을 잡아먹었던 문제는 PostgreSQL 엔진의 복잡한 장애보다는 Port와 File Path처럼 기본 환경값을 정확히 확인하지 않았을 때 생기는 문제가 더 컸다.

    7. pg_dump 하나로 PostgreSQL 전체가 백업되는 것은 아니다

    공식 문서를 다시 보면 이 부분도 명확하다. pg_dump하나의 Database를 내보내는 도구다. Role이나 Tablespace처럼 Cluster 전역에서 사용하는 Global Object는 pg_dumpall의 범위다.

     

    필요한 경우 Global Object를 별도로 저장할 수 있다.

    PowerShell
    & "D:\PostgreSQL\18\bin\pg_dumpall.exe" -h 127.0.0.1 -p 5433 -U postgres --globals-only -f "D:\Backup\PostgreSQL\globals.sql"

    단순히 데이터만 살아 있으면 되는 시스템이 아니라 기존 Object Owner와 권한 구조를 유지해야 한다면, 이 부분을 빠뜨리지 않는 것이 중요하다.

    백업 대상으로 나눠서 본 항목 Database 내부 객체 / Role과 Tablespace 같은 Global Object / Extension / postgresql.conf와 pg_hba.conf 같은 서버 설정을 각각 별개의 영역으로 생각하는 것이 편했다.

    8. 백업은 파일 생성보다 실제로 읽을 수 있는지가 중요했다

    .dump 파일이 만들어졌다는 사실만으로 백업이 검증된 것은 아니다. Custom Format Archive라면 먼저 pg_restore -l로 Archive의 Table of Contents를 읽을 수 있는지 확인할 수 있다.

    PowerShell
    & "D:\PostgreSQL\18\bin\pg_restore.exe" -l "D:\Backup\PostgreSQL\MYDB_FULL.dump"

    실제로는 이것보다 더 확실한 방법이 있다. 신규 PostgreSQL에 직접 Restore해보는 것이다. 백업 파일의 목적은 보관 자체가 아니라 복구이기 때문이다.

    9. 같은 5433 Port를 사용했기 때문에 Service를 순서대로 전환했다

    이전 초안에서는 PostgreSQL 18을 임시 5434 Port에서 동시에 운영하는 예시를 넣었지만, 실제 이번 환경을 기준으로 보면 그 내용은 맞지 않았다.

     

    기존 PostgreSQL 15와 신규 PostgreSQL 18 모두 운영 Port가 5433이었기 때문에 같은 서버에서 두 Service를 동시에 5433으로 LISTEN시킬 수 없다. 그래서 최종 백업이 끝난 뒤 기존 Service를 내리고 새로운 Service를 올리는 순서로 보는 것이 정확하다.

    설치된 PostgreSQL Service 확인

    PowerShell
    Get-Service *postgres*

    기존 PostgreSQL 15 Service 중지

    PowerShell
    Stop-Service postgresql-x64-15

    PostgreSQL 18 Service 시작

    PowerShell
    Start-Service postgresql-x64-18
    서비스 이름은 환경마다 다를 수 있다 위 이름은 일반적인 Windows PostgreSQL 설치 형태다. 실제 작업에서는 Get-Service *postgres*로 먼저 등록된 서비스명을 확인한 뒤 사용하는 것이 안전하다.

    10. PostgreSQL 18에 Database를 만들고 pg_restore를 실행했다

    신규 Cluster가 정상적으로 5433에서 실행되는 것을 확인한 뒤 복원 대상 Database를 준비했다. 공식 pg_restore 문서의 예제처럼 완전히 빈 Database를 만들 때는 template0을 기반으로 생성하는 방식도 사용할 수 있다.

    PowerShell
    & "D:\PostgreSQL\18\bin\createdb.exe" -h 127.0.0.1 -p 5433 -U postgres -T template0 MYDB

    Global Object를 백업했다면 필요한 범위에서 먼저 적용한다.

    PowerShell
    & "D:\PostgreSQL\18\bin\psql.exe" -h 127.0.0.1 -p 5433 -U postgres -d postgres -X -f "D:\Backup\PostgreSQL\globals.sql"

    이후 Custom Format Archive를 복원한다.

    PowerShell
    & "D:\PostgreSQL\18\bin\pg_restore.exe" -h 127.0.0.1 -p 5433 -U postgres -d MYDB -v "D:\Backup\PostgreSQL\MYDB_FULL.dump"

    pg_restore는 기본적으로 오류가 발생해도 가능한 범위에서 계속 진행하고 마지막에 오류 수를 출력할 수 있기 때문에, 단순히 프로세스가 끝났다는 이유만으로 성공이라고 판단하지 않는 것이 중요하다.

     

    Restore 로그에서는 특히 다음 항목을 확인했다.

    • role does not exist
    • extension does not exist
    • permission denied
    • object already exists
    • Function / Procedure 생성 오류
    • Constraint / Index 생성 오류

    11. Restore 이후에는 데이터뿐 아니라 Object까지 비교했다

    테이블 몇 개가 조회된다고 해서 마이그레이션이 끝난 것은 아니었다. 운영 DB에서는 조회보다 등록·수정 과정에서 뒤늦게 문제가 드러나는 경우가 있기 때문이다.

    버전 확인

    SELECT version();

    Database 크기 확인

    SELECT pg_size_pretty(pg_database_size(current_database()));

    사용자 테이블 수 확인

    SELECT COUNT(*)
    FROM pg_catalog.pg_tables
    WHERE schemaname NOT IN ('pg_catalog', 'information_schema');

    Extension 확인

    SELECT extname, extversion FROM pg_extension ORDER BY extname;

    중요 테이블 데이터 건수 비교

    SELECT COUNT(*) FROM important_table;

    실제로 확인해야 할 대상은 더 넓었다.

    • 주요 업무 테이블의 Row Count
    • 최근 생성된 데이터
    • Primary Key / Foreign Key
    • Index
    • Sequence와 신규 INSERT
    • View
    • Function / Procedure
    • Trigger
    • Extension
    • Role / Permission

    특히 Sequence는 조회만으로는 이상을 알기 어렵다. 실제 INSERT까지 실행해 신규 Key가 정상적으로 생성되는지 확인하는 것이 좋다.

    12. Database가 정상이어도 애플리케이션 검증이 남아 있었다

    DB 콘솔에서 SELECT가 정상이라는 것과 실제 서비스가 정상이라는 것은 다르다. PostgreSQL 버전이 올라가면서 기존 Driver나 SQL, Function 호출 방식에서 예상하지 못한 문제가 발생할 수 있기 때문이다.

     

    Connection String에서는 다음을 다시 확인했다.

    • Host
    • Port 5433
    • Database
    • Username
    • Password
    • SSL 관련 설정

    이번 환경은 Host, Port, Database 이름을 기존과 동일하게 유지하는 방향이었기 때문에 애플리케이션 연결 설정을 크게 변경하지 않아도 되는 것이 장점이었다.

     

    하지만 실제 업무 기능은 별도로 확인했다.

    • 로그인
    • 목록 조회와 상세 조회
    • 신규 등록
    • 수정과 삭제
    • Transaction
    • Function / Procedure 호출
    • Batch
    • 파일 또는 대량 데이터 처리

    이 단계까지 와서야 Database Migration과 Service Migration은 다른 작업이라는 말이 실감났다.

    13. PostgreSQL 15를 바로 삭제하지 않은 이유

    PostgreSQL 18에서 Restore와 기본 테스트가 끝난 뒤에도 PostgreSQL 15를 곧바로 제거하지 않았다. 기존 설치와 Data Directory는 문제가 발생했을 때 돌아갈 수 있는 가장 직접적인 Rollback 수단이었다.

     

    최종 전환을 다시 정리하면 다음 순서다.

    1. 1애플리케이션 쓰기 중지백업 이후 데이터가 변경되지 않도록 최종 전환 구간을 만든다.
    2. 2PostgreSQL 15 최종 Dump전환 직전 시점의 데이터를 다시 백업한다.
    3. 3PostgreSQL 15 Service 중지Port 5433을 신규 서비스가 사용할 수 있도록 기존 서비스를 내린다.
    4. 4PostgreSQL 18 Service 시작동일 Port 5433에서 새로운 Cluster를 기동한다.
    5. 5Restore 및 DB 검증최종 Dump를 복원하고 데이터와 Object 상태를 확인한다.
    6. 6애플리케이션 검증 후 서비스 재개실제 업무 기능이 정상인 것을 확인한 뒤 사용자 접근을 다시 허용한다.
    Rollback에서 주의할 점 PostgreSQL 18 전환 후 사용자 쓰기 작업을 이미 허용했다면 단순히 PostgreSQL 15 Service를 다시 켜는 것만으로는 안전한 Rollback이 아니다. PostgreSQL 18에 새로 기록된 데이터가 PostgreSQL 15에는 없기 때문이다. 따라서 단순 Service Rollback은 새 버전에서 쓰기가 시작되기 전이 가장 안전하다.

    14. 공식 문서와 실제 경험을 함께 놓고 보니

    PostgreSQL 공식 문서에서 말하는 큰 원칙은 명확하다. 메이저 버전을 올릴 때는 dump/restore, pg_upgrade, Logical Replication 같은 정식 마이그레이션 방법을 사용해야 하고, 신중한 사용자는 새 버전 전환 전에 애플리케이션을 테스트하는 것이 좋다.

     

    실제 작업에서는 여기에 운영 환경 특유의 문제들이 더해졌다.

    • 기본값이라고 생각했던 5432가 실제 운영 Port와 달랐다.
    • 백업 출력 폴더가 없어 pg_dump가 파일을 만들지 못했다.
    • Database 백업과 Role 같은 Global Object 백업을 구분해야 했다.
    • Dump 파일 생성보다 Restore 검증이 더 중요했다.
    • PostgreSQL 15와 18이 같은 Port를 사용하므로 서비스 전환 순서가 중요했다.
    • DB 자체보다 실제 애플리케이션의 조회·등록·프로시저 호출 검증이 마지막 관문이었다.
    이번 작업을 다시 한 줄로 정리한다면 백업 → 백업 검증 → 쓰기 중지 → 기존 서비스 중지 → 신규 서비스 시작 → Restore → DB 검증 → 애플리케이션 검증 순서였다. 그리고 모든 단계 뒤에는 문제가 생겼을 때 어떻게 돌아갈 것인지에 대한 Rollback 계획이 필요했다.

    PostgreSQL 15 → 18 마이그레이션에서 자주 헷갈리는 부분

    Q. PostgreSQL 15의 data 폴더를 PostgreSQL 18에 그대로 연결하면 되나? 아니다. 15와 18은 서로 다른 메이저 버전이다. 공식적으로 지원되는 dump/restore, pg_upgrade 또는 Logical Replication 같은 방법을 사용해야 한다.
    Q. 왜 PostgreSQL 18의 pg_dump를 PostgreSQL 15에 사용했나? PostgreSQL 공식 업그레이드 문서는 새로운 버전의 pg_dump와 pg_dumpall에 포함된 개선 사항과 수정 사항을 활용할 수 있도록 신규 버전의 Dump 프로그램 사용을 권장한다.
    Q. PostgreSQL 15와 18을 둘 다 5433으로 동시에 실행할 수 있나? 같은 IP에서 두 프로세스가 동시에 같은 TCP Port를 LISTEN할 수는 없다. 두 버전을 동시에 실행해 비교하려면 하나의 Port를 변경해야 하고, 이번 작업처럼 최종적으로 동일한 5433을 유지하려면 기존 서비스를 중지한 뒤 신규 서비스를 시작해야 한다.
    Q. pg_dump 파일이 만들어졌으면 백업이 성공한 것 아닌가? 파일 생성은 첫 단계에 가깝다. 최소한 pg_restore가 Archive 목록을 읽을 수 있는지 확인하고, 가능하면 별도 신규 Cluster에 실제 Restore까지 수행해보는 것이 훨씬 안전하다.