💡이 포스팅은 토비님의 인프런 강의인 토비의 스프링 부트 - 이해와 원리를 수강하고 학습한 내용을 정리한 포스팅입니다.
토비님의 강의를 수강하며 정리한 GitHub Repository입니다.
GitHub - kiekk/inflearn-toby-spring-boot
Contribute to kiekk/inflearn-toby-spring-boot development by creating an account on GitHub.
github.com
이번 챕터에 대해 정리할 내용은 다음과 같습니다.
- 애노테이션 선언 시 필수 애노테이션
- 메타 애노테이션 vs 합성 애노테이션
- 애플리케이션 빈, 컨테이너 인프라스트럭쳐 빈, 애플리케이션 인프라스트럭쳐 빈
- ImportSelector, ImportCandidates
- @Configuration 동작 방식 확인
애노테이션 선언 시 필수 애노테이션
애노테이션을 생성할 때 필수로 작성해야 하는 애노테이션은 다음과 같습니다.
- @Retention
- @Target
@Retention 애노테이션은 작성한 애노테이션이 유지되는 범위를 지정할 수 있으며 아래와 같이 RetentionPolicy을 인자로 전달받습니다.
RetentionPolicy에는 아래와 같이 3개의 값이 있습니다.
- SOURCE: 컴파일러 이후에 사라집니다.
- CLASS: 컴파일러가 클래스를 참조할 때까지만 유효합니다.
- RUNTIME: 컴파일 이후에도 JVM으로 인해 계속 참조가 가능합니다.
@Target의 경우 애노테이션을 사용할 수 있는 범위를 지정하며 아래와 같이 ElementType을 배열로 전달받습니다.
ElementType에는 아래와 같이 다양한 값들이 있는데, 여기서 주로 사용되는 것들은 TYPE, FIELD, METHOD 정도인 것 같습니다.
메타 애노테이션 vs 합성 애노테이션
메타 애노테이션에 대해 찾아보면 주로 애노테이션을 선언할 때 사용되는 애노테이션이라고 합니다.
그리고 합성 애노테이션은 토비님 강의에서는 언급되지만 따로 공식 용어는 없는 것으로 보아 토비님이 직접 정의하신 용어인 듯하며, 합성 애노테이션에 대해 여러 애노테이션을 조합한 애노테이션이라고 강의에서 설명하십니다.
위에 언급했던 @Retention, @Target이 메타 애노테이션에 해당되는데, 그렇다면 @Controller, @Service에도 @Component가 사용되는데 이때 @Component도 메타 애노테이션이라고 할 수 있지 않을까요?
그리고 이때 @Retention, @Target, @Component 등 여러 애노테이션이 조합되었기 때문에 이 또한 합성 애노테이션이라고 할 수 있지 않을까요?
하지만 이렇게 되면 모든 애노테이션이 메타 애노테이션이자 합성 애노테이션이 될 수 있기 때문에 용어들을 사용하려는 의도에 대해 개인적으로 생각해 보고 결론을 내려보았습니다.
먼저 메타 애노테이션을 기능, 그리고 사용하려는 목적에 따라서 나눠볼 수 있을 것 같습니다.
@Retention, @Target 애노테이션을 확인해보면 @Target에 ANNOTATION_TYPE이 지정되어 있습니다.
따라서 @Target에 ANNOTATION_TYPE이 지정되어 있는 애노테이션을 메타 애노테이션이라고 부릅니다.
기능에 따라서 분류할 경우 ANNOTATION_TYPE이 지정되어 있는 @Retention, @Target이 메타 애노테이션이 됩니다.
또한 메타 애노티에션은 애노테이션 생성 시 필수로 작성해야 하기 때문에 해당 애노테이션이 사용되었다고 합성 애노테이션이라고 부르지는 않습니다.
그리고 목적에 따라서 분류할 경우 @Retention, @Target 외에 애노테이션 생성 시 사용되는 애노테이션의 경우도 메타 애노테이션이라고 할 수 있습니다.위의 예로 들면 @Controller, @Service 애노테이션을 생성 시 @Component 애노테이션이 사용되었기 때문에 이때 @Component는 @Controller, @Service 애노테이션에 대한 메타 애노테이션이다라고 할 수 있습니다.
그리고 메타 애노테이션 외에 애노테이션 생성 시 애노테이션을 2개 이상 사용할 경우 합성 애노테이션이라고 할 수 있습니다.위의 예로 들면 @Controller, @Service는 @Component 애노테이션만 사용되었기 때문에 합성 애노테이션이라고 할 수 없으며,
아래와 같이 @Controller, @ResponseBody 가 사용된 @RestController를 합성 애노테이션이다라고 할 수 있습니다.
이 부분은 용어들을 사용하려는 의도나 목적을 생각하면서 개인적으로 추측해 본 내용이므로 정확하지 않을 수 있습니다.
혹시 포스팅을 보시는 분들 중 위 내용이 틀리다거나 저와 다른 의견이 있으신 분들은 댓글로 남겨주시면 감사하겠습니다.
애플리케이션 빈, 컨테이너 인프라스트럭쳐 빈, 애플리케이션 인프라스트럭쳐 빈
빈 오브젝트의 역할과 구분 강의에서 각각의 역할과 그에 해당하는 Bean들을 설명해 주는데, 이 부분은 토비의 스프링에도 언급된 내용입니다.
하지만 복습 차원에서 한 번 정리해 보도록 하겠습니다.
💡애플리케이션 로직 빈 = 애플리케이션 빈
스프링에서 말하는 빈은 스프링 IoC/DI 컨테이너에 의해 생성되고 관리되는 오브젝트다.
일반적으로 애플리케이션의 로직을 담고 있는 주요 클래스의 오브젝트가 빈으로 지정된다.
Vol.1에서 살펴봤던 데이터 로직을 다루는 DAO, 비즈니스 로직과 기반 서비스를 다루는 서비스 오브젝트 그리고 앞으로 살펴볼 웹 로직을 다루는 컨트롤러 오브젝트 등이 대표적이다.
이런 빈을 앞으로 애플리케이션 로직 빈(애플리케이션 빈)이라고 부르겠다.
💡애플리케이션 인프라 빈 = 애플리케이션 인프라스트럭쳐 빈
...
DAO가 사용하는 DataSource 오브젝트는 여러 DAO 빈과 관계를 맺고 사용된다.
구현 클래스가 여러 가지여서 하나를 지정해야 하고 연결 방법과 관련된 속성은 코드 외부에서 제공하는 경우가 일반적이다.
...
트랜잭션 추상화에 사용되는 DataSourceTransactionManager도 유사하다.
DataSourceTransactionManager는 DataSource 오브젝트와 관계를 맺으며 DB 커넥션에 대한 트랜잭션을 관리하는 책임을 진다.
...
그런데 DataSource나 DataSourceTransactionManager는 UserDao나 UserSErvice처럼 스프링 컨테이너에 등록되는 빈이기는 하지만 성격이 다르다.
DataSource빈은 UserDao처럼 애플리케이션의 로직을 직접 담당하지 않는다.
DataSource는 대부분 스프링 또는 다른 프로젝트에서 만들어둔 클래스를 사용한다.
이런 빈의 특징은 애플리케이션의 로직 빈(애플리케이션 빈)을 지원한다는 점이다.
...
이런 빈들도 애플리케이션이 동작하는 데 직접 참여하므로 애플리케이션 빈의 일종이다.
그런데 개발자가 직접 작성하는 로직을 담고 있는 것은 아니므로 애플리케이션 로직 빈과 구분해서 애플리케이션 기반 빈 또는 애플리케이션 인프라스트럭쳐 빈이라고 부르면 좋겠다.
앞으로는 간략히 줄여서 애플리케이션 인프라 빈(애플리케이션 인프라스트럭쳐 빈)이라고 하겠다.
💡컨테이너 인프라 빈 = 컨테이너 인프라스트럭쳐 빈
AOP를 설명하면서 소개했던 DefaultAdvisorAutoProxyCreator 자신은 애플리케이션 로직을 담고 있지 않으며 다른 애플리케이션 로직을 담은 빈과 관계를 맺고 외부 서비스를 사용하는데 도움을 주는 것도 아니다.
대신 스프링 컨테이너의 기능에 관여한다.
...
이렇게 스프링 컨테이너의 기능을 확장해서 빈의 등록과 생성, 관계설정, 초기화 등의 작업에 참여하는 빈을 컨테이너 인프라스트럭쳐 빈, 줄여서 컨테이너 인프라 빈이라고 부르겠다.
정리하면 애플리케이션 빈은 개발자가 직접 작성하는 애플리케이션 로직을 담당하는 Bean
애플리케이션 인프라 빈은 애플리케이션의 로직을 담당하지는 않지만 애플리케이션 빈과 같이 사용되는 Bean
컨테이너 인프라 빈은 애플리케이션의 로직에는 관여하지 않지만 스프링 컨테이너의 기능에 관여하는 Bean
ImportSelector, ImportCandidates
@Configuration이 적용된 클래스를 import 하고 싶은 경우에는 @Import 애노테이션을 사용하는 데 단점은 import 할 클래스들이 정적으로 작성되어 있다는 점입니다.
이를 동적으로 가져올 수 있게 하려면 ImportSelector를 사용해야 합니다.
ImportSelector를 사용하면 selectImports() 메서드에 import 할 클래스 정보를 문자열로 전달하게 되는데 전달한 문자열을 동적으로 가져오게 됩니다.
강의에서는 ImportSelector를 상속한 DeferredImportSelector를 사용하는 데 사용 방법은 동일합니다.
그리고 이 클래스를 @EnableMyAutoConfiguration 애노테이션 내부에서 @Import를 하게 되면 @EnableMyAutoConfiguration 애노테이션만 작성해도 selectImports() 내부에 작성된 DispatcherServletConfig, TomcatWebServerConfig가 적용됩니다.
@EnableMyAutoConfiguration에 DispatcherServlet, TomcatWebServerConfig를 각각 @Import에 작성해도 되지 않냐고 생각할 수도 있는데, 이에 대해서는 토비님이 강의에 자세하게 설명해 주셨으니 강의를 참고하시기 바랍니다.
여기서 한 단계 더 나아가 selectImports() 메서드 내부에서 문자열로 적은 클래스 정보를 별도의 파일로 분리해 보도록 하겠습니다.
이 때는 ImportCandidates를 사용하게 되는데, 이 클래스는 아래와 같이 특정 경로에 있는 imports 파일을 불러옵니다.
imports 파일 안에는 아까 작성했던 클래스 정보 문자열이 작성되어 있습니다.
위와 같이 ImportCandidates를 사용해 구성 정보를 load 하게 되면 위와 같이 클래스 정보를 Iterable <String> 타입으로 반환합니다.
그렇게 되면 imports 파일 내부에 작성된 모든 클래스 정보들이 선택되게 됩니다.
개인적으로는 이 부분이 매우 흥미로웠고 Spring Boot가 이렇게 자동 구성을 하는 구나라고 이해할 수 있었습니다.
이후 챕터에서 나오는 @Conditional 애노테이션과 같이 사용하면 imports 파일에 작성된 여러 구성 정보들 중에서 특정 조건에 따라 동적으로 적용하고 싶은 구성 정보를 선택할 수 있는데, 이 부분은 다음 챕터에서 다시 정리해 보도록 하겠습니다.
@Configuration 동작 방식 확인
@Configuration은 보통 자바 코드로 개발자가 직접 @Bean을 등록하거나 구성 정보를 등록할 때 사용합니다.
@Configuration을 사용하면 조금 특이하게 동작하는데 아래 코드를 예로 들어보겠습니다.
Bean1, Bean2 클래스를 만든 후 MyConfig 클래스에 각각 bean1, bean2로 빈을 등록했습니다.
먼저 MyConfig를 직접 생성하여 각각의 bean1(), bean2() 메서드로 두 인스턴스를 생성한 후 비교를 해보았습니다.
결과는 당연하게도 일치하지 않습니다. bean1() bean2() 메서드가 실행되면서 common() 메서드를 호출할 때 new 키워드에 의해 서로 다른 인스턴스가 생성되었기 때문입니다.
하지만 @Configuration을 사용하면 이와 전혀 다르게 동작합니다.
이번에는 MyConfig를 Bean으로 등록하여 @Configuration이 동작할 수 있도록 한 후 테스트를 해보았습니다.
이제는 bean1, bean2의 common이 같은 객체임을 확인할 수 있습니다.
우리가 이해하기로는 common() 메서드에서 매번 new Common()을 하기 때문에 첫 번째 테스트처럼 각각 다른 인스턴스가 생성되어야 한다고 생각했지만, @Configuration과 @Bean을 사용하게 되면 해당 @Bean이 싱글톤으로 생성될 수 있도록 하기 때문에 위와 같이 common이 여러 번 생성되지 않고 딱 한 번 생성된 것을 확인할 수 있습니다.
'Lecture > 토비의 스프링 부트 - 이해와 원리' 카테고리의 다른 글
토비의 스프링 부트 - 외부 설정을 이용한 자동 구성 (1) | 2023.02.18 |
---|---|
토비의 스프링 부트 - 조건부 자동 구성 (0) | 2023.02.18 |
토비의 스프링 부트 - DI와 테스트, 디자인 패턴 (0) | 2023.02.08 |
토비의 스프링 부트 - 독립 실행형 스프링 애플리케이션 (1) | 2023.02.05 |
토비의 스프링 부트 - 독립 실행형 서블릿 애플리케이션 (0) | 2023.02.02 |
댓글