Skip to main content
Spring Boot는 PostgreSQL JDBC 드라이버(pgjdbc)와 HikariCP 연결 풀을 통해 Postgres에 연결하며, 애플리케이션 시작 시 Flyway 마이그레이션을 실행합니다. 이 가이드에서는 Flyway 마이그레이션으로 todos 테이블을 생성하고, Spring Data JPA REST 컨트롤러로 생성, 조회, 수정, 삭제 요청을 처리합니다. 모든 연결에는 TLS가 사용되며, 인증서 검증이 완전하게 수행됩니다.

사전 요구 사항

  • JDK 17 이상(Spring Boot 4 요구 사항). 이 가이드는 Java 25, Spring Boot 4.1.1, pgjdbc 42.7.13, Hibernate 7.4.5, HikariCP 7.0.2, Flyway 12.4.0 환경에서 테스트되었습니다.
  • ClickHouse Cloud 계정
  • 데이터베이스 생성에 사용할 psql. SQL 콘솔에서 CREATE DATABASE SQL 문을 실행해도 됩니다.

ClickHouse Managed Postgres 서비스 생성

ClickHouse Cloud 콘솔에서 New service를 클릭한 다음 Postgres를 선택하십시오. 인스턴스는 몇 분 안에 준비됩니다. 단계별 안내는 빠른 시작 가이드를 참조하십시오.

연결 정보 확인하기

서비스의 왼쪽 사이드바에서 Connect를 클릭하십시오. 모달에는 사용자 이름, 비밀번호, 서버, 포트가 표시되며, Directly / via PgBouncer 토글도 있습니다. Directly가 선택된 상태에서 Use SSL을 켜고 jdbc 탭을 여십시오. 이 탭에 표시되는 JDBC URL은 두 가지만 수정하면 Spring Boot에서 사용할 수 있습니다. 수정할 항목은 데이터베이스 이름과 CA 인증서 파일 이름이며, 연결 구성하기에서 변경합니다.
JDBC URL에는 사용자 이름과 비밀번호가 포함되지 않습니다. Spring Boot는 이 값들을 별도의 속성에서 읽어옵니다. 이 가이드에서는 포트 5432를 통해 Postgres에 직접 연결합니다. Spring Boot 앱은 자체 HikariCP 풀(기본 10개 연결)을 유지하는 장기 실행 프로세스이므로 Postgres 앞에 별도의 풀러를 둘 필요가 없습니다. 앱 인스턴스 수와 풀 크기를 곱한 값이 서비스의 max_connections를 넘지 않도록 하십시오. 인스턴스를 많이 실행한다면 PgBouncer 사용을 참조하십시오. 모달에서 Download CA certificate를 클릭하십시오. Settings → CA Certificate에서도 다운로드할 수 있습니다. 이 인증서는 인스턴스마다 고유하므로, 드라이버는 이 인증서로 통신 대상이 해당 인스턴스의 서버가 맞는지 검증할 수 있습니다.

프로젝트 설정

Spring Initializr에서 Spring Web, Spring Data JPA, Flyway Migration, PostgreSQL Driver 의존성을 추가하여 Maven 프로젝트를 생성하십시오:
기존 앱에 추가하는 경우curl 명령은 건너뛰십시오. pom.xml에 spring-boot-starter-data-jpa, spring-boot-starter-flyway, org.flywaydb:flyway-database-postgresql 의존성과 runtime 범위의 org.postgresql:postgresql을 추가하고, 앱에서 H2를 사용 중이라면 com.h2database:h2를 제거하십시오. 그런 다음 연결 구성 단계를 진행하십시오.

연결 구성

다운로드한 CA 인증서를 프로젝트 디렉터리로 옮긴 다음 파일 이름을 ca-certificate.pem으로 변경하십시오. 애플리케이션용 데이터베이스를 생성하십시오. <PASSWORD>와 호스트는 Connect 모달에 표시된 값으로 바꾸십시오.
src/main/resources/application.properties의 내용을 다음으로 교체하십시오. 기존 앱이라면 spring.datasource 및 spring.jpa 줄을 추가하고, H2 URL 등 다른 spring.datasource 설정은 모두 제거하십시오. URL은 jdbc 탭에 표시된 값을 사용하되, 데이터베이스는 guide_spring으로, sslrootcert는 이름을 바꾼 파일로 변경합니다:
src/main/resources/application.properties
pgjdbc는 URL에서 sslmode=verify-full과 sslrootcert를 읽습니다. CA 파일에 있는 모든 인증서를 로드하고 인증서 체인과 호스트명을 모두 검사하므로 별도의 TLS 코드가 필요하지 않습니다. Spring Boot는 DATABASE_PASSWORD 환경 변수에서 비밀번호를 읽으므로 비밀번호를 파일에 저장하지 않아도 됩니다. 나머지 설정은 다음과 같습니다.
  • ddl-auto=validate를 설정하면 Hibernate가 시작 시 엔터티가 Flyway에서 생성한 테이블과 일치하는지 검사합니다. Hibernate는 스키마를 변경하지 않습니다.
  • open-in-view=false를 설정하면 HTTP 응답이 작성될 때까지 연결을 붙잡아 두지 않고, 트랜잭션이 끝나는 즉시 각 연결을 풀에 반환합니다.
sslrootcert에 상대 경로를 지정하면 JVM의 작업 디렉터리를 기준으로 해석됩니다. ./mvnw spring-boot:run은 프로젝트 디렉터리에서 앱을 시작하므로 이 경우에는 ca-certificate.pem을 그대로 사용할 수 있습니다. 패키징된 JAR를 다른 위치에서 실행할 때는 절대 경로를 사용하세요. pgjdbc는 ~를 확장하지 않습니다.Use SSL을 끈 상태에서 URL을 복사하면 sslmode가 포함되지 않으며, 이때 pgjdbc는 prefer를 사용합니다. 즉, 연결은 암호화하지만 서버는 검증하지 않습니다. CA가 잘못되었으면 PKIX path building failed 오류와 함께 연결에 실패합니다. 파일이 없으면 Could not open SSL root certificate file 오류가 발생합니다. IP 주소로 연결하는 경우처럼 호스트가 인증서와 일치하지 않으면 The hostname ... could not be verified by hostnameverifier PgjdbcHostnameVerifier 오류가 발생합니다.

Flyway로 테이블 생성하기

Flyway는 애플리케이션이 시작될 때 Hibernate가 스키마를 검사하기 전에 src/main/resources/db/migration에 있는 SQL 파일을 버전 순서대로 실행합니다. src/main/resources/db/migration/V1__create_todos.sql 파일을 생성하십시오.
src/main/resources/db/migration/V1__create_todos.sql
Flyway는 적용된 각 파일을 flyway_schema_history 테이블에 기록하며, 이후 다시 시작할 때는 새 파일만 실행합니다. 스키마를 변경하려면 이미 실행된 파일을 수정하지 말고 V2__add_due_date.sql과 같은 새 파일을 추가하세요.
H2를 사용하던 앱에서는 보통 Hibernate가 테이블을 생성했습니다. 내장 데이터베이스에서는 ddl-auto의 기본값이 create-drop이기 때문입니다. Postgres에서는 기본값이 none이므로, 기존 테이블을 생성하는 마이그레이션을 추가하세요. 또한 별도 옵션 없이 @GeneratedValue만 지정한 @Id에는 엔터티 이름을 딴 시퀀스(예: Employee라면 employee_seq)가 필요하며, 이 시퀀스는 Hibernate의 기본 할당 크기인 50씩 증가해야 합니다. 그렇지 않으면 시작 시 Schema validation: missing sequence [employee_seq] 오류가 발생하고, 시퀀스가 1씩 증가하도록 설정되어 있으면 The increment size of the [employee_seq] sequence is set to [50] in the entity mapping but the mapped database sequence increment size is [1] 오류가 발생합니다.

데이터베이스 쿼리하기

src/main/java/com/example/todos/Todo.java에 엔터티를 생성하세요. Spring Boot는 createdAt 필드를 created_at 컬럼에 매핑합니다.
src/main/java/com/example/todos/Todo.java
src/main/java/com/example/todos/TodoRepository.java에 Spring Data 리포지토리를 생성하세요. 이 인터페이스의 구현은 Spring Data가 런타임에 자동으로 제공합니다.
src/main/java/com/example/todos/TodoRepository.java
src/main/java/com/example/todos/TodoController.java에 REST 컨트롤러를 생성하고, 라우트마다 리포지토리를 한 번씩 호출하도록 작성하십시오:
src/main/java/com/example/todos/TodoController.java
각 리포지토리 호출은 HikariCP 풀에서 가져온 연결을 사용하여 개별 트랜잭션으로 실행됩니다. 여러 호출을 하나의 트랜잭션으로 묶으려면 해당 호출들을 @Transactional 어노테이션이 지정된 메서드 안에 넣으세요.

실행 및 확인

비밀번호를 설정하고 앱을 시작하십시오:
처음 시작할 때 Flyway는 웹 서버가 시작되기 전에 마이그레이션을 적용합니다. 로그에는 다음과 같은 내용이 출력됩니다. 여기서는 타임스탬프와 스레드 이름을 생략하고 표시합니다:
두 번째 터미널에서 할 일 2개를 생성하고, 첫 번째 항목은 완료로 표시한 다음 두 번째 항목은 삭제합니다.
POST 요청은 새로 생성된 행을 반환하고, PATCH 요청은 수정된 행을 반환합니다. -w '\n' 옵션은 각 응답 뒤에 줄 바꿈을 추가합니다.
브라우저에서 http://localhost:3113/todos를 열면 남은 할 일 목록을 확인할 수 있습니다. 응답은 다음과 같습니다.
애플리케이션의 연결에 TLS가 사용되는지 확인하려면 애플리케이션이 실행 중인 상태에서 pg_stat_ssl을 쿼리하십시오.
10개의 연결은 HikariCP 풀에서 생성된 것입니다. Ctrl+C를 눌러 애플리케이션을 중지하세요. 콘솔에서 데이터를 확인하려면 서비스의 왼쪽 사이드바에서 SQL 콘솔을 열고 guide_spring과 public을 차례로 펼친 다음 todos 테이블을 클릭하세요. 그 옆에 있는 flyway_schema_history 테이블에는 마이그레이션 이력이 저장됩니다. todos 테이블에는 남아 있는 행이 다음과 같이 표시됩니다.

PgBouncer 사용

여러 앱 인스턴스의 연결 수를 합한 값이 Postgres 연결 한도에 가까워지면, 앱은 포트 6432에서 함께 제공되는 PgBouncer를 통해 연결하고 Flyway는 계속 직접 연결을 사용하십시오. application.properties에서 spring.datasource.url의 포트를 6432로 변경하고, 그 아래에 다음 spring.flyway 행을 추가하십시오.
src/main/resources/application.properties
spring.flyway.url이 설정되어 있으면 Flyway는 자체 연결을 열며 spring.datasource의 사용자 이름과 비밀번호를 사용하지 않으므로, spring.flyway.user와 spring.flyway.password도 함께 설정하세요. 두 포트 모두 동일한 CA 인증서를 사용할 수 있습니다. PgBouncer는 트랜잭션 풀링 모드(transaction pooling mode)로 실행되므로 트랜잭션마다 서로 다른 Postgres 연결에서 실행될 수 있습니다. Spring Boot를 사용할 때는 다음 사항에 유의하세요.
  • Prepared statement는 기본 드라이버 설정으로 동작하므로 prepareThreshold=0을 지정할 필요가 없습니다. pgjdbc는 하나의 연결에서 같은 SQL 문이 다섯 번째로 실행될 때부터(prepareThreshold) 이름이 지정된 서버 측 prepared statement를 사용합니다. 이 SQL 문은 프로토콜 수준의 Close 메시지로 해제되며, 번들로 제공되는 PgBouncer가 이를 처리합니다.
  • pgjdbc 42.7.5 이상이 필요합니다. 이전 버전은 extra_float_digits 시작 매개변수를 전송하므로, PgBouncer가 FATAL: unsupported startup parameter: extra_float_digits 오류와 함께 연결을 거부합니다. Spring Boot 4에는 최신 pgjdbc가 포함되어 있습니다. 이전 Spring Boot 버전에서는 pom.xml의 <properties> 섹션에 <postgresql.version>42.7.13</postgresql.version>을 설정하세요.
  • 세션 설정은 유지되지 않습니다. SET 문과 Connection.setSchema(HikariCP의 spring.datasource.hikari.schema 속성이 사용)는 해당 문을 실행한 Postgres 연결에만 적용되며, 다른 클라이언트에 영향을 줄 수 있습니다. currentSchema 및 options URL 매개변수는 FATAL: unsupported startup parameter 오류와 함께 실패합니다. 스키마를 명시한 테이블 이름을 사용하거나 트랜잭션 내에서 SET LOCAL을 사용하세요.
  • Flyway는 트랜잭션 수준의 advisory 잠금을 사용하므로 기본 설정으로 PgBouncer를 통해 동작합니다. CREATE INDEX CONCURRENTLY를 실행하는 마이그레이션에는 spring.flyway.postgresql.transactional-lock=false가 필요하며, 이 설정을 사용하면 Flyway가 세션 수준 잠금으로 전환됩니다. PgBouncer를 거치면 이 잠금은 Unable to release PostgreSQL advisory lock 오류와 함께 실패하고 풀링된 연결에 그대로 남으므로, Flyway에는 직접 연결을 사용하세요.
  • 풀 크기: 각 HikariCP 연결은 PgBouncer에 대한 부담이 적은 클라이언트 연결입니다. PgBouncer는 애플리케이션 인스턴스가 여는 클라이언트 연결 수와 관계없이, 데이터베이스 및 사용자별로 여는 Postgres 연결 수를 제한합니다(default_pool_size). 이 한도를 초과한 트랜잭션은 HikariCP에서 실패하지 않고 PgBouncer에서 최대 query_wait_timeout(기본값 120초) 동안 대기합니다. HikariCP 풀을 늘려도 동시성은 이 한도까지만 높아지므로, 인스턴스당 기본값인 10으로 시작하는 것이 좋습니다. 자세한 내용은 PgBouncer 및 Postgres 연결 한도를 참조하세요.
pg_stat_ssl은 PgBouncer와 Postgres 간의 연결을 보고하므로, 실행 및 확인의 TLS 쿼리에는 PgBouncer를 거치는 애플리케이션 연결이 표시되지 않습니다. 하지만 애플리케이션과 PgBouncer 간의 연결도 검증되므로, 잘못된 CA 인증서를 사용하면 마찬가지로 연결에 실패합니다.

다음 단계

  • 연결: 연결 문자열(connection string), PgBouncer, TLS
  • 설정: max_connections 등의 Postgres 및 PgBouncer 매개변수
  • 읽기 레플리카(Read Replica): 읽기 전용 쿼리를 레플리카로 전송
  • 보안: IP 액세스 목록 및 프라이빗 네트워킹
마지막 수정일 2026년 10월 14일