스프링을 이용해서 회사 서비스를 개발하다보면 은근히 비슷하게 작성하는 설정 코드가 존재하기 마련이다. 예를 들어, 여러 서비스에서 LDAP 연동을 위해 같은 스프링 시큐리티 설정을 사용하거나 내부 API 연동을 위해 동일한 설정을 사용할 수 있다. 또한, 데이터 저장소에 따라 사내에서 제안하는 설정 가이드가 존재할 수도 있다.
이런 류의 설정은 여러 곳에서 반복되기 때문에 http://javacan.tistory.com/entry/spring-at-enable-config 글에서 설명한 @Enable을 이용해서 커스텀 설정 편의 기능을 제공하면 편리하다. 스프링부트를 사용한다면, 부트의 자동 설정 기능까지 함께 지원함으로써 설정 편의 기능을 완성할 수 있다.
스프링캠프2016에서 이수홍님이 발표하신 자료(http://www.slideshare.net/sbcoba/2016-deep-dive-into-spring-boot-autoconfiguration-61584342)를 보면 스프링 부트의 자동 설정 기능에 대해 많은 내용을 알 수 있다. 이 자료와 함께 이수홍님 깃헙 프로젝트(https://github.com/sbcoba/spring-camp-2016-spring-boot-autoconfiguration)에서 관련 코드도 참고할 수 있다.
이수홍님 자료를 바탕으로 자동 설정 기능을 추가하는 방법을 정리하면 다음과 같다.
- 자동 설정 기능 제공 모듈: 설정을 제공하는 @Configuration 적용 클래스 구현
- 자동 설정 기능 제공 모듈: spring.factories 파일 작성
- 적용: 자동 설정이 필요한 프로젝트에서 모듈에 대한 의존 추가
1. 자동으로 설정할 내용을 담을 @Configuration 클래스 작성
자동 설정 기능을 위한 코드를 담고 있는 모듈(jar 파일)은 @Configuration 애노테이션을 사용해서 설정 클래스를 작성한다.
@Configuration
@ConditionalOnMissingBean(AuthClient.class)
public class CompAutoConf {
@Bean
public AuthClient authClient() {
return new HttpAuthClient();
}
}
이 설정 클래스는 일반적인 스프링 설정 클래스와 차이가 없다. 보통 자동 설정 기능에서 사용하는 @Configuration 클래스는 조건에 따라 설정 사용 여부를 결정하기 위해 @Conditional 애노테이션을 포함한다. 위 코드의 경우 AuthClient 타입의 빈이 없는 경우에만 CompAutoConf를 설정 클래스로 사용하도록 했다.
2. META-INF/spring.factories 파일에 설정 클래스 지정하기
설정 클래스를 구현했다면, 그 다음으로 할 일은 spring.factories 파일에 설정 클래스를 지정하는 것이다. 클래스패스 위치에(메이븐 같으면 src/main/resources 폴더에) META-INF/spring.factories 파일을 만들고, org.springframework.boot.autoconfigure.EnableAutoConfiguration 프로퍼티의 값으로 자동 설정으로 사용할 클래스를 값으로 준다.
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=mycompany.CompAutoConf
3. 자동 설정 모듈 사용
앞서 언급한 이수홍님 자료를 보면, 스프링 부트는 클래스패스에 위치한 모든 META-INF/spring.factories 파일의 org.springframework.boot.autoconfigure.EnableAutoConfiguration 프로퍼티 값을 읽어와 설정 클래스로 사용한다. 따라서, 스프링 부트 프로젝트에 자동 설정 모듈에 대한 의존을 추가해주기만 하면 된다.
@SpringBootApplication // META-INF/spring.factories에 지정한 설정 클래스 사용
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
// CompAutoConf에 정의한 @Bean 설정에 따라 HttpAuthClient 객체를 빈으로 등록
AuthClient client = ctx.getBean(AuthClient.class);
...
}
}
조건에 따라 설정 적용하기: @Conditional
앞서 CompAutoConf 예를 보면 @ConditiionalOnMissingBean(AuthClient.class)가 있는데, 이는 AuthClient 타입의 빈이 없는 경우에만 해당 설정을 사용한다는 것을 의미한다. 만약 다음과 같이 설정에 AuthClient 타입 빈이 포함되어 있으면 @ConditiionalOnMissingBean(AuthClient.class)에 따라 CompAutoConf 설정 클래스를 사용하지 않는다.
@SpringBootApplication
public class Application {
@Bean
public AuthClient authClient() {
return new ProtobuffAuthClient();
}
...
@Conditional은 스프링 4.0부터 지원하는데, 스프링은 클래스 존재 여부, 프로퍼티 존재 여부, 빈 존재 여부 등 다양한 @Conditional을 제공하고 있다.
설정 순서 지정하기: @AutoConfigureBefore/@AutoConfigureAfter/@AutoConfigureOrder
자동 설정 클래스를 적용하는 순서가 중요할 때가 있다. 예를 들어, 내가 만들 자동 설정이 DataSource를 설정한다고 해보자. 이 자동 설정은 스프링 부트가 제공하는 DataSource 자동 설정 클래스인 DataSourceAutoConfiguration보다 먼저 실행되어야 한다. 그렇지 않을 경우, DataSourceAutoConfiguration이 생성한 DataSource와 내가 만든 자동 설정 기능이 생성한 DataSource가 함께 만들어진다.
보통 이는 원하는 결과가 아니다. 스프링 부트의 DataSourceAutoConfiguration는 DataSource 타입 빈이 존재하지 않는 경우에만 DataSource를 만드므로, 스프링 부트의 DataSourceAutoConfiguration보다 내가 만든 자동 설정 클래스가 먼저 실행되면 여러 DataSource가 만들어지는 문제를 피할 수 있다. 이를 위한 간단한 방법은 내가 만든 자동 설정 클래스에 @AutoConfigureBefore를 붙이는 것이다.
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@ConditionalOnMissingBean(DataSource.class)
public class MyCompDataSourceAutoConfiguration {
...
}
@AutoConfigureAfter를 사용해서 다른 설정 뒤에 자동 설정을 적용하거나 @AutoConfigureOrder를 사용해서 설정의 우선순위를 지정할 수 있다.