IAM 인증을 사용하여 Java 애플리케이션을 PostgreSQL용 AlloyDB에 연결

1. 시작하기 전에

Java 및 AlloyDB: 간단하고 안전한 연결

PostgreSQL용 AlloyDB는 까다로운 엔터프라이즈 워크로드를 위한 Google Cloud의 완전 관리형 PostgreSQL 호환 데이터베이스 서비스입니다. Java 애플리케이션을 AlloyDB에 안전하고 효율적으로 연결하려면 표준 JDBC 드라이버를 뛰어넘는 클라이언트 라이브러리인 AlloyDB Java 커넥터(alloydb-jdbc-connector)를 사용해야 합니다.

AlloyDB Java 커넥터의 마법

alloydb-jdbc-connector는 표준 PostgreSQL JDBC 드라이버(org.postgresql:postgresql)의 스마트 SocketFactory 역할을 합니다. 보안 데이터베이스 연결 설정을 추상화하므로 네트워크 보안이나 사용자 인증 정보 순환보다는 애플리케이션 로직에 집중할 수 있습니다.

  • mTLS 1.3 암호화: SSL/TLS 클라이언트 인증서를 수동으로 관리할 필요 없이 JVM과 AlloyDB 인스턴스 간에 상호 인증된 TLS 1.3 연결을 자동으로 설정합니다.
  • IAM 기반 승인: 커넥터는 정적 데이터베이스 비밀번호를 사용하는 대신 Google Cloud IAM 및 애플리케이션 기본 사용자 인증 정보(ADC)를 활용하여 단기 OAuth2 액세스 토큰을 자동으로 가져옵니다.
  • 동적 IP 확인: AlloyDB Admin API를 쿼리하여 데이터베이스 인스턴스의 현재 IP 주소(PRIVATE, PUBLIC 또는 Private Service Connect PSC)를 동적으로 확인합니다. 호스트 이름이나 IP 주소를 하드 코딩할 필요가 없으므로 장애 조치 및 유지보수가 투명하게 이루어집니다.

AlloyDB Java 커넥터의 내부 작동 방식

기본 요건

  • Java, Maven, 기본 SQL/JDBC 개념에 대한 지식
  • Google Cloud CLI(gcloud) 및 IAM 역할에 대한 기본 지식

실습할 내용

  • 비밀번호 없는 IAM 데이터베이스 인증을 위해 AlloyDB 인스턴스와 서비스 계정을 구성합니다.
  • IAM 주 구성원에게 최소 권한 PostgreSQL 스키마 및 테이블 권한을 부여합니다.
  • AlloyDB SocketFactory를 사용하여 일반 JDBC 및 HikariCP로 프로덕션 등급 DataSource를 구성합니다.
  • Spring Boot, Quarkus, Micronaut 애플리케이션에 AlloyDB Java 커넥터를 통합합니다.
  • GraalVM 네이티브 이미지 컴파일을 위해 AlloyDB SocketFactory를 등록하고 일반적인 IAM 인증 문제를 해결합니다.

필요한 항목

  • 결제가 사용 설정되고 활성 AlloyDB 클러스터 및 기본 인스턴스가 있는 Google Cloud 프로젝트(AlloyDB 클러스터 생성 가이드를 참고하거나 2초 이내에 AlloyDB Express 클러스터를 가동)
  • 프로젝트의 roles/alloydb.admin 및 roles/resourcemanager.projectIamAdmin IAM 역할
  • Java 17 이상(Java 17 또는 21 LTS 권장) 및 Apache Maven이 설치되어 있어야 합니다.
  • gcloud CLI 및 psql 클라이언트가 설치되어 있거나 Google Cloud Shell에 액세스할 수 있어야 합니다.

2. AlloyDB에서 IAM 데이터베이스 인증 설정

Java 코드를 작성하기 전에 IAM 토큰을 허용하도록 AlloyDB 인스턴스를 구성하고 애플리케이션이 Google Cloud IAM, AlloyDB 컨트롤 플레인, PostgreSQL 엔진의 세 가지 레이어에서 사용할 IAM ID를 만들어야 합니다.

AlloyDB IAM 데이터베이스 인증을 위한 3계층 설정

환경 변수 설정

터미널에서 Google Cloud 프로젝트 및 AlloyDB 인스턴스에 해당하는 변수를 내보냅니다( 자리표시자를 값으로 바꿈).

export PROJECT_ID="<PROJECT_ID>"
export REGION="europe-west1"
export CLUSTER_ID="<CLUSTER_ID>"
export INSTANCE_ID="<INSTANCE_ID>"
export DB_NAME="dvdrental"

필수 API 사용 설정

프로젝트에서 AlloyDB, Compute Engine, Cloud Resource Manager, IAM, Service Usage API를 사용 설정합니다.

gcloud services enable \
    alloydb.googleapis.com \
    compute.googleapis.com \
    cloudresourcemanager.googleapis.com \
    iam.googleapis.com \
    serviceusage.googleapis.com \
    --project="${PROJECT_ID}"

서비스 계정 만들기 및 IAM 역할 부여

Java 애플리케이션의 ID 역할을 할 전용 서비스 계정을 만듭니다.

gcloud iam service-accounts create java-alloydb-connector \
    --project="${PROJECT_ID}" \
    --display-name="Java Application DB Connector"

AlloyDB Java 커넥터에 필요한 두 가지 IAM 역할을 서비스 계정에 부여합니다.

  1. roles/alloydb.client: AlloyDB Admin API에서 인스턴스 메타데이터를 가져오고 임시 mTLS 인증서를 요청하는 데 필요합니다.
  2. roles/serviceusage.serviceUsageConsumer: IAM 데이터베이스 인증에 필요합니다.
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/alloydb.client"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/serviceusage.serviceUsageConsumer"

AlloyDB 인스턴스에서 IAM 인증 사용 설정

AlloyDB 인스턴스 플래그를 업데이트하여 alloydb.iam_authentication=on를 설정합니다. 이 플래그는 동적 플래그이며 인스턴스를 다시 시작하지 않고 즉시 적용됩니다.

gcloud alloydb instances update "${INSTANCE_ID}" \
    --cluster="${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --database-flags=alloydb.iam_authentication=on

IAM 데이터베이스 사용자 등록

gcloud를 사용하여 AlloyDB 클러스터에 서비스 계정을 IAM_BASED 데이터베이스 사용자로 등록합니다.

gcloud alloydb users create "java-alloydb-connector@${PROJECT_ID}.iam" \
    --cluster="${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --type=IAM_BASED

PostgreSQL 스키마 및 테이블 권한 부여

새로 생성된 IAM 데이터베이스 사용자에게는 기본적으로 테이블 권한이 없습니다. postgres 관리자 사용자로 AlloyDB 인스턴스에 연결하고(Cloud 콘솔의 psql 또는 AlloyDB Studio를 통해) 대상 데이터베이스 및 테이블(예: DVD 대여 샘플 데이터베이스)에 대한 액세스 권한을 부여합니다.

-- Connect to your target database first
\c dvdrental

-- (Optional) Create a sample film table if your database is empty
CREATE TABLE IF NOT EXISTS public.film (
    film_id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    release_year INTEGER DEFAULT 2006,
    rating VARCHAR(10) DEFAULT 'PG',
    rental_rate NUMERIC(4,2) DEFAULT 2.99
);

INSERT INTO public.film (film_id, title, release_year, rating, rental_rate) VALUES
    (1, 'Academy Dinosaur', 2006, 'PG', 0.99),
    (2, 'Ace Goldfinger', 2006, 'G', 4.99),
    (3, 'Adaptation Holes', 2006, 'NC-17', 2.99),
    (4, 'Affair Prejudice', 2006, 'G', 2.99),
    (5, 'African Egg', 2006, 'G', 2.99)
ON CONFLICT (film_id) DO NOTHING;

-- Grant permissions to the IAM user (double quotes are required because of '@' and '.')
GRANT CONNECT ON DATABASE "dvdrental" TO "java-alloydb-connector@<PROJECT_ID>.iam";
GRANT USAGE ON SCHEMA public TO "java-alloydb-connector@<PROJECT_ID>.iam";
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "java-alloydb-connector@<PROJECT_ID>.iam";

AlloyDB Studio(또는 psql)에서 이러한 문을 실행할 때 public.film 스키마를 검사하고 샘플 행을 확인할 수 있습니다.

AlloyDB Studio에서 IAM 권한 부여 및 public.film 검사

로컬 애플리케이션 기본 사용자 인증 정보(ADC) 구성

Cloud Run, Google Kubernetes Engine(GKE) 또는 Compute Engine에서 실행하는 경우 java-alloydb-connector 서비스 계정을 워크로드에 직접 연결합니다. 워크스테이션에서 로컬로 실행할 때 ADC가 해당 ID의 토큰을 생성하도록 서비스 계정을 가장합니다.

gcloud auth application-default login \
    --impersonate-service-account="java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com"

3. 일반 JDBC 및 HikariCP로 연결

프로덕션 등급 Java 애플리케이션의 경우 연결 풀이 필수적이며 HikariCP가 업계 표준입니다. 이 단계에서는 소켓 생성을 com.google.cloud.alloydb.SocketFactory에 위임하는 일반 JDBC + HikariCP 애플리케이션을 빌드합니다.

Maven 프로젝트 만들기(pom.xml)

PostgreSQL JDBC 드라이버(42.7.14), AlloyDB Java 커넥터(1.4.0), HikariCP(7.1.0)가 포함된 pom.xml 파일로 새 디렉터리 jdbc-app를 만듭니다.

mkdir -p jdbc-app/src/main/java
cd jdbc-app

pom.xml 만들기:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.alloydb</groupId>
    <artifactId>alloydb-jdbc-sample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <exec.mainClass>AlloyDBConnectivity</exec.mainClass>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.14</version>
        </dependency>

        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>alloydb-jdbc-connector</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>7.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.16</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.5.0</version>
                <configuration>
                    <cleanupDaemonThreads>false</cleanupDaemonThreads>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

AlloyDBConnectivity.java 만들기

src/main/java/AlloyDBConnectivity.java 생성 (GitHub Gist에서 보기):

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class AlloyDBConnectivity {
    // Retrieve configuration from environment variables
    private static final String ALLOYDB_INSTANCE_URI = System.getenv("ALLOYDB_INSTANCE_NAME"); // e.g., projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>
    private static final String ALLOYDB_DB_NAME = System.getenv("ALLOYDB_DB"); // e.g., "dvdrental"
    private static final String ALLOYDB_USER = System.getenv("ALLOYDB_USER"); // e.g., "java-alloydb-connector@<PROJECT_ID>.iam"
    private static final String ALLOYDB_PASS = System.getenv("ALLOYDB_PASS"); // Database password if not using IAM auth

    public static HikariDataSource createDataSource() {
        HikariConfig config = new HikariConfig();

        // The JDBC URL does not need a host or port. The SocketFactory will resolve the IP address.
        config.setJdbcUrl(String.format("jdbc:postgresql:///%s", ALLOYDB_DB_NAME));
        config.setUsername(ALLOYDB_USER);

        // Only set the password if you are NOT using IAM authentication.
        if (System.getenv("ALLOYDB_ENABLE_IAM_AUTH") == null || !System.getenv("ALLOYDB_ENABLE_IAM_AUTH").equalsIgnoreCase("true")) {
            config.setPassword(ALLOYDB_PASS);
        }

        // 1. Set the SocketFactory to the AlloyDB Connector's implementation.
        config.addDataSourceProperty("socketFactory", "com.google.cloud.alloydb.SocketFactory");

        // 2. Tell the connector which AlloyDB instance to connect to.
        config.addDataSourceProperty("alloydbInstanceName", ALLOYDB_INSTANCE_URI);

        // 3. (Recommended) Enable IAM database authentication.
        if (System.getenv("ALLOYDB_ENABLE_IAM_AUTH") != null && System.getenv("ALLOYDB_ENABLE_IAM_AUTH").equalsIgnoreCase("true")) {
            config.addDataSourceProperty("alloydbEnableIAMAuth", "true");
        }

        // 4. (Optional) Specify IP type (e.g., "PRIVATE", "PUBLIC", "PSC"). Defaults to PRIVATE.
        String ipType = System.getenv("ALLOYDB_IP_TYPE");
        if (ipType != null) {
            config.addDataSourceProperty("alloydbIpType", ipType);
        }

        // Baseline HikariCP settings
        config.setMaximumPoolSize(10);
        config.setMinimumIdle(2);
        config.setConnectionTimeout(30000); // 30 seconds
        config.setIdleTimeout(600000);      // 10 minutes
        config.setMaxLifetime(1800000);     // 30 minutes

        return new HikariDataSource(config);
    }

    public static void main(String[] args) {
        String sql = "SELECT title, release_year FROM public.film ORDER BY film_id LIMIT 5";

        try (HikariDataSource pool = createDataSource();
             Connection conn = pool.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {
            System.out.println("Successfully connected to AlloyDB via IAM! Sample films:");
            while (rs.next()) {
                System.out.printf(" - %s (%d)%n", rs.getString("title"), rs.getInt("release_year"));
            }
        } catch (SQLException e) {
            System.err.println("Database connection failed: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

AlloyDB JDBC URL 및 HikariCP 구성 분석

일반 JDBC 애플리케이션 실행

AlloyDB 인스턴스의 환경 변수를 내보내고(공개 IP를 통해 VPC 외부에서 연결하는 경우 ALLOYDB_IP_TYPE="PUBLIC"를 설정하고 VPC 내부인 경우 "PRIVATE" / "PSC"를 설정) 애플리케이션을 실행합니다.

export ALLOYDB_INSTANCE_NAME="projects/${PROJECT_ID}/locations/${REGION}/clusters/${CLUSTER_ID}/instances/${INSTANCE_ID}"
export ALLOYDB_DB="${DB_NAME}"
export ALLOYDB_USER="java-alloydb-connector@${PROJECT_ID}.iam"
export ALLOYDB_ENABLE_IAM_AUTH="true"
export ALLOYDB_IP_TYPE="PRIVATE" # Set to PUBLIC if connecting over Public IP from a local laptop

mvn compile exec:java

다음과 비슷한 출력이 표시됩니다.

[AlloyDBConnectivity.main()] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[AlloyDBConnectivity.main()] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1998bf07
[AlloyDBConnectivity.main()] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
Successfully connected to AlloyDB via IAM! Sample films:
 - Academy Dinosaur (2006)
 - Ace Goldfinger (2006)
 - Adaptation Holes (2006)
 - Affair Prejudice (2006)
 - African Egg (2006)

일반 JDBC 및 HikariCP 샘플의 터미널 출력

4. Spring Boot와 연결

Spring Boot는 애플리케이션 속성에서 HikariCP 연결 풀을 자동으로 구성하여 데이터베이스 연결을 간소화합니다. AlloyDB Java 커넥터를 통합하는 데 커스텀 DataSource Bean 코드가 필요하지 않습니다.

Spring Boot 프로젝트 만들기(pom.xml)

새 spring-boot-app 디렉터리를 만듭니다.

cd ..
mkdir -p spring-boot-app/src/main/java/com/example/alloydb
mkdir -p spring-boot-app/src/main/resources
cd spring-boot-app

spring-boot-starter-jdbc(HikariCP 포함), org.postgresql:postgresql, com.google.cloud:alloydb-jdbc-connector를 사용하여 pom.xml를 만듭니다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.0.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example.alloydb</groupId>
    <artifactId>alloydb-spring-boot-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.14</version>
        </dependency>

        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>alloydb-jdbc-connector</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.properties 또는 환경 변수 구성

src/main/resources/application.properties에서 연결 URL을 구성할 수 있습니다.

# No host or port is needed; the SocketFactory handles it.
spring.datasource.url=jdbc:postgresql:///dvdrental?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>&alloydbEnableIAMAuth=true
spring.datasource.username=java-alloydb-connector@<PROJECT_ID>.iam
# No password required when alloydbEnableIAMAuth is true

소스 파일에 프로젝트 ID 또는 인스턴스 이름이 전혀 포함되지 않도록 하려면 application.properties를 비워 두고 런타임 시 다음 표준 Spring Boot 환경 변수를 제공하면 됩니다.

환경 변수

값

SPRING_DATASOURCE_URL

jdbc:postgresql:///dvdrental?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects//locations//clusters//instances/&alloydbEnableIAMAuth=true

SPRING_DATASOURCE_USERNAME

java-alloydb-connector@.iam

AlloyDbTestApplication.java 만들기

src/main/java/com/example/alloydb/AlloyDbTestApplication.java 생성 (GitHub Gist에서 보기):

package com.example.alloydb;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
public class AlloyDbTestApplication {

    public static void main(String[] args) {
        System.exit(SpringApplication.exit(SpringApplication.run(AlloyDbTestApplication.class, args)));
    }

    @Bean
    CommandLineRunner testConnection(JdbcTemplate jdbcTemplate) {
        return args -> {
            String result = jdbcTemplate.queryForObject("SELECT 'Connection Successful!'", String.class);
            System.out.println(">>> ALLOYDB STATUS: " + result);
        };
    }
}

Spring Boot 애플리케이션 실행

환경 변수를 내보내고 mvn spring-boot:run로 앱을 실행합니다.

export SPRING_DATASOURCE_URL="jdbc:postgresql:///${DB_NAME}?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects/${PROJECT_ID}/locations/${REGION}/clusters/${CLUSTER_ID}/instances/${INSTANCE_ID}&alloydbEnableIAMAuth=true"
export SPRING_DATASOURCE_USERNAME="java-alloydb-connector@${PROJECT_ID}.iam"

mvn spring-boot:run

다음과 같이 표시됩니다.

>>> ALLOYDB STATUS: Connection Successful!

Spring Boot 자동 구성 및 실시간 실행 출력

5. Quarkus, Micronaut, GraalVM 네이티브 이미지와 연결

AlloyDB Java 커넥터는 JDBC 드라이버 수준에서 작동하므로 클라우드 네이티브 Java 프레임워크 간에 전환해도 데이터베이스 트래픽을 보호하거나 ID를 관리하는 방식은 변경되지 않습니다. Quarkus와 Micronaut는 모두 기본 JDBC 드라이버를 래핑하고 mTLS 암호화, 동적 IP 확인, IAM 인증을 ADC를 통해 커넥터에 위임합니다.

AlloyDB를 사용하는 클라우드 네이티브 Java 프레임워크 및 GraalVM 네이티브 이미지

Quarkus 구성(Agroal을 사용하는 application.properties)

Quarkus 애플리케이션(quarkus-agroal 및 quarkus-jdbc-postgresql)에서 com.google.cloud:alloydb-jdbc-connector:1.4.0를 종속 항목에 추가하고 src/main/resources/application.properties의 quarkus.datasource.jdbc.additional-jdbc-properties를 통해 커넥터 매개변수를 전달합니다.

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=java-alloydb-connector@<PROJECT_ID>.iam
quarkus.datasource.jdbc.url=jdbc:postgresql:///dvdrental
quarkus.datasource.jdbc.additional-jdbc-properties.socketFactory=com.google.cloud.alloydb.SocketFactory
quarkus.datasource.jdbc.additional-jdbc-properties.alloydbInstanceName=projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>
quarkus.datasource.jdbc.additional-jdbc-properties.alloydbEnableIAMAuth=true

Micronaut 구성(HikariCP를 사용하는 application.yml)

Micronaut 애플리케이션(micronaut-jdbc-hikari)에서 com.google.cloud:alloydb-jdbc-connector:1.4.0를 추가하고 src/main/resources/application.yml를 구성합니다.

datasources:
  default:
    url: jdbc:postgresql:///dvdrental?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>&alloydbEnableIAMAuth=true
    username: java-alloydb-connector@<PROJECT_ID>.iam
    driver-class-name: org.postgresql.Driver

GraalVM 네이티브 이미지용 커넥터 등록

애플리케이션을 GraalVM 네이티브 이미지(Spring Boot 3+, Quarkus 또는 Micronaut 사용)로 컴파일할 때 중요한 세부정보가 하나 있습니다. 바로 리플렉션입니다.

PostgreSQL JDBC 드라이버는 리플렉션을 사용하여 클래스 이름으로 com.google.cloud.alloydb.SocketFactory를 동적으로 인스턴스화합니다. 네이티브 컴파일 중에 GraalVM의 정적 분석이 클래스를 삭제하지 않도록 하려면 명시적으로 등록합니다.

  • Spring Boot 3 이상: 기본 애플리케이션 또는 @Configuration 클래스에 @RegisterReflectionForBinding(com.google.cloud.alloydb.SocketFactory.class)를 추가합니다.
    @SpringBootApplication
    @RegisterReflectionForBinding(com.google.cloud.alloydb.SocketFactory.class)
    public class AlloyDbTestApplication { ... }
    
  • Quarkus: 기본 클래스 또는 CDI Bean에 @RegisterForReflection(targets = com.google.cloud.alloydb.SocketFactory.class)를 추가합니다.
    @QuarkusMain
    @RegisterForReflection(targets = com.google.cloud.alloydb.SocketFactory.class)
    public class QuarkusAlloyDbApp implements QuarkusApplication { ... }
    

6. 일반적인 IAM 및 연결 문제 해결

FATAL: password authentication failed for user 또는 AlloyDB IAM authentication failed 오류가 발생하면 다음 네 가지 영역을 확인하세요.

AlloyDB IAM 및 연결 문제 해결 치트 시트

1. 서비스 계정 데이터베이스 사용자 이름에서 .gserviceaccount.com 삭제

IAM 인증 실패의 가장 일반적인 원인은 전체 서비스 계정 이메일 주소를 데이터베이스 사용자 이름으로 전달하는 것입니다. AlloyDB는 주 구성원 유형에 따라 IAM 데이터베이스 사용자 이름을 다르게 지정합니다.

  • IAM 서비스 계정의 경우(.gserviceaccount.com 삭제): Google Cloud 서비스 계정 이메일이 java-alloydb-connector@.iam.gserviceaccount.com인 경우 .gserviceaccount.com 서픽스를 삭제하고 JDBC/AlloyDB 데이터베이스 사용자 이름을 java-alloydb-connector@.iam로 설정해야 합니다.
  • IAM 사용자 계정 또는 그룹(전체 이메일 유지): 실제 Google Workspace 또는 Cloud ID 사용자로 인증하는 경우 전체 이메일 주소를 수정하지 않고 사용합니다(예: ).

2. 데이터베이스 사용자가 생성된 방식 확인

gcloud alloydb users create --type=IAM_BASED(또는 Console/Terraform)를 통해 사용자를 등록했나요? AlloyDB는 GRANT alloydbiamuser를 컨트롤 플레인으로 제한하므로 원시 SQL CREATE USER을 사용하여 사용자를 만들면 IAM 인증에 작동하지 않습니다.

3. 활성 ADC ID 확인

Java 프로세스가 실행 중인 환경이 실제로 대상 IAM 주 구성원으로 인증되었는지 확인합니다.

  • 로컬 워크스테이션(서비스 계정으로 연결): ADC가 개인 이메일이 아닌 서비스 계정의 토큰을 생성하도록 gcloud auth application-default login --impersonate-service-account=java-alloydb-connector@.iam.gserviceaccount.com를 실행합니다.
  • Cloud Run/GKE: 서비스 계정이 워크로드에 연결되어 있고 roles/alloydb.client 및 roles/serviceusage.serviceUsageConsumer를 모두 보유하고 있는지 확인합니다.

4. IAM 전파 및 네트워크 라우팅 고려

  • IAM 전파 지연: IAM 정책 바인딩이 전파되는 데 60~120초가 걸릴 수 있습니다. 서비스 계정 또는 역할 바인딩을 방금 만든 경우 잠시 기다렸다가 다시 시도하세요.
  • VPC 네트워크 액세스: 기본적으로 AlloyDB Java 커넥터는 인스턴스의 PRIVATE IP에 연결됩니다. 애플리케이션이 Cloud Run에서 실행되는 경우 VPC 직접 연결 이그레스를 구성하거나(기존 서버리스 VPC 액세스 커넥터보다 지연 시간과 비용이 낮아 권장됨) Private Service Connect(PSC)를 통해 연결합니다. VPC 외부의 로컬 머신에서 연결하는 경우 인스턴스에서 공개 IP를 사용 설정하고 alloydbIpType=PUBLIC를 전달합니다.

7. 삭제

Google Cloud 계정에 불필요한 요금이 청구되지 않도록 하고 이 Codelab에서 만든 IAM ID를 삭제하려면 아래 리소스를 삭제하세요.

1. AlloyDB IAM 데이터베이스 사용자 및 서비스 계정 삭제

AlloyDB 클러스터에서 IAM 데이터베이스 사용자를 삭제하고 서비스 계정을 삭제합니다.

# Delete the IAM database user from AlloyDB
gcloud alloydb users delete "java-alloydb-connector@${PROJECT_ID}.iam" \
    --cluster="${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --quiet

# Remove IAM policy bindings
gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/alloydb.client"

gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/serviceusage.serviceUsageConsumer"

# Delete the Service Account
gcloud iam service-accounts delete \
    "java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --project="${PROJECT_ID}" \
    --quiet

2. 로컬 애플리케이션 기본 사용자 인증 정보 재설정

로컬 ADC 가장을 구성한 경우 로컬 사용자 인증 정보를 기본 사용자 ID로 재설정합니다.

gcloud auth application-default login

3. (선택사항) AlloyDB 클러스터 삭제

gcloud alloydb clusters delete "${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --force \
    --quiet

8. 축하합니다

Codelab을 완료하고 Java에서 PostgreSQL용 AlloyDB로 안전한 비밀번호 없는 연결을 설정했습니다.

학습한 내용

  1. IAM 데이터베이스 인증 구성: 최소 권한 서비스 계정을 만들고, alloydb.iam_authentication=on를 사용 설정하고, AlloyDB 클러스터에 IAM 사용자를 등록하고, PostgreSQL 테이블 권한을 부여했습니다.
  2. 일반 JDBC 및 HikariCP로 연결됨: 자동 mTLS 1.3 암호화, IAM 토큰 새로고침, 동적 IP 확인을 위해 com.google.cloud.alloydb.SocketFactory로 HikariConfig를 구성했습니다.
  3. 통합 클라우드 네이티브 프레임워크: 커스텀 연결 풀 상용구 없이 Spring Boot, Quarkus(Agroal), Micronaut(HikariCP)를 구성하고 GraalVM 네이티브 이미지에 대한 리플렉션 힌트를 제공합니다.
  4. IAM 문제 해결 숙달: 사용자 이름 형식 지정, ADC 가장, VPC 라우팅 문제를 진단하는 방법을 배웠습니다.

자세히 알아보기