관례 중심으로 조립되는 영속성 계층
DataSource → EntityManagerFactory → JpaTransactionManager → JPA 저장소 프록시
Spring Boot가 JPA 공급자를 중심으로 익숙하고 통합된 영속성 경로를 구성할 수 있습니다.
영속성 컨텍스트의 상태, 변경 감지, 트랜잭션 가로채기가 프로그래밍 방식에 포함됩니다.
자동 설정 · 조건 · 소유권
JDBC 활성화는 Spring의 DataSource에 Exposed를 연결하고, 정해진 이름의 트랜잭션 관리자를 조건부로 제공합니다. R2DBC 활성화는 의도적으로 더 얇습니다. 풀과 R2dbcDatabase, 트랜잭션 범위, 종료 절차는 여전히 애플리케이션이 맡습니다.
소유권부터 확인
DataSource → EntityManagerFactory → JpaTransactionManager → JPA 저장소 프록시
Spring Boot가 JPA 공급자를 중심으로 익숙하고 통합된 영속성 경로를 구성할 수 있습니다.
영속성 컨텍스트의 상태, 변경 감지, 트랜잭션 가로채기가 프로그래밍 방식에 포함됩니다.
JDBC: DataSource → Database.connect → 이름 있는 관리자 · R2DBC: 매핑 + 저장소 팩터리
SQL과 데이터베이스 객체, Coroutine 트랜잭션 경계를 애플리케이션 설계에 분명히 드러낼 수 있습니다.
R2DBC 기반 객체와 여러 호출의 원자성은 추론되는 기본값이 아니라 애플리케이션의 책임입니다.
원본 아키텍처
두 모듈은 저장소 탐색과 매핑 개념을 공유하지만 같은 기반 객체를 소유하지는 않습니다. 각 원본 다이어그램을 열면 전체 크기로 확인할 수 있습니다.
조건 실험실
각 시나리오는 Spring이 평가하는 항목과 애플리케이션이 계속 제공해야 하는 항목을 구분합니다. 선택한 카드는 조건·결과 원장과 시퀀스 다이어그램을 함께 바꿉니다.
EntityClass 있음자동 설정 클래스가 활성화 대상이 됩니다.
DataSource 빈 있음이름 있는 트랜잭션 관리자 메서드를 실행할 수 있습니다.
springTransactionManager 이름의 빈 없음JDBC 자동 설정이 후퇴하지 않습니다.
ExposedMappingContext 생성Spring Data가 Exposed 엔티티를 해석할 수 있습니다.
Database.connect(DataSource) 호출Exposed JDBC가 애플리케이션 DataSource에 연결됩니다.
저장소 프록시는 기본으로 springTransactionManager를 사용합니다.
EntityClass 있음R2DBC 매핑 자동 설정이 활성화 대상이 됩니다.
얇은 자동 설정이 컨텍스트 하나를 만들 수 있습니다.
풀과 R2dbcDatabase는 이 자동 설정의 범위 밖입니다.
ExposedMappingContext 생성엔티티 메타데이터를 해석할 수 있습니다.
R2dbcDatabase는 애플리케이션 소유생성과 종료 훅을 명시적으로 유지합니다.
Spring 리액티브 관리자를 추가하지 않습니다.
두 저장소 등록기가 모두 실행될 수 있습니다.
두 번째 매핑 설정이 첫 번째 설정 결과를 확인합니다.
애플리케이션이 모호한 인터페이스 탐색을 막습니다.
JDBC 쪽 빈이 이미 있으면 R2DBC 설정이 후퇴합니다.
DataSource와 빈 이름 조건은 그대로 적용됩니다.
매핑 컨텍스트를 공유해도 트랜잭션을 공유하는 것은 아닙니다.
DataSource 빈 있음JDBC 연동은 계속 활성화 대상입니다.
애플리케이션이 빈 이름을 정합니다.
transactionManagerRef 일치JDBC 확장이 정확한 이름을 팩터리 빈 정의에 복사합니다.
저장소 연결은 설정한 참조를 따릅니다.
생성된 저장소 프록시가 애플리케이션 빈을 사용합니다.
오타는 자동 대체 신호가 아니라 시작·연결 결함입니다.
EntityClass 있음R2DBC 자동 설정을 평가합니다.
@ConditionalOnMissingBean 조건이 일치하지 않습니다.
중복 매핑 컨텍스트를 만들지 않습니다.
해당 컨텍스트의 엔티티 해석 방식이 기준이 됩니다.
저장소 인터페이스는 탐색될 수 있습니다.
연동 모듈은 풀을 만들지 않습니다.
R2dbcDatabase 없음저장소 실행이 사용할 데이터베이스가 없습니다.
메타데이터 성공이 런타임 준비를 증명하지는 않습니다.
첫 실제 호출에서 애플리케이션 소유 객체의 부재가 드러납니다.
관찰하거나 닫을 리소스 생명주기가 없습니다.
EntityClass 없음클래스 수준 조건이 두 자동 설정을 모두 거부합니다.
DataSource로 활성화할 수 없음클래스 조건이 실패하므로 빈 조건까지 도달하지 않습니다.
자동 설정 클래스가 활성화되지 않습니다.
관리자 메서드 조건을 평가하지 않습니다.
빈 이름보다 Classpath를 먼저 확인합니다.
호출 → 평가 → 결과
DataSource가 있고 기본 관리자 이름을 사용 중인 빈이 없으므로 JDBC 연동이 전체 빈 구성을 추가할 수 있습니다.
Spring Boot가 추가하고 애플리케이션이 DataSource 소유저장소 탐색과 매핑은 활성화되지만 데이터베이스 기반 객체와 트랜잭션 조합은 명시적인 애플리케이션 코드로 남습니다.
애플리케이션이 풀·데이터베이스·Coroutine 범위 소유두 모듈은 하나의 매핑 컨텍스트로 공존할 수 있지만 데이터베이스 객체와 트랜잭션 경계는 서로 분리됩니다.
메타데이터 공유, 실행 소유권 분리저장소 설정 확장은 관리자 참조를 정확한 빈 이름으로 취급하고 JDBC 팩터리 빈에 전달합니다.
애플리케이션이 관리자 선택 소유애플리케이션이 ExposedMappingContext를 이미 제공하면 R2DBC 매핑 자동 설정은 의도적으로 후퇴합니다.
애플리케이션이 매핑 사용자 정의 소유저장소 탐색과 매핑 컨텍스트만으로는 부족합니다. 데이터베이스 작업 전에 애플리케이션이 풀과 R2dbcDatabase를 만들어야 합니다.
애플리케이션이 기반 객체 경계를 충족하지 못함가장 바깥의 Classpath 조건이 실패하므로 매핑 빈과 JDBC 관리자 생성 조건을 검토하지 않습니다.
의존성 구성이 활성화 여부 소유소유권
| 관심사 | JDBC 경로 | R2DBC 경로 | 운영 결과 |
|---|---|---|---|
| 저장소 탐색 | @EnableExposedJdbcRepositories | @EnableExposedR2dbcRepositories | 두 경로 모두 애플리케이션이 패키지 경계를 정합니다. |
| 매핑 컨텍스트 | EntityClass가 있으면 생성 | 기존 빈이 없을 때만 생성 | R2DBC 경로에서는 사용자 정의 ExposedMappingContext가 우선합니다. |
| 데이터베이스 객체 | 관리자 생성 시 Database.connect(DataSource) | 애플리케이션이 R2dbcDatabase 생성 | R2DBC 시작과 종료 절차는 명시적으로 남습니다. |
| 트랜잭션 관리자 | springTransactionManager라는 이름이 없을 때만 생성 | ReactiveTransactionManager를 제공하지 않음 | JDBC 프록시 연결은 이름에 민감하고, R2DBC는 Exposed Coroutine 트랜잭션을 사용합니다. |
| 여러 호출의 원자성 | Spring 서비스 트랜잭션으로 묶을 수 있음 | 하나의 suspendTransaction으로 묶음 | 저장소 탐색은 비즈니스 트랜잭션 경계를 정하지 않습니다. |
구성 레시피
| 의도 | 애플리케이션이 제공 | 활성화·연결 | 검증 |
|---|---|---|---|
| 기본 JDBC | DataSource | @EnableExposedJdbcRepositories | springTransactionManager 빈과 두 저장소 호출의 롤백을 확인합니다. |
| 사용자 정의 JDBC 관리자 | 이름 있는 PlatformTransactionManager | transactionManagerRef = "ordersTransactionManager" | 생성된 저장소 정의가 정확히 그 이름을 가리키는지 확인합니다. |
| Coroutine R2DBC | ConnectionPool, R2dbcDatabase, 생명주기 | @EnableExposedR2dbcRepositories | 관련 작업을 하나의 명시적인 suspendTransaction이 소유하는지 확인합니다. |
| JDBC + R2DBC | 두 기반 객체 집합 | 저장소 패키지 분리 | 매핑 컨텍스트가 하나이고 저장소 인터페이스가 모호하지 않은지 확인합니다. |
실패 진단
| 증상 | 의심할 경계 | 확인할 항목 | 해결 |
|---|---|---|---|
| Exposed 빈이 없음 | EntityClass 부재 | Classpath와 조건 평가 보고서 | 해당 Exposed DAO·core 의존성을 추가하거나 연동을 제거합니다. |
| JDBC 저장소가 관리자를 찾지 못함 | 빈 이름 불일치 | transactionManagerRef와 빈 이름 | 참조한 이름과 정확히 같은 빈을 제공하거나 기본 이름을 사용합니다. |
| R2DBC 저장소는 시작하지만 DB 접근 실패 | 기반 객체 미생성 | 풀과 R2dbcDatabase 초기화 | 애플리케이션이 기반 객체를 만들고 등록하며 종료합니다. |
| 두 호출이 따로 커밋됨 | 저장소 메서드마다 경계를 생성 | 서비스 호출 구조 | 관련 작업을 하나의 Spring 트랜잭션이나 suspendTransaction으로 옮깁니다. |
| 예상과 다른 매핑 동작 | 사용자 정의 컨텍스트로 자동 설정 후퇴 | ExposedMappingContext 빈 | 의도한 컨텍스트 하나만 두고 엔티티 해석을 테스트합니다. |
선택 기준
| 판단 | JDBC 연동이 적합한 경우 | R2DBC 연동이 적합한 경우 | 추정하면 안 되는 것 |
|---|---|---|---|
| 실행 방식 | 요청 경로가 블로킹이고 Spring 트랜잭션 가로채기가 필요합니다. | 전체 경로가 Coroutine·논블로킹이고 명시적인 suspend 실행이 필요합니다. | 웹 프레임워크 이름만으로 드라이버 방식을 결정합니다. |
| 기반 객체 | Spring 관리 DataSource가 이미 안정적인 경계입니다. | 애플리케이션이 풀, R2dbcDatabase, 준비 상태, 종료를 관리할 수 있습니다. | R2DBC 자동 설정이 이 객체들을 만듭니다. |
| 트랜잭션 조합 | 여러 저장소를 서비스 트랜잭션에 참여시켜야 합니다. | 관련 작업을 하나의 명시적인 Coroutine 트랜잭션으로 표현할 수 있습니다. | 활성화 애노테이션이 비즈니스 원자성을 만듭니다. |
| 사용자 정의 | 이름 있는 관리자를 명시적으로 연결하고 테스트할 수 있습니다. | 명시적인 팩터리와 데이터베이스 객체를 애플리케이션 코드로 관리할 수 있습니다. | 선언만 되고 실제로 쓰이지 않는 애노테이션 속성이 런타임을 바꿉니다. |
검증 근거
| 주장 | 근거 파일 |
|---|---|
| JDBC 활성화는 EntityClass 조건을 확인하고 매핑 빈과 이름 조건이 있는 관리자를 제공합니다. | |
| JDBC 관리자 경로는 Exposed를 애플리케이션 DataSource에 연결합니다. | |
| JDBC 저장소 탐색은 등록기와 JDBC 자동 설정을 가져옵니다. | |
| JDBC 확장은 transactionManagerRef를 생성된 저장소 팩터리 빈에 전달합니다. | |
| JDBC 저장소는 Spring Data 트랜잭션 팩터리 지원을 통해 참여합니다. | |
| R2DBC 자동 설정은 JDBC 단계 이후 누락된 매핑 컨텍스트만 제공합니다. | |
| R2DBC 저장소 탐색은 활성화 애노테이션과 등록기를 통해 명시적으로 시작합니다. | |
| R2DBC 확장은 suspend와 Flow 저장소 메서드를 허용하지만 transactionManagerRef를 사용하지 않습니다. | |
| R2DBC 저장소는 Spring 트랜잭션 가로채기 대신 일반 팩터리 지원과 Exposed suspend 트랜잭션을 사용합니다. | |
| 한·영 매뉴얼은 JDBC·R2DBC 기반 객체와 트랜잭션 소유권 경계를 기록합니다. |