자동 구성 @AutoConfiguration을 사용한 라이브러리 만들어보기
spring boot에서는 AutoConfiguration 기능을 통해 web, data, aop, cache, jdbc 등 여러 라이브러리에 대한 자동 구성을 제공하는데요.
아래 내용은 '@AutoConfiguration을 적용하여 자동 구성이 실행되는 방식을 활용한 라이브러리를 만드는 방법과 만들어진 라이브러리를 사용하는 방법'에 대한 내용입니다.
(예시에서는 gradle build tool을 사용하였습니다.)
* 추가로 spring boot 자동 구성 auto configuration이 동작하는 원리가 궁금하시다면 포스팅 맨 하단에 관련 자료에 대한 링크가 있으니 참고하시면 좋을 것 같습니다.
build.gradle
plugins {
id 'java'
}
group = 'testlibrary'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:3.0.2'
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.0.2'
}
tasks.named('test') {
useJUnitPlatform()
}
(라이브러리 프로젝트의 build.gradle)
먼저 라이브러리를 생성할 프로젝트의 build.gradle 파일입니다.
여기서는 실행 가능한 jar를 만드는 것이 아니라 다른 프로젝트에 포함되어 사용할 수 있는 라이브러리 jar를 만드는 것이 목적이기 때문에 스프링 부트 플러그인 기능을 사용하지 않았습니다.
의존성의 경우 코드 내부적으로는 spring-boot-autoconfigure, SpringFramework Context 등이 사용되었는데, spring-boot-starter-web 의존성 하나에 필요한 의존성이 모두 들어있어 하나로 사용하였는데요.
의존성 버전을 자동으로 관리해 주는 io.spring.dependency-management 플러그인을 사용하지 않기 때문에 버전을 직접 명시하였습니다.
(의존성의 경우 당연히 구현하는 기능에 때라 포함되는 것이 다를 수 있습니다.)
이어서 간단한 클래스를 포함한 라이브러리를 빌드하여 사용하는 방법을 살펴볼 텐데요.
해당 내용은 @AutoConfiguration을 적용하지 않은 경우와 적용한 경우 두 가지를 비교해서 살펴보겠습니다.
라이브러리 빌드 및 사용(@AutoConfiguration을 적용하지 않은 경우)
@Slf4j
public class TestUtils {
@PostConstruct
public void init() {
log.info("===== init testUtils =====");
}
public String something() {
log.info("===== something method =====");
return "something";
}
}
(라이브러리 프로젝트의 TestUtils 클래스)
다음과 같이 TestUtils라는 아주 간단한 클래스를 하나 생성하고 프로젝트를 빌드하여 라이브러리 jar 파일을 생성합니다.
(프로젝트 디렉터리/build/libs 하위에 라이브러리 jar 파일이 생성됩니다.)
이어서 생성된 라이브러리를 사용할 프로젝트에서는 프로젝트 디렉터리 하위에 libs 디렉터리를 생성 후, 생성된 라이브러리 jar 파일을 넣어줍니다.
dependencies {
implementation files('libs/testlibrary.jar')
...
}
(라이브러리를 사용하는 프로젝트의 build.gradle 파일 일부)
그리고 build.gradle 파일의 dependencies 부분에 다음과 같이 implementation files를 통해 라이브러리 파일 경로를 포함한 파일명을 명시해 줍니다.
@RestController
@RequiredArgsConstructor
public class TestController {
private final TestUtils testUtils;
@GetMapping("/test")
public ResponseEntity<?> test() {
String something = testUtils.something();
return ResponseEntity.ok(something);
}
}
(TestController class)
@Configuration
public class LibraryConfig {
@Bean
public TestUtils testUtils() {
return new TestUtils();
}
}
(TestUtils를 bean 등록하기 위한 config 클래스)
다음과 같이 추가한 라이브러리의 TestUtils 클래스를 의존성 주입 방식으로 사용하기 위해서는 TestUtils 클래스를 bean으로 등록하는 코드가 추가로 필요한데요.
(bean으로 등록하여 사용한다는 경우에 대한 가정이며, 라이브러리 활용 방식에 따라 생성자를 통해 인스턴스를 만들어 사용할 수도 있습니다.)
만약 bean 등록을 하지 않은 상태로 위 예시와 같이 TestUtils 클래스를 의존성 주입 방식으로 사용하려고 하는 경우 당연히 해당 빈을 찾을 수 없으며, 빈 등록이 필요하다는 결과를 만나게 됩니다.
그러면 다시 라이브러리 프로젝트로 이동하여 스프링 부트 자동 구성을 위한 @AutoConfiguration을 적용해 보겠습니다.
자동 구성 추가(@AutoConfiguration을 적용할 경우)
@AutoConfiguration
public class TestAutoConfiguration {
@Bean
public TestUtils testUtils() {
return new TestUtils();
}
}
(라이브러리 프로젝트에 @AutoConfiguration 어노테이션을 설정한 클래스 생성)
기존에 TestUtils 클래스만 존재하던 라이브러리 프로젝트에 다음과 같이 @AutoConfiguration 어노테이션을 적용한 클래스를 생성하여 내부에서 TestUtils를 빈으로 등록합니다.
그리고 src/main/resources 하위에 META-INF/spring 디렉터리를 추가로 생성하고, 해당 디렉터리에 위 이미지와 같이 org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일을 생성합니다.
testlibrary.TestAutoConfiguration
(org.springframework.boot.autoconfigure.AutoConfiguration.imports)
그리고 해당 파일 내부에 AutoConfiguration의 대상이 되는 클래스 경로를 명시합니다.
* spring boot의 AutoConfiguration 기능은 해당 경로에 있는 파일들을 읽어 자동 구성을 적용하는데요. 자동 구성의 원리에 대한 자세한 내용은 포스팅 하단 관련 자료를 참고하시면 좋을 것 같습니다.
여기까지 자동 구성을 위한 추가 작업이 완료가 되었으면 라이브러리를 다시 빌드합니다. (clean -> build)
라이브러리를 사용할 프로젝트에서는 기존의 라이브러리 파일을 삭제 후 새로 빌드된 라이브러리 파일을 추가하고 build.gradle도 reload 하여 새로 추가한 라이브러리를 읽을 수 있도록 합니다.
이번에는 @AutoConfiguration을 적용하지 않았을 때 TestUtils 클래스의 bean 등록을 위해 추가했던 LibraryConfig 클래스를 제거하고 프로젝트를 다시 실행해 보는데요.
(@ConditionalOnMissingBean 같은 기능을 사용하지 않았기 때문에 기존의 빈 등록 부분을 제거하지 않으면 bean이 중복 등록되어 프로젝트가 실행되지 않습니다.)
실행 결과 자동 구성이 적용되어 TestUtils에서 @PostConstruct를 적용한 init() 메서드가 동작하는 것을 확인할 수 있었고, 라이브러리를 사용하는 프로젝트에서 TestUtils에 대한 빈을 따로 등록하지 않았음에도 스프링 부트 자동 구성을 통해 TestUtils가 빈으로 등록되어 의존성 주입 방식으로 사용할 수 있게 되었습니다.
@Value Annotation 추가
@Slf4j
public class TestUtils {
@Value("${test.library.id}")
private String id;
...
}
(TestUtils Class)
추가로 @Value 어노테이션을 통해 properties에 있는 값을 가지고 올 수 있는지도 궁금했는데요.
이렇게 @Value를 추가하여 라이브러리를 빌드 후 프로젝트에서 해당 라이브러리를 추가하여 구동하였을 때, 아래와 같이 test.library.id 값을 확인할 수 없다는 문구가 출력되는 것을 확인하였고, properties(또는 yml) 파일에 해당 값을 추가하였을 때 정상적으로 구동되는 것을 확인하였습니다.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'test.library.id' in value "${test.library.id}"
여기까지 @AutoConfiguration 적용하여 라이브러리를 생성하고 사용해 보았는데요.
회사에서 사용하던 내부 라이브러리에도 해당 내용을 적용하려고 해 봤는데, @AutoConfiguration 적용에는 생각보다 고려해야 할 사항이 많아 바로 적용을 하지는 못했습니다.
하지만 라이브러리를 만드는 쪽에서 고려해야 될 부분들을 잘 고려하여 자동 구성을 적용한 라이브러리를 만든다면 확실히 사용하는 측면에서의 장점은 많을 것 같다는 생각입니다.
(해당 포스팅은 김영한 님의 '스프링 부트 - 핵심 원리와 활용' 강의 내용을 참고하여 직접 만들어보며 정리한 내용입니다.)
< spring boot 자동 구성이 동작하는 원리 >
2023.07.21 - [Programming/Spring Boot] - Spring Boot 자동 구성 AutoConfiguration 동작 원리 파헤치기
< conditional 기능과 확장 기능 >
2023.07.18 - [Programming/Spring Boot] - @Conditional 어노테이션의 기능과 확장(@ConditionalOnXxx)
< 참고용 testlibrary github >
'Programming > Spring Boot' 카테고리의 다른 글
(spring boot) XSS 공격 개념과 대응 방법 정리 (0) | 2023.08.12 |
---|---|
(spring boot) Redis Key Event Notification 처리 방법 (0) | 2023.08.09 |
Spring Boot 자동 구성 AutoConfiguration 동작 원리 파헤치기 (0) | 2023.07.21 |
@Conditional 어노테이션의 기능과 확장(@ConditionalOnXxx) (0) | 2023.07.18 |
@QueryDelegate 어노테이션으로 QueryDSL where절 간결하게 사용하기 (0) | 2023.07.07 |