
오늘의 목표
TransactionManager, EntityManagerFactory, Datasource가 언제 자동구성되는지 이해하기
Spring 과 Spring Boot의 여러 차이점 중 자동구성이 존재합니다.
이 글에서는 Data JPA를 사용하는 환경에서는 어떠한 과정을 거쳐 자동구성이 설정되는지 알아보고자 합니다.
Spring은 스프링 프레임워크의 핵심 모듈을 모아서 만든 프레임워크입니다. Spring에서는 개발자가 직접 설정 파일을 작성하여 스프링 컨테이너를 구성하고, 필요한 빈 객체를 등록하고, 빈 객체 간의 의존성을 설정해야 합니다. Spring은 특정한 구성을 위해 추가적인 라이브러리와 설정이 필요합니다.
반면, Spring Boot는 스프링 프레임워크를 보다 쉽게 사용할 수 있도록 만든 프레임워크입니다. Spring Boot에서는 개발자가 설정 파일을 작성할 필요 없이, 프로젝트의 설정과 라이브러리 의존성을 자동으로 처리해주는 기능을 제공합니다.
우선 프로젝트 설정은 아래와 같습니다.
Spring Boot 3.2.4dependencies
application.properties
spring-boot-starter를 디펜던시로 설정해놓으면 아래와 같이 자동으로 autoconfigure 기능이 추가되는 것을 알 수 있습니다.

spring-boot-starter 를 통해 추가된 autoconfigure 안에서
DataSourceAutoConfiguration를 확인할 수 있는데, DataSourceAutoConfiguration는 Datasource를 자동으로 등록해주는 클래스입니다.

만약
org.springframework.boot:spring-boot-starter-data-jpa혹은org.springframework.boot:spring-boot-starter-jdbc를 의존성에 추가하지 않았다면 아래와 같이org.springframework.jdbc를 인식할 수 없어DataSource를 자동으로 등록하는 자동구성을 이용할 수 없습니다.
이 클래스는 애플리케이션에서 데이터베이스 연결을 위한 DataSource 빈을 자동으로 생성하고 구성하는 역할을 담당합니다.

여기서는 크게 3가지 설정들을 살펴보겠습니다.
EnableConfigurationProperties({DataSourceProperties.class}) : 외부 설정 파일의 DataSource 관련 프로퍼티를 바인딩합니다.
EmbeddedDatabaseCondition : 이 조건은 spring.datasource.url 속성이 존재하지 않거나 비어있을 경우, 내장형 데이터베이스 구성을 활성화하기 위한 조건입니다.
@Import를 통해 DataSourceAutoConfiguration는 데이터베이스 연결을 위한 DataSource 빈을 자동으로 생성하기 위한 여러 구현체의 구성을 가져옵니다.

스프링 부트에서는 데이터베이스 종류와 상관없이 기본
DataSource구현체로HikariCP를 사용하기에 아래Hikari DataSource가 등록됩니다.
현재 상황은 application.yml(application.properties 등)에서 하나의 DataSource만 설정했을 때의 예시이고, 이 경우 Spring Boot의 DataSourceAutoConfiguration이 조건에 맞춰 자동으로 DataSource 빈(예: HikariDataSource 등)을 생성하고 구성합니다.
만약에
DataSource를 2개 이상 설정하게 된다면? -> 해당 블로그 글 참고
다시 본론으로 돌아와서 DataSourceAutoConfiguration을 통해 자동으로 Datasource를 등록해주는 것을 알 수 있었습니다.
이와 같이 Spring Data JPA를 사용하면 HibernateJpaAutoConfiguration와 JpaRepositoriesAutoConfiguration가 함께 작동하여 TransactionManager, EntityManagerFactory 등 JPA를 사용하기 위한 기본 빈들을 자동으로 생성·등록합니다.
- HibernateJpaAutoConfiguration: 이 클래스는 EntityManagerFactory, PlatformTransactionManager 등 JPA 환경에 필요한 핵심 빈들을 자동으로 등록합니다.
- JpaRepositoriesAutoConfiguration: 이 클래스는 Repository 인터페이스를 자동으로 스캔하고 등록하여, 별도의 구현체 없이도 CRUD 및 커스텀 메서드를 사용할 수 있도록 도와줍니다.
이렇게 두 자동 구성 클래스가 함께 동작함으로써, 개발자는 복잡한 설정 없이 Spring Data JPA 기능을 손쉽게 사용할 수 있습니다.
우선 JpaRepositoriesAutoConfiguration 를 간략히 살펴보자면,
이 클래스는 Spring Boot에서 JPA 기반의 Repository를 자동 구성하기 위한 auto-configuration 클래스로 아래와 같은 애노테이션들이 설정되어 있습니다.

Hibernate JPA와 Task Execution 관련 자동 구성들이 먼저 완료된 후에 이 설정이 적용되도록 순서를 보장합니다.DataSource 빈이 존재해야 이 auto-configuration이 활성화됩니다. 즉, 데이터베이스 연결 설정이 되어 있어야 합니다.JpaRepository 클래스가 있어야 JPA 리포지토리 기능이 활성화됩니다.
Data JPA가 아닌JPA를 사용하게 된다면HibernateJpaAutoConfiguration클래스만을 사용하고,JpaRepositoriesAutoConfiguration클래스는 사용하지 않습니다.
Data JPA가 아닌 JPA를 사용하려면 이와 같이 의존성을 추가해야 하며 기존에 Data JPA가 제공하는 기능 5가지 중 JpaRepository 를 사용하지 못합니다.
jakarta.persistence-api 덕분에 JPA 어노테이션과 인터페이스 사용 가능hibernate-core가 JPA 구현체 역할spring-boot-starter에 포함된 spring-tx 덕분에 트랜잭션 처리 가능spring-boot-starter-jdbc가 DataSource, HikariCP 설정 지원해당 클래스에서는 EntityManagerFactory, PlatformTransactionManager 등 JPA 환경에 필요한 핵심 빈들을 자동으로 등록하는 과정이 이루어집니다.
해당 클래스의 애노테이션들을 확인하게 되면 조금은 익숙한(?) 클래스명이 보입니다.
DataSourceAutoConfiguration을after에 지정하는 코드가 보이는데, 해당DataSourceAutoConfiguration는 초반에 언급했던DataSource를 자동구성하는 클래스입니다.

// code
package org.springframework.boot.autoconfigure.orm.jpa;
import jakarta.persistence.EntityManager;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@AutoConfiguration(
after = {DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class},
before = {TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class}
)
@ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class})
@EnableConfigurationProperties({JpaProperties.class})
@Import({HibernateJpaConfiguration.class})
public class HibernateJpaAutoConfiguration {
public HibernateJpaAutoConfiguration() {
}
}
해당 애노테이션에 구성된 설정 값 대해서도 간략히 알고 넘어가겠습니다.
@AutoConfiguration(
after = {DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class},
before = {TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
해당 애노테이션은 HibernateJpaAutoConfiguration가 실행되는 시점을 제어하는데, 이는 JpaTransactionManager 구성과 밀접하게 관련됩니다. 조금 더 자세히 살펴보겠습니다.
after 속성: DataSourceAutoConfiguration와 TransactionManagerCustomizationAutoConfiguration가 먼저 실행되어, 데이터소스와 트랜잭션 매니저에 대한 기본 설정 및 추가 커스터마이징이 완료된 상태여야 합니다.
여기서 주의해야 할 부분은 TransactionManagerCustomizationAutoConfiguration는 JpaTransactionManager를 직접 생성하는 곳이 아닙니다.
JpaTransactionManager는 주로 JpaBaseConfiguration(그리고 이를 상속받은 HibernateJpaConfiguration)에서 EntityManagerFactory를 기반으로 생성됩니다.
TransactionManagerCustomizationAutoConfiguration는 이미 생성된 TransactionManager에 대해 추가적인 커스터마이징을 적용할 수 있도록 돕는 역할을 합니다. JpaTransactionManager의 기본 생성은 HibernateJpaConfiguration에서 진행되고(HibernateJpaAutoConfiguration X), 그 이후에 TransactionManagerCustomizationAutoConfiguration가 존재하면, 이를 이용해 추가적인 설정이나 수정을 적용합니다.
방금 다룬 부분은 밑에서 한번 더 다루니 지금은 그냥
"JpaBaseConfiguration가 JpaTransactionManager를 생성하는구나!"정도로만 이해하셔도 괜찮습니다.
TransactionAutoConfiguration와 DataSourceTransactionManagerAutoConfiguration보다 먼저 실행되어, JPA 관련 빈들(예: EntityManagerFactory, JpaTransactionManager 등)이 등록되어야 합니다.이러한 순서 덕분에 HibernateJpaAutoConfiguration는 필요한 빈들이 이미 구성된 상태에서 실행되며, 그 후에 전체 트랜잭션 관련 자동 구성이 이루어지도록 보장됩니다.
@ConditionalOnClass({
LocalContainerEntityManagerFactoryBean.class,
EntityManager.class,
SessionImplementor.class})
LocalContainerEntityManagerFactoryBean: EntityManagerFactory를 생성하는 Spring ORM의 핵심 클래스EntityManager: JPA 표준 인터페이스SessionImplementor: Hibernate의 내부 세션 인터페이스즉, JPA와 Hibernate 관련 라이브러리가 존재해야 이 자동 구성이 적용됩니다.
@EnableConfigurationProperties({JpaProperties.class})
JpaProperties 클래스에 정의된 외부 설정(예: application.properties 또는 application.yml 파일의 spring.jpa.* 관련 설정)이 자동으로 바인딩되어, 개발자가 설정한 값을 기반으로 Hibernate 관련 빈들이 구성됩니다.
@Import({HibernateJpaConfiguration.class})
HibernateJpaConfiguration 클래스를 가져옵니다.EntityManagerFactory, EntityManager 및 기타 Hibernate 관련 빈들을 등록하는 작업이 이루어집니다.스프링에서 자바 설정을 추가할 때 사용하는 Import를 타고 들어가게 된다면 실제 Hibernate JPA 설정을 담당하는 HibernateJpaConfiguration 클래스가 나오게 되는데 여기서 주의해야 할 부분은 @ConditionalOnSingleCandidate(DataSource.class) 입니다.

이 클래스는 @ConditionalOnSingleCandidate(DataSource.class) 조건이 붙어 있어서, DataSource 빈이 단 하나일 때만 자동 구성됩니다.
즉, 여러 개의 DataSource가 등록되어 있으면 이 조건이 충족되지 않아 HibernateJpaConfiguration이 자동으로 동작하지 않습니다.
수동으로 등록하는 방법 외에도 여러 개의
DataSource가 등록되어 있을 때, 그 중 하나를@Primary로 지정하면 해당DataSource가 단일 후보로 간주됩니다. 그러면@ConditionalOnSingleCandidate(DataSource.class)조건을 충족하여HibernateJpaConfiguration가 자동으로 동작하게 됩니다.
해당 클래스(HibernateJpaConfiguration)는 JpaBaseConfiguration 를 상속받고 있습니다. JpaBaseConfiguration 에서 중점적으로 볼 부분은 크게 두가지입니다.
트랜잭션 매니저 빈 등록
transactionManager 메서드는 JpaTransactionManager를 생성하고, 추가적인 커스터마이징을 적용합니다. 만약 TransactionManager 빈이 없을 경우에만 생성하도록 조건부(@ConditionalOnMissingBean)로 설정되어 있습니다.
EntityManagerFactory 빈 등록
entityManagerFactory 메서드에서는 entityManagerFactoryBuilder()를 이용해 LocalContainerEntityManagerFactoryBean을 생성합니다.JPA의 EntityManagerFactory를 구성하며, 여기서 데이터 소스, 관리 대상 클래스, 추가적인 JPA 속성들이 적용됩니다.
LocalContainerEntityManagerFactoryBean는Spring Framework에서 제공하는FactoryBean으로,JPA의EntityManagerFactory를 생성하고 구성하는 데 사용됩니다.
이 클래스를 사용하면 스프링 컨테이너와 통합된 방식으로JPA설정을 손쉽게 관리할 수 있으며,데이터 소스,JPA 벤더 어댑터,엔티티 패키지 스캔,추가 JPA 속성등을 한 곳에서 설정할 수 있습니다.
HibernateJpaConfiguration와 JpaBaseConfiguration을 통해 우리가 흔히 접하는 JpaTransactionManager와 EntityManagerFactory가 자동으로 생성된다는 것을 알 수 있었습니다. 물론 이렇게 내부 흐름을 자세히 알지 않더라도 Spring Data JPA를 사용하는 데 큰 문제는 없지만, 편리하게 사용하는 Spring Boot 내부에는 수많은 자동 구성 메커니즘과 정교한 설계가 숨어 있다는 것을 알 수 있었습니다.
개발자가 정의한 Component들이 먼저 스캔 돼서 Bean으로 등록된 후에, AutoConfiguration의 Bean들이 등록됩니다
BeanDefinition이 등록되는 순서는 개발자가 정의한Component들이 맞지만, 의존성과BeanFactory내부 순서에 따라 동시에 생성이 진행됩니다.
| 순서 | 설명 | 구성 요소 |
|---|---|---|
| 1️⃣ | @ComponentScan + @Configuration 처리 시작 | 개발자 정의 컴포넌트 클래스들이 스캔되어 BeanDefinition으로 등록됨 (이 시점엔 인스턴스 아님) |
| 2️⃣ | @EnableAutoConfiguration 처리 | spring.factories에 정의된 @AutoConfiguration 클래스들도 BeanDefinition으로 등록됨 |
| ✅ | 여기까지는 "정의 등록"만 완료된 시점 (BeanDefinition Registry 단계) | → 즉, 개발자 정의 빈과 AutoConfig 빈은 정의만 먼저 됨 |
| 3️⃣ | Bean 인스턴스 생성 시작 | 이 시점부터 @Component, @Service, AutoConfig 클래스 순서에 따라 실제 객체로 생성됨 (빈 생성 및 주입) |
| ⚠️ | Bean 생성 순서는 의존성에 따라 달라짐 | 우선순위 지정이 없으면 의존성 그래프 기준으로 생성됨 |
@Component 등) → @AutoConfigurationSpring에서는 같은 타입의 BeanDefinition이 여러 개 등록되어 있을 경우, 기본적으로 사용자 정의 빈이 우선됩니다. 이건 자동 구성(AutoConfiguration) 설계 원칙 중 하나입니다.
@ConditionalOnMissingBean : 대부분의 자동 구성 클래스는 이 조건이 붙어 있음. 즉, 동일 타입의 빈이 이미 존재하면 자동 구성은 생략됨BeanDefinition 우선순위 : Spring은 BeanDefinition을 만들 때, 사용자가 등록한 Bean을 자동 구성보다 우선시함명시적 정의 > 자동 등록 : 개발자가 명시적으로 등록한 빈이 우선 → Spring Boot의 핵심 철학인 "자동이지만 필요하면 덮어써라"를 반영한 설계DataSource 빈을 생성합니다. (데이터베이스 연결, 커넥션 풀 등)DataSource와 트랜잭션 매니저 커스터마이징)가 완료된 후 실행됩니다.EntityManagerFactory를 생성하며, 이 빈은 @Primary로 등록됩니다.JpaTransactionManager)를 생성 및 등록합니다.