해결된 질문
23.11.22 19:06 작성
·
2K
3
@Configuration
@ComponentScan
public class HelloApplication {
public static void main(String[] args) {
AnnotationConfigServletWebApplicationContext applicationContext = new AnnotationConfigServletWebApplicationContext(){
@Override
protected void onRefresh() {
super.onRefresh();
ServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory();
WebServer webServer = webServerFactory.getWebServer(
servletContext -> {
servletContext.addServlet("dispatcherServlet",
new DispatcherServlet(this)
).addMapping("/*");
}
);
webServer.start();
}
};
// applicationContext.register(HelloController.class);
// applicationContext.register(SimpleHelloService.class);
applicationContext.register(HelloApplication.class);
applicationContext.refresh();
}
}
강사님 안녕하세요,
강의를 복습하다보니 @ComponentScan을 사용할 때 @Configuration이 필요한지 의문이 들더라구요
처음에 @Configuration javaDoc를 보면 @ConponentScan 부분에
@Configuration 클래스는 구성 요소 스캐닝(component scanning)을 통해 부트스트랩될 뿐만 아니라, @ComponentScan 주석을 사용하여 스스로 구성 요소 스캐닝을 설정할 수도 있습니다.
라고 작성이 되었더라구요
@ComponentScan
public class HelloApplication {}
이 상태로 register에 등록을 해도 컴포넌트 스캔이 동작합니다.
Creating shared instance of singleton bean 'helloApplication'
Creating shared instance of singleton bean 'helloController'
Creating shared instance of singleton bean 'simpleHelloService'
@Configuration이 없어도 직접 register에 등록하면 애노테이션을 읽어서 컴포넌트 스캔이 동작합니다.
그러면 register에 @Configuration을 붙이지 않고 직접 등록한다면 @ComponentScan만 있어도 되는데
붙이는 이유가 궁금합니다.
답변 1
9
2023. 11. 23. 11:33
날카로운 질문을 해주셨네요.
자바의 애노테이션은 한국어로 하면 "주석"입니다. 책의 본문이나 문서의 텍스트에 부가적인 설명 등을 달아놓는 것을 말합니다. 자바의 애노테이션도 기본적으로 주석이라서 그 자체로 어떤 기능이 동작하지 않습니다. 동작하는 코드에 노트처럼 추가한 것이라고 볼 수 있습니다. @Override 같은 것이 그런 용도이지요. @Override를 붙이지 않는다고 오버라이드를 못하는 건 아닙니다. 이건 코드를 읽는 사람을 위해서 상위 타입에서 정의된 메소드를 오버라이드하는 메소드라는 일종의 코멘트를 붙인 겁니다.
그런데 @Configuration, @ComponentScan과 같은 스프링 기술에서 쓰는 애노테이션은 단순 각주 이상으로 런타임에 코드의 동작에 관여합니다. 주로 프레임워크가 하는 일에 참고하는 정보입니다. 그 자체로 명령은 아니지만, 메타데이터로 프레임워크가 사용해서 어떤 기능이 수행되게 만드는 것입니다. 이러게 런타임까지 유지되는 애노테이션은 그런 용도로도 활용됩니다.
@Configuration은 우선 @Component를 메타 애노테이션으로 가지고 있으므로 자신이 스프링의 빈으로 등록될 대상임을 각주로 표시합니다. 그러면서 @ComponentScan 대상이 됩니다.
스프링부트의 관례를 따르면 HelloApplication은 그 자체로 스프링의 빈으로 등록되야 합니다. 그래서 최소한 @Component를 붙여야 하는데, 그 안에 @Bean과 같은 팩토리 메소드를 넣기도 하니까 그중에서 @Configuration을 붙이는 것이 관례입니다. 이건 @SpringBootApplication이라는 부트가 만든 합성 애노테이션을 보면 알 수 있습니다. 그 메타 애노테이션으로 @Configuration이 있거든요. 위 코드가 나온 강의 부분은 아직 부트의 애노테이션을 직접 사용하는 단계가 아니라서 우선 @Configuration을 붙였습니다.
그 자체로 애플레케이션의 로직/기능을 제공하는 것이 아니라, 구성정보를 정의하는 것이 목적인 @Configuration이 붙은 클래스에 @ComponentScan도 같이 들어가는 것이 자연스럽습니다. 그래서 둘은 보통 같이 등장하고, @SpringBootApplication 애노테이션의 메타 애노테이션에도 이 두가지가 다 나옵니다.
그런데 질문하신 내용은 뭐냐면, HelloApplication은 초기화 코드에서 직접 register를 해주니까 기본적으로 빈으로 등록이 되고, 그렇게 등록된 빈에 @ComponentScan이 붙으면 나머지 빈들도 알아서 찾아주는데 왜 또 @Configuration을 붙여냐는 질문이신 거죠.
사실 애플리케이션의 부트스트래핑을 해주는 클래스를 register로 직접 등록하는 것은 아주 특별한 경우입니다. 부트의 xxxApplication으로 끝나는 클래스 정도 뿐이죠. 기능적으로만 보자면 HelloApplication에 @Configuration을 붙이지 않아도 됩니다. 그렇다면 스프링부트가 기본적으로 붙여주는 @SpringBootApplication 애노테이션이 붙은 클래스도 register로 등록이 되는데 거기에는 왜 또 @Configuration이 들어가있을까요?
두가지를 생각해볼 수 있습니다.
첫째는 애노테이션을 기능이 아니라 주석으로 생각한다면 이 클래스는 스프링의 빈이고 그 중에서도 구성정보를 자바 코드로 다루는 것임을 나타내기 위해서입니다. 애플리케이션 구조 전반에 적용되는 @ComponentScan을 일반 컨트롤러 클래스 같은데 붙이지 않죠. 자연스럽게 @Configuration 빈으로 정의된 클래스에 나오게 됩니다. 그래서 스프링부트의 애노테이션도 부트스트래핑으로 직접 등록되는 빈 클래스라고 하더라도 @Configuration을 붙이게 됩니다.
두 번째는 이게 멀티 모듈로 나눠지는 경우 해당 모듈의 루트가 되는 클래스이고 하위 패키지에서 빈 클래스를 찾아서 등록시키기 위해 @ComponentScan가 붙어있더라도 부트의 부트스트래핑 클래스가 아니게 될 수 있습니다. 물론 이때도 @Import나 자동구성에 의해서 등록될 수도 있긴하지만 register로 등록되는 건 아니겠죠. 설명을 적고 보니, 결국 질문하신 내용에 포함이 될 수도 있겠네요.
어쨌든 저는 다른 방식으로 빈으로 등록된다고 하더라도, 자바 애노테이션을 이용한 빈 구성 전략을 선택했다면 명시적으로 빈이 되는 클래스에는 @Componet류의 애노테이션을 붙이는 것을 권장합니다. 다른 분들이 코드를 읽을 때도 도움이 되겠죠.
2023. 11. 23. 18:20
강사님이 제 머리 속까지 읽으신줄 알았습니다.
제가 작성한 질문외에 궁금했지만 질문에 남지기 않았던 부분까지
상세하게 설명해주실 줄 몰랐습니다.
토비님 강의는 복습할때 마다 새로운걸 얻게 되네요
항상 건강하세요 ! 그리고 감사합니다
앞으로 준비하시는 강의도 결제대기하고 있습니다!