안녕하세요. 이번 포스팅에서는 SpringBoot가 어떻게 자동적으로 필요한 Dependencies를 받아오는지, 필요한 Bean들을 생성하는지에 대한 포스팅을 진행해보려 합니다.
Dependencies 관리
만약에 저희가 maven project로 다음의 dependency만 추가한 뒤, package를 실행해 보면 어떠한 일이 벌어질까요?
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
보시는 바와 같이 정말 많은 의존성 패키지들이 추가됩니다.
심지어 저희는 spring-boot-starter-web이나 spring-boot-starter-test의 버전조차 명시하지 않았는데 이렇게 자동으로 필요한 의존성들이 로딩됩니다.
pom.xml을 보시면
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
</parent>
이러한 spring-boot-starter-parent가 설정되어있는 것을 볼 수 있습니다.
이 spring-boot-starter-parent를 살펴보시면
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
***
<artifactId>spring-boot-dependencies</artifactId>
***
<version>2.5.5</version>
</parent>
... 이하 생략
</project>
다시 spring-boot-dependencies 라는 xml 파일을 부모 의존성으로 가지고 있는 것을 볼 수 있습니다.
이 spring-boot-dependencies라는 xml파일에 저희가 필요한 모든 의존성에 대한 버전 정보가 기록되어있습니다.
이런 식으로 정의가 되어있습니다. 따라서 저희는 이 spring-boot-dependencies에 있는 의존성을 명시하게 되면, 별도의 설정이 없게 되면 적힌 버전에 따라서 로딩이 되는 것입니다.
(따라서 이 파일에 존재하지 않는 라이브러리에 대해서는 버전을 명시해주어야 합니다. 또한 설정된 버전 말고 다른 버전을 사용하시고 싶으면, 직접 버전 추가를 해 주셔야 합니다.)
이렇게 되면 어떠한 장점이 있을까요?
개발자가 해야 할 일이 줄어듭니다.
가령, 만약에 저희가 사용 중인 써드파티 라이브러리의 버전이 더 이상 지원이 안된다고 생각해보면, 저희는 라이브러리의 버전을 변경해주어야 합니다. 하지만 기존의 스프링 라이브러리와 호환이 잘 되는지 안되는지 직접 버전마다 변경을 해보며 확인해야 합니다...
이 과정을 묶어서 하나의 파일로 제공해주기 때문에 개발자는 이러한 과정에 필요한 노력이 줄어들게 됩니다.
그러면 내가 사용하는 이 라이브러리가 버전 관리를 해주는 라이브러리인지 확인하려면 직접 확인해보아야 할까요?
사실 intelliJ를 사용하시는 분이라면, 왼쪽 화살표를 클릭하시면 spring boot dependencies가 관리해주는 버전 정보를 알 수 있습니다. 따라서 저 문양이 확인되시면 버전 관리가 이루어지고 있는 라이브러리라고 생각하시면 됩니다.
아니라면, 직접 루트 파일에 가셔서 확인해보실 수 있습니다.
Auto Configuration
저희가 SpringBoot Project를 처음 생성하면 다음과 같은 코드가 생성됩니다.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
이 상태에서 저희는 Application의 main 메서드만 실행하여도 웹 애플리케이션을 생성할 수 있습니다.
그 이면에는 다양한 일들을 Spring framework에서 제공해주기 때문입니다.
위에 적었던 Dependencies 관리는 물론이고, Tomcat이라는 Web Application Server도 띄워줍니다. Tomcat의 IOC container에서는 Spring Application에 필요한 Bean들을 자동으로 생성해줍니다.
@SpringBootApplication은 그러면 어떠한 동작을 하길래, Bean들을 생성해줄까요?
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "nameGenerator"
)
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
보면 굉장히 복잡해 보이는데, 가장 중요한 Annotation은 다음과 같습니다.
@SpringBootConfiguration
@ComponentScan
@EnableAutoConfiguration
위에서부터 @SpringBootConfiguration은 흔히 사용하는 @Configuration과 비슷한 기능을 합니다.
다만 @SpringBootConfiguration은 스프링 부트 애플리케이션의 Configuration을 제공한다는 것을 명시하기 위해 사용한다고 합니다. 실제로도 SpringBootConfiguration은 Configuration을 포함하고 있기 때문에 기능상으로는 차이가 없고 다만, SpringBootConfiguration은 자동적으로 어디에 위치하는지를 파악하기 쉽게 만들어준다 합니다.
https://www.baeldung.com/springbootconfiguration-annotation
위 링크를 보시면 둘의 차이점에 대해서 서술하고 있습니다.
이제 실제로 Bean 등록에 필요한 어노테이션을 살펴보겠습니다.
실제로 SpringBoot는 두 단계에 나누어 Bean을 생성합니다.
@ComponentScan
@EnableAutoConfiguration
이렇게 두 단계로 나누는데요. Component Scan은 해당 class가 존재하는 루트 package로부터 하위 package에 있는 @Component 어노테이션을 찾아서 모두 생성합니다.
예를 들어 @Component, @Configuration, @Service, @Controller와 같은 어노테이션들이 빈으로 생성됩니다.
하지만 저희가 WebApplication을 생성하는데 필요한 Bean들을 모두 명시해서 생성하지는 않습니다. 따라서 웹 애플리케이션이 실행되는데 필요한 상당한 양의 Bean들이 자동적으로 생성되어 IOC container안에 생성되어야 합니다.
이 과정이 AutoConfiguration입니다.
SpringBoot autoconfigure라는 프로젝트에서 Spring Web Application에 필요한 Meta정보들을 가지고 있습니다. 파일 중에 spring.factories라는 파일이 존재하는데, 여기에 Auto Configure에 필요한 파일들이 명시되어있습니다.
이 EnableAutoConfiguration이라는 Key 값을 가지고 해당 위치로 온 다음에 하위에 필요한 Bean들을 모두 생성해줍니다. 정확히 말하면 "모두"는 아닙니다. 해당 파일을 각각 살펴보시면, Conditional이 붙어있는데 즉 생성하는 조건이 맞으면 생성하고 아니면 생성하지 않습니다.
오늘은 이렇게 Spring의 자동 설정 및 의존성 관리에 대해 알아보았습니다.
*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.
'Backend > Spring' 카테고리의 다른 글
[ SpringBoot ] 장르 기반 간단한 영화 추천 API 설계하기 #2 (0) | 2021.10.15 |
---|---|
[ SpringBoot ] 장르 기반 간단한 영화 추천 API 설계하기 #1 (0) | 2021.10.15 |
[Spring Boot] JWT token, refresh token을 활용한 회원 도메인 구현 (4) | 2021.09.30 |
[Spring/SpringBoot] Spring Security - Remember Me (0) | 2021.08.11 |
[Spring/SpringMVC] spring 빌드 시 npm 라이브러리 추가하기 (0) | 2021.08.05 |