파란하늘의 지식창고
article thumbnail
반응형

Spring Boot 기반의 프로젝트를 개발하면서 사용하는 기능들을 정리

@ConfigurationProperties 사용하기

ConfigurationProperties는 properties 파일에 설정한 property 값을 java object에 매핑하여 java 코드에서 해당 값을 사용하기 편하게 해 준다.

아래와 같은 properties class를 만든다고 가정한다.

@Data
@ConfigurationProperties(prefix = "test.sample")
public class TestSampleProperties {
	private String testStr;
	private int testInt;
}

해당 Properties 설정과 매칭 되는 property 값을 properties 파일에 설정한다.

주의할 점은 java class의 변수명은 camel case이지만 properties는 - (하이픈)을 사용한 snake case를 사용한다.

test.sample.test-str=테스트 문자열 값
test.sample.test-int=1234

해당 properties 파일의 값을 java TestSampleProperties 객체에 바인딩해주는 설정을 아래와 같이 한다.

@Configuration
@EnableConfigurationProperties(TestSampleProperties.clsas)
@PropertySource("classpath:TestSample.properties")
public class TestSampleConfiguration {
}

@EnableConfigurationProperties 선언은 해당 Properties class로 object를 생성하겠다는 선언이다.

@PropertySource는 properties 파일을 호출하기 위한 선언이다.

두 설정 서로 연관 관계가 없으며  1:1로 매칭 될 필요가 없다.

propertySource를 여러 개 선언하여 여러 파일에 TestSampleProperties에 prefix로 선언된 "test.sample"로 시작한 변수 값이 분산되어 있어도 알아서 매핑한다.

해당 설정된 값은 간편하게 아래처럼 호출하여 사용할 수 있다.

@Service
public class TestSampleService {

	@Autowired
	private TestSampleProperties testSampleProperties;
    
	public void test() {
		String testStr = testSampleProperties.getTestStr();
		// 해당 값을 사용
	}

}

@Conditional* 사용하기

spring boot는 spring-boot-autoconfigure에서 모든 설정을 자동화한다.

그러기 위해 spring-boot-autoconfigure는 자동화할 모든 dependency를 optional로 참조하고 있어 실제 사용 시엔 필요한 라이브러리를 각각 개별로 dependency 설정하여 사용한다.

따라서 어떤 라이브러리가 참조되어 있는지 혹은 어떤 환경 설정값이 세팅되어 있는지에 따라 자동화된 설정을 취사선택하여 사용할 수 있도록 스프링은 @Conditional* 어노테이션을 제공한다.

스프링은 해당 조건에 일치하는 경우에만 해당 빈을 생성한다.

예를 들어 spring-boot-autoconfigure에 있는 AopAutoConfiguration을 보면 다음과 같다.

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
		AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
	// 중간 생략
}

@ConditionalOnClass는 해당 설정된 class가 있는지 여부를 확인한다.

이를 통해 참조된 jar가 있는지를 확인할 수 있다.

@ConditionalOnProperty는 설정된 property 값이 있는지 확인한다.

이러한 조건들은 다양하며 spring-boot-autoconfigure의 org.springframework.boot.autoconfigure.condition 패키지 내에 제공된다.

대부분은 이름만 봐도 어떤 조건인지 유추할 수 있다.

다만 사용과 관련해서 주의해야 할 점은 @ConditionalOnClass와 같이 Class 조건을 확인하는 어노테이션은 class와 method에서 다르게 사용해야 한다. (이와 관련한 자세한 이유는 찾아보지 않음)

@ConditionalOnMissingClass는 반대로 처리한다.

@Configuration
@CondiionalOnClass(Servlet.class)
public class TestSampleConfiguration1 {
}

@Configuration
public class TestSampleConfiguration2 {
	@Bean
	@CondiionalOnClass(name = "javax.servlet.Servlet")
	public TestSample testSample() {
		return new TestSample();
	}
}

이렇게 수많은 configuration 들이 설정되면 spring-boot가 동작시 해당 configuration들을 호출할 수 있도록 등록해주어야 한다.

Servlet / Webflux 구분하기

webflux가 추가된 이후 웹에 대해 해당 조건 체크를 편하게 할 수 있는 @ConditionalOnWebApplication 어노테이션이 추가되었다.

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
public class ServletConfiguration {
}

@Configuration
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class WebfluxConfiguration {
}

Configuration 등록하기

등록은 classpath의 META-INF/spring.factories 파일에 아래와 같이 등록하면 된다.

EnableAutoConfiguration 항목이 설정되어 있으면 spring boot 실행 시 설정된 AutoConfiguration을 호출한다.

호출된 AutoConfiguration 각각에 설정된 Conditional 에 따라 빈이 생성되거나 생성되지 않는다.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
... 이하 생략

테스트 코드 작성

spring-boot-autoconfigure가 여러 라이브러리를 참조하고 필요한 경우에만 로드하여 사용하기 때문에 테스트 코드도 각 configuration 마다 개별적으로 동작해야 한다.

이를 위해 spring-boot는 ApplicationContextRunner / WebApplicationContextRunner / ReactiveWebApplicationContextRunner를 제공한다.

대략 다음과 같다. (spring-boot-autoconfigure의 jdbc 테스트 코드 중 일부)

public class DataSourceAutoConfigurationTests {

	private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
			.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
			.withPropertyValues("spring.datasource.initialization-mode=never",
					"spring.datasource.url:jdbc:hsqldb:mem:testdb-"
							+ new Random().nextInt());

	@Test
	public void testDefaultDataSourceExists() {
		this.contextRunner
				.run((context) -> assertThat(context).hasSingleBean(DataSource.class));
	}
    
 }

withConfiugration으로 로드할 Configuration을 지정하여 로드하여 사용하는 형태이다.

또한 withUserConfiguration도 제공하고 있다.

withConfiguration과 withUserConfiguration의 차이는 withUserConfiguration의 경우 PriorityOrdered가 LOWEST_PRECEDENCE로 설정되어 있다는 점이다.

spring-boot-autoconfigure에 설정들은 @ConditionalOnMissingBean과 같이 사용자가 bean을 설정하지 않은 경우 생성되는 설정들이 많다.

LOWEST_PRECEDENCE인 설정을 먼저 호출하며 해당 bean을 생성하고 sping-boot-autoconfigure의 설정을 로드하는 식으로 우선순위 조절이 필요한 경우를 위해 withUserConfiguration 설정을 제공한다.

Servlet 기반 테스트 코드 작성을 하는 경우 WebApplicationContextRunner 로 작성을 하고 Reactive 기반 테스트 코드 작성을 하는 경우 ReactiveWebApplicationContextRunner로 작성을 한다.

WebApplicationContextRunner / ReactiveWebApplicationContextRunner는 각각 @ConditionalOnWebApplication의 type이 SERVLET인 경우와 REACTIVE인 조건을 가진 autoConfiguration을 로드할 수 있게 설정해 준다.


개인적으로 boot의 autoconfigure 방식의 개발을 코드 작성하기 귀찮아 회피하고 있었다.

그런데 reactive가 등장하면서 취사 선택으로 코드가 동작해야하는 요구 조건이 생기자 autoconfigure 방식의 코드로 작성하지 않은 것이 결국 개발부채가 되었다.

미리미리 준비하는 것이 중요하다는걸 새삼 깨닫게 된다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요