Framework/Spring

[Spring] @SpringBootApplication은 무엇이 다를까?

KOOCCI 2022. 8. 19. 03:12

목표 : @SpringBootApplication의 Annotation에 대해 설명할 수 있다.


앞서, Spring 자체가 EJB로 서버개발이 너무 어려워, 그에 대한 대체/보완으로 나온 프레임워크라는 것을 배웠다.

그럼에도 점차 발전한 Spring은 SpringBoot를 내놓으며, 더 간소화된 내용으로 제공하고 있다.

그리고, SpringBoot에는 @SpringBootApplication이 main 실행의 Annotation으로 존재하며, SpringBoot와 Spring을 비교했을 때, 그 차이점에 포함된다.

그럼 무엇이 다른지 알아보자.

 

@SpringBootApplication은 어떻게 생겼을까?

@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;
}
Indicates a configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan
하나 이상의 @Bean 메서드를 선언하고 AutoConfiguration 및 ComponentScan을 트리거하는 구성 클래스를 나타냅니다. @Configuration, @EnableAutoConfiguration 및 @ComponentScan을 선언하는 것과 동일한 편리한 주석입니다.

 

@SpringBootApplication 의 주석을 지운 소스코드와 제일 상단에 있는 주석 내용이다.

Annotation들을 하나씩 파헤쳐 보면서, 그 의미를 찾아보자.

 

@Target(ElementType.TYPE)

@Target은 어디에 이 Annotation을 어디에 쓸 수 있는가? 에 대한 내용이다.

TYPE : Class, interface (including annotation type), or enum declaration

Enum인 ElementType으로 정의되어 있으며, TYPE의 경우, Class, Interface, Enum에 사용할 수 있다는 뜻이다.

Wiki에 따르면, Built-in Annotation 중, Meta-Annotation 중 하나로, 다른 Annotation에 적용된 Annotation이다.

 

TYPE 외에도  FIELD (Bean이 ANNOTATION_TYPE과 함께 대표적인 FIELD가 적용되는 Annotation이다.), METHOD(@Autowired에는 FIELD, CONSTRUCTOR, PARAMETER, METHOD, ANNOTATION_TYPE이 들어간다) 등 다양하니 다음 링크를 보면 그 리스트를 알 수 있다.

 

@Retention(RetentionPolicy.RUNTIME)

다음은 @Retention 이라는 Annotation이며 언제까지 살아있는가?(LifeCycle) 를 의미한다.

@SpringBootApplication 의 경우, RUNTIME시에 유지된다는 것을 알 수 있다. (@Retention도 @Retention이 걸려있는 걸 보면 재미있는 요소다(재귀?))

 

RetentionPolicy의 경우, 3개로 구성되어 있는데, 다음과 같다.

  • SOURCE
  • CLASS
  • RUNTIME

SOURCE의 경우, 이 Annotation은 .java 파일(소스코드 파일) 까지만 남아있게 된다. (컴파일시 사라짐)

Lombok의 @Getter를 보자.

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {

SOURCE로 선언이 되어 있다. 즉, 컴파일 시, 사라진다.

그 이유는 Lombok에서 바이트 코드를 생성해서 넣어주기 때문에, 컴파일 시 사라져도 상관없기 때문이다.

그래서, Lombok을 Gradle에 Dependency로 넣어줄 때, 컴파일 때만 적용해두면 되기 때문에 아래처럼 CompileOnly로 적용한다.

compileOnly 'org.projectlombok:lombok'

RUNTIME의 경우, 실행되어 메모리에 올라왔을 때도, 남아있게 된다.

즉, 실행중인 도중에도, 어노테이션 정보가 필요한 상황에 사용한다. (Controller, Service 모두 실행중에 ComponentScan이 가능해야 한다)

CLASS의 경우, .class 파일(컴파일 후 생성코드)까지 남아있게 된다.

이에 대해서는 잘 모르던 부분인데, 다음 글의 댓글에 적혀있어 남겨둔다.

.java 뿐 아니라 .class로 만들어진 파일(라이브러리 등)에 대해서도 어노테이션 정보를 가지고 와서 보여주기 위함이다. (IDE에서 자주 사용한다)

 

@Documented

@Documented JavaDoc 생성 시, Anntation에 대한 정보도 보여줄 수 있도록 설정하는 것이다.

 

 

@Inherited

Annotation이 상속되도록 설정하는 것이다.

 

@SpringBootConfiguration

Indicates that a class provides Spring Boot application @Configuration. Can be used as an alternative to the Spring's standard @Configuration annotation so that configuration can be found automatically (for example in tests).
Application should only ever include one @SpringBootConfiguration and most idiomatic Spring Boot applications will inherit it from @SpringBootApplication.
클래스가 Spring Boot 애플리케이션 @Configuration을 제공함을 나타냅니다. 구성을 자동으로 찾을 수 있도록(예: 테스트에서) Spring의 표준 @Configuration 주석에 대한 대안으로 사용할 수 있습니다. 애플리케이션에는 @SpringBootConfiguration이 하나만 포함되어야 하며 대부분의 관용적인 Spring Boot 애플리케이션은 @SpringBootApplication에서 이를 상속합니다.

위 설명에 따르면, @Configuration과 동일한 기능을 하지만, 단 한 개만 쓸 수 있는 Annotation이라고 한다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {

그리고, 내부적으로 @Configuration을 가지고 있다는 것을 알 수 있다.

또한, SpringBootTest 시, 그 정보를 자동으로 찾을 수 있다는 장점이 있다.

 

@EnableAutoConfiguration

사실 기존 Spring에서 Spring Boot로 바뀔 때, 가장 간편해지는 요소 중 하나가 Configuration의 자동화다.

@EnableAutoConfigurationspring.factories에 있는 자동 설정들을 실행 시에 적용시키는 것이다.

 다음과 같은 설정 파일들이 있고, 이를 별도의 작업없이 자동으로 적용할 수 있다.

해당 클래스패스에 Configuration관련 사항이 존재할 때만 실행되며, 만약 쓰고 싶지 않다면 exclude도 가능하다.

 

@ComponentScan

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

전체를 다 긁어보면 위와 같다.

앞서 말했지만, @ComponentScan@Configuration과 @Bean 및 @Component의 하위 어노테이션(@Repository, @Service, @Controller, @RestController, @ControllerAdvice, @RestControllerAdvice)이 있는 클래스 및 메소드를 찾아, Spring Container에 등록한다.

 

그런데 옆이 @Filter를 포함해 많은 내용이 있다.

한번 보도록 하자.

 

먼저 excludeFilters를 통해, 자동 등록에 포함시키지 않을 내용을 적용하고 있다.

그 내용은 TypeExcludeFilterAutoConfigurationExcludeFilter를 제외하고 자동 등록을 하겠다는 것이다.

여러 FilterType 중, CUSTOM으로 만들어진 두 Filter를 사용한다는 것이며, TypeExcludeFilter 는 SpringApplicationTest에서 사용하는 부분이라 제외를 하며(Hashcode, Equals 구현관련), AutoConfigurationExcludeFilter는 Auto-configuration 부분을 제외하는 것이다.

 

WrapUp

 

SpringBoot가 달라진점은 보통 다음을 꼽는다.

 

  • Dependency 간소화 (spring-boot-starter-*)
  • Configuration 간소화 (application.yml 설정)
  • Auto-Configuration
  • Embedded-timcat

그 중 Auto-Configuration 설정이 어디에 들어간 것인지, 확인해 볼 수 있었다.