스프링 3 계층을 클래스 화할 때 @Controller, @Repository, @Service, @Component... 등 어노테이션을 선언하여 사용하는데 어노테이션을 지정하는 것만으로도 각 계층에서 서로를 DI 해서 사용할 수 있다.
이 구조를 이해하기 위해선 우선 Spring Bean에 대해 이해할 필요가 있다.
Spring Bean?
스프링에서는 DI 컨테이너(혹은 스프링 IoC 컨테이너)에서 관리되는 POJO(Plain Old Java Object)를 Bean이라고 부른다. 이러한 Bean들은 스프링에 의해 생성되고, 라이프 사이클을 수행하고, 의존성 주입을 수행한다. 즉, Bean들은 스프링을 구성하는 중요한 역할을 수행한다.
스프링에서는 이러한 Bean들을 XML, @Bean, @Component를 이용하여 등록한다. XML 방식을 제외한 나머지 방법으로 스프링이 어떻게 Bean을 관리하고 사용하는지 알아보자.
스프링 3계층을 통한 DI 구현
지난번 포스팅에선 Controller 구현을 통해 스프링에서 컨트롤러를 어떻게 인지하는지를 알아봤다. 이번엔 Controller, Service, Repository를 구현하고 3 계층에서 어떻게 Bean으로 등록되고 서로를 어떻게 의존성 주입하는지 알아보자.
우선 각각의 계층을 구성해 보자.
클라이언트가 '/test'를 실행하였을때 'TEST' 란 값이 응답되는 구조이다. 앞 전 포스팅에서 이야기했듯이 클라이언트의 요청을 서버에서 우선 응답하는 클래스는 컨트롤러이다. 컨트롤러는 실제 요청을 처리하는 메서드, 즉 핸들러를 동작하여 처리를 하게 된다.
@RestController
public class TestController {
@Autowired
TestService testService;
@GetMapping("/test")
public String getTest(){
return testService.find();
}
}
그럼 컨트롤러라는 것은 어떻게 아는 것일까? @RestController 어노테이션 정의를 보면 알 수 있다. 컨트롤러라는 것을 명시하는 어노테이션인 @Controller가 선언되어 있다.
그럼 @Controller 어노테이션을 확인해 보면 @Component 어노테이션이 선언되어 있는 것을 확인할 수 있다.
요청에 실행되는 메서드를 보면 TestService의 메서드를 호출하는 것을 확인할 수 있다. 다른 클래스의 메서드를 활용하는 것을 의존한다.라고 표현하는데 의존성 주입을 위해 @Autowired라는 어노테이션이 활용되었다. 그럼 우리는 @Autowired 어노테이션이 의존 객체를 호출을 위해 사용하는 어노테이션이라고 유추가 가능하다.
그럼 실제 컨트롤러한테 비즈니스 처리 요청을 받아 처리하는 서비스를 구현해 보자.
서비스도 마찬가지로 @Service 어노테이션을 선언하여 서비스라는 것을 명시해 준다.
@Service
public class TestService {
@Autowired
TestRepository testRepository;
public String find() {
return testRepository.find();
}
}
@Service 어노테이션을 살펴보면 여기도 마찬가지로 @Component 어노테이션이 선언되어 있는 것을 확인할 수 있다.
그리고 서비스는 비즈니스 로직을 처리하기 위해선 DB와 상호작용하는 Repository 클래스를 의존받아 처리를 진행한다.(여기선 테스트이기 때문에 DB 상호작용하지는 않음)
마찬가지로 @Autowired를 이용하며 의존성 주입을 한다.
마지막으로 Repository를 구현해 보면 @Repository 어노테이션을 사용하면 구성한다. @Repository 어노테이션 정의 살펴보면 @Component 가 선언되어 있는 것을 알 수 있다.
@Repository
public class TestRepository {
public String find() {
return "TEST";
}
}
이런 구성을 통해 클라이언트 요청에 따라 Controller -> Service -> Repository -> Service -> Controller 구조로 의존관계가 된다.
그래서?
위에서 구현한 3 계층을 살펴보면 각각 계층을 나타내는 @Controller, @Service, @Repository 어노테이션은 공통적으로 @Component가 선언되어 있는 것을 알 수 있다. 즉, 스프링 빈은 @Component 어노테이션을 이용하여 관리가 된다.
스프링 빈으로 등록이 되면 각 클래스에서 의존성 주입(DI)을 할 수 있는데 각 빈들을 찾아 연결시켜 주는 역할은 @Autowired 어노테이션 이용한다. (DI 선언 방식은 3가지 있지만 우린 일단 과정만 보면 된다.)
그럼 @Component 선언한 클래스들을 어디서 스캔하는 것일까?
스프링 부트 프로젝트를 만들게 되면 @SpringBootApplication 어노테이션을 붙은 클래스 생성이 된다. 해당 어노테이션의 정의를 확인해 보면 @ComponentScan 어노테이션이 있는 것을 확인할 수 있고 여기서 @Component 들을 스캔하는 것을 알 수 있다.
최종적으로 정리해 보면, @Controller, @Service, @Repository 등은 @Component을 사용하여 구성한 어노테이션들이고 스프링 빈을 등록하려면 @Component 어노테이션을 사용해야 한다. 그리고 사용된 @Component 어노테이션들은 @SpringBootApplication에서 스캔되어 빈등록이 되므로 의존성 주입을 할 수 있게 된다.
그럼 @Bean은 언제 사용하지?
스프링을 사용하다 보면 @Configuration + @Bean 어노테이션 구조로 설정 클래스를 만들어 빈 등록하는 구조를 많이 봤을 것이다.
그럼 @Component 와의 차이는 무엇일까?
@Component는 클래스 레벨에 선언되어 스프링이 런타임시에 컴포넌트스캔을 하여 자동으로 빈을 등록하는 어노테이션이다. 반면에 @Configuration + @Bean 구조는 수동으로 스프링 컨테이너에 빈을 등록하는 방법이다.
기본적으로 @Configuration도 컴포넌트의 한 종류인데 메서드 레벨에 선언되는 @Bean을 관리하기 위해 사용된다. @Component를 이용해 자동으로 빈 등록을 한다면 스프링이 해당 클래스의 객체의 생성을 제어하지만 @Bean 만 사용하여 직접 빈을 등록하였다면 해당 빈을 여러 번 호출할 수 있는 문제가 발생할 수 있다. 그러한 이슈를 제어하기 위해 스프링은 @Bean 사용 시 @Configuration을 같이 사용해 싱글톤을 보장하도록 한다.
@Configuration은 이름에서 알 수 있듯이 자바 코드 기반으로 스프링의 설정 정보를 관리할 때 주로 사용되게 된다.
Reference
https://mangkyu.tistory.com/234
https://jojoldu.tistory.com/27
https://medium.com/sjk5766/bean%EA%B3%BC-component-%EC%B0%A8%EC%9D%B4-96a8d0533bfd
'Framework > Spring' 카테고리의 다른 글
[스진초5기/Spring] DTO, Entity 변환은 어느 계층에서 일어나야할까? (0) | 2023.11.16 |
---|---|
[스진초5기/Spring] DTO, VO, Entity (0) | 2023.11.08 |
[스진초5기/Spring] 그래서 Controller가 뭐야? (1) | 2023.11.03 |
[스진초5기/Spring] Controller, Service, Repository (3계층) (0) | 2023.10.31 |
[스진초5기/Spring] IoC, DI 정리 (0) | 2023.10.30 |