Spring으로 개발을 하면서 다양한 Annotation을 만났지만 “이걸 왜 쓰지?”라는 의문 없이 무작정 써왔다.
이제껏 모를 수 있어도 앞으론 모르지 말자. 기록하고, 익히고, 체득하자.
아래 어노테이션을 이해하기 전에 Bean의 개념에 대해 짚고 넘어가자.
쉽게 말하자면 스프링 IoC 컨테이너(= 스프링 컨테이너)가 관리하는 자바 객체(POJO)인데 생명주기가 관리되는 객체이다. 스프링 프레임워크의 특징 중 하나는 제어의 역전입니다. 근데 무엇을 제어하는가? Bean입니다. 아래 어노테이션들은 빈을 추가하여 스프링 컨테이너가 인식하게 해주는 방법들입니다.
Component VS Bean
Component는 싱글톤 빈을 생성한다. 클래스 단위로 생성되며 Service, Repository, Controller가 여기에 포함된다. 개발자가 직접 컨트롤이 가능한 Class에 선언한다.
Bean은 @Configuration이 들어간 클래스 내에 메소드에 선언한다. 개발자가 컨트롤이 불가능한 외부 라이브러리를 Bean객체로 선언하고 싶을때 사용한다.
Repository, Service, Controller(RestController)
세가지 어노테이션의 차이는 없다고 봐도 됩니다. 실제로 코드를 봤을때도 전부 선언된 어노테이션이 동일합니다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
사실상 @Component를 사용하셔도 무방합니다만, 로직의 분리를 위해서 나눠서 써줍시다.
참고로 @RestController은 @Controller + @ResponseBody입니다. Controller은 view뿐만 아니라 Data를 반환할 수도 있습니다. REST API를 개발할 때 JSON 형태의 객체 데이터를 반환하기 위해 ResponseEntity로 객체를 반환합니다. HttpStatus등의 객체를 담기 위해선 @ResponseBody가 필요하므로 REST API를 개발할 땐 @RestController을 사용하면 됩니다.
Autowired에서 필요한 과정을 살펴보자.
@Component로 객체를 스프링 Bean에 등록한다 → @Autorwired를 통해 등록된 빈에서 필요한 의존 관계를 설정한다.(타입을 우선으로 Bean 탐색) 하지만 Bean의 이름이 중복되면 오류가 발생한다. 이런 경우엔 @Qualifier나 @Primary를 사용하여 해결할 수 있다.
@Autowired
@Qualifier("sse-topic")
private ChannelTopic channelTopic;
Bean에 이름을 등록하여(Alias)중복을 방지한다.
@Primary는 여러 타입의 Bean이 존재할 때, 특정 Bean을 우선적으로 주입받게 해준다.
@Resource와 @Inject는 Spring이 아닌 Java가 제공하는 애들이다. @Autowired와 달리 Type이 아닌 Name이 우선이며, 생성자에는 붙일 수 없다.
근데 어차피 DI는 @Autowired가 권장되지 않아 생성자 주입 방법만 사용하는데 얘들이 이제 필요할까?
@Scope를 통해 Bean의 스코프를 지정할 수 있습니다. 스프링은 컨테이너에 선언한 Bean마다 하나의 인스턴스를 생성하고 이렇게 만들어진 인스턴스는 전체 컨테이너 스코프에 공유됩니다. Bean의 스코프는 기본적으로 singleton입니다.
| singleton | IoC 컨테이너당 빈 인스턴스 하나를 생성 |
|---|---|
| prototype | 요청할 때마다 빈 인스턴스를 새로 생성 |
| request | HTTP 요청당 하나의 빈 인스턴스 생성 |
| session | HTTP 세션당 빈 인스턴스 하나를 생성 |
| globalSession | 전역 HTTP 세션당 빈 인스턴스 하나를 생성 |
Bean의 생성주기는 생성 → 초기화 → 사용 → 소멸이다. 간략하게 짚고 넘어가자.
@Component에 대해서만 동작 한다. 말 그대로 생성 이후에, 폐기 이전에 실행된다.
스프링은 POJO를 즉시 호출(Eager Loading)하는데 비해 @Lazy는 여유롭게 호출(Lazy Loading)한다. 애플리케이션이 요구하거나 다른 POJO가 참조하기 전까지 초기화 되지 않는다. 한번에 모든 Bean을 호출하는 행위가 무거워서 부담이 될 수 있다. 이를 이용하면 초기 실행시 더욱 성능 개선을 기대할 수 있다. 다만 Bean을 호출하기 전까지는 어떤 오류가 있는지 알수도 없다.