스프링부트로 프로젝트를 만들면 DataSource도, JdbcTemplate도, 웹 서버도 자동으로 설정됩니다. 아무 설정도 하지 않았는데 어떻게 이게 가능할까요?

개념 정의

Auto-Configuration 은 스프링부트가 클래스패스에 있는 라이브러리와 이미 정의된 빈을 기반으로 필요한 설정을 자동으로 적용하는 메커니즘입니다. "관례가 설정보다 우선한다(Convention over Configuration)"는 원칙의 핵심 구현체입니다.

왜 필요한가

스프링 프레임워크만 사용하면 이런 설정을 직접 해야 합니다.

JAVA
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        ds.setUsername("root");
        ds.setPassword("password");
        ds.setMaximumPoolSize(10);
        return ds;
    }

이어서 @Bean을 적용한 나머지 구현부입니다.

JAVA
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

스프링부트의 Auto-Configuration이 있으면 application.yml에 URL만 적으면 됩니다. 나머지는 자동입니다.

YAML
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password

내부 동작

전체 흐름

Auto-Configuration이 빈을 등록하기까지의 과정은 다음과 같습니다.

  1. @SpringBootApplication 안에 @EnableAutoConfiguration이 포함되어 있습니다.
  2. AutoConfigurationImportSelectorAutoConfiguration.imports 파일에서 자동 설정 클래스 목록(약 150개)을 읽습니다.
  3. 각 클래스의 @Conditional 조건을 평가합니다 — 클래스패스에 해당 라이브러리가 있는지, 사용자가 이미 빈을 등록했는지 등.
  4. 조건을 통과한 클래스만 빈으로 등록됩니다.

이 과정 덕분에 spring-boot-starter-web을 의존성에 추가하면 Tomcat, DispatcherServlet, Jackson이 자동으로 설정됩니다.

@Conditional 시리즈

Auto-Configuration의 핵심은 조건부 로딩입니다.

JAVA
@ConditionalOnClass(DataSource.class)
// DataSource 클래스가 클래스패스에 있을 때 (라이브러리가 의존성에 포함)

@ConditionalOnMissingClass("com.example.SomeClass")
// 특정 클래스가 클래스패스에 없을 때

@ConditionalOnBean(DataSource.class)
// DataSource 타입의 빈이 이미 등록되어 있을 때

@ConditionalOnMissingBean(DataSource.class)
// DataSource 타입의 빈이 아직 없을 때

@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
// 특정 프로퍼티가 특정 값일 때

@ConditionalOnWebApplication(type = Type.SERVLET)
// 서블릿 웹 애플리케이션일 때

@ConditionalOnResource(resources = "classpath:schema.sql")
// 특정 리소스 파일이 존재할 때

실제 Auto-Configuration 클래스 예시

JAVA
// DataSourceAutoConfiguration (개념적 구조)
@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(DataSource.class) // 사용자가 직접 등록하면 적용 안 됨
    @ConditionalOnProperty(name = "spring.datasource.url")
    static class PooledDataSourceConfiguration {

이어서 @Bean을 적용한 나머지 구현부입니다.

JAVA
        @Bean
        @ConditionalOnMissingBean
        DataSource dataSource(DataSourceProperties properties) {
            return properties.initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
        }
    }
}

핵심 패턴: @ConditionalOnMissingBean → 사용자가 직접 빈을 등록하면 Auto-Configuration은 물러남

코드 예제

Auto-Configuration 디버깅

BASH
# 방법 1: 커맨드라인
java -jar app.jar --debug

# 방법 2: application.yml
debug: true

출력 결과:

PLAINTEXT
============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------
   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource',
        'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'

Negative matches:
-----------------
   RabbitAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class
           'com.rabbitmq.client.Channel'

자동 설정 오버라이드 (사용자 설정 우선)

JAVA
@Configuration
public class MyDataSourceConfig {

    @Bean
    public DataSource dataSource() {
        // 사용자가 직접 DataSource를 등록하면
        // DataSourceAutoConfiguration의 DataSource 빈은 생성되지 않음
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://custom-server:3306/mydb");
        ds.setMaximumPoolSize(50); // 커스텀 설정
        return ds;
    }
}

특정 Auto-Configuration 비활성화

JAVA
// 방법 1: 어노테이션으로 제외
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    SecurityAutoConfiguration.class
})
public class MyApplication { ... }

// 방법 2: 프로퍼티로 제외
// application.yml
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

커스텀 Auto-Configuration 만들기

JAVA
// 1. 자동 설정 클래스 작성
@AutoConfiguration
@ConditionalOnClass(MyLibrary.class)
@EnableConfigurationProperties(MyLibraryProperties.class)
public class MyLibraryAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyLibraryClient myLibraryClient(MyLibraryProperties properties) {
        return new MyLibraryClient(properties.getApiKey(), properties.getTimeout());
    }
}

이어서 나머지 구현 부분입니다.

JAVA
// 2. 프로퍼티 클래스
@ConfigurationProperties(prefix = "mylib")
public class MyLibraryProperties {
    private String apiKey;
    private Duration timeout = Duration.ofSeconds(30);
    // getter, setter
}

// 3. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.MyLibraryAutoConfiguration
YAML
# 사용자가 설정만 넣으면 자동으로 MyLibraryClient 빈이 생성됨
mylib:
  api-key: my-api-key
  timeout: 10s

Auto-Configuration 순서 제어

JAVA
@AutoConfiguration(
    after = DataSourceAutoConfiguration.class,    // DataSource 이후에 실행
    before = JpaRepositoriesAutoConfiguration.class // JPA Repository 이전에 실행
)
public class MyAutoConfiguration { ... }

Actuator로 Auto-Configuration 확인

YAML
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: conditions

GET /actuator/conditions를 호출하면 어떤 Auto-Configuration이 적용되었고 왜 적용(또는 미적용)되었는지 JSON으로 확인할 수 있습니다.

주의할 점

1. 의존성만 추가했는데 의도치 않은 자동 설정이 활성화된다

spring-boot-starter-data-jpa를 추가하면 DataSource, EntityManagerFactory, TransactionManager가 자동으로 구성됩니다. DB 접속 정보 없이 이 의존성을 추가하면 Failed to configure a DataSource 에러로 애플리케이션이 시작되지 않습니다. 아직 DB를 사용하지 않는데 의존성만 미리 넣어두면 이런 문제가 발생하므로, @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)로 명시적으로 제외해야 합니다.

2. 사용자 빈과 자동 설정 빈의 우선순위를 모르면 설정이 무시된다

@ConditionalOnMissingBean 덕분에 사용자가 직접 등록한 빈이 우선하지만, 빈 타입이 정확히 일치해야 합니다. 예를 들어 자동 설정이 ObjectMapper 타입을 체크하는데 사용자가 서브클래스를 등록하면 자동 설정이 추가로 적용되어 빈이 2개가 됩니다. --debug 플래그로 CONDITIONS EVALUATION REPORT를 확인하여 어떤 자동 설정이 적용되었는지 반드시 검증해야 합니다.

3. spring.factories에서 AutoConfiguration.imports로의 마이그레이션을 놓치면 자동 설정이 동작하지 않는다

Spring Boot 3.0부터 자동 설정 등록 방식이 META-INF/spring.factories에서 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports로 변경되었습니다. Spring Boot 2에서 3으로 업그레이드할 때 커스텀 스타터의 등록 파일을 변경하지 않으면 자동 설정이 무시되어, 빈이 등록되지 않는데 에러 메시지도 없어 원인 파악이 어렵습니다.

Boot 4.0에서의 Auto-Configuration 변경사항

Boot 4.0(Spring Framework 7)에서는 Auto-Configuration에 몇 가지 변화가 있습니다.

@AutoConfiguration 필수화

Boot 3.x에서는 @Configuration + AutoConfiguration.imports 등록만으로 자동 설정이 동작했지만, Boot 4.0부터는 ** 반드시 @AutoConfiguration을 사용 **해야 합니다. @Configuration만으로는 자동 설정으로 인식되지 않습니다.

JAVA
// Boot 3.x — @Configuration도 동작했음
@Configuration
@ConditionalOnClass(MyLibrary.class)
public class MyAutoConfig { ... }

// Boot 4.0 — @AutoConfiguration 필수
@AutoConfiguration
@ConditionalOnClass(MyLibrary.class)
public class MyAutoConfig { ... }

조건부 어노테이션 개선

Boot 4.0에서 @ConditionalOnThreading이 추가되어 Virtual Threads 활성화 여부에 따라 자동 설정을 분기할 수 있습니다.

JAVA
@AutoConfiguration
@ConditionalOnThreading(Threading.VIRTUAL)
public class VirtualThreadTaskExecutorAutoConfiguration {
    // Virtual Threads가 활성화된 경우에만 적용되는 설정
}

Boot 4.0으로 마이그레이션할 때, 커스텀 Auto-Configuration 클래스의 어노테이션을 @AutoConfiguration으로 변경하는 것을 잊지 마세요. 자세한 내용은 Spring Boot 4.0 마이그레이션 가이드를 참고해 주세요.

정리

항목설명
동작 원리클래스패스 + @Conditional 조건으로 빈 자동 등록
사용자 우선@ConditionalOnMissingBean — 사용자 빈이 항상 우선
디버깅--debug 플래그로 CONDITIONS EVALUATION REPORT 확인
비활성화@SpringBootApplication(exclude = ...)
등록 파일Spring Boot 3+: AutoConfiguration.imports
Boot 4.0@AutoConfiguration 필수, @ConditionalOnThreading 추가
댓글 로딩 중...