평소에 코드를 짤 때 필드에 객체를 선언하고 @Autowired 애노테이션을 통해 의존성을 주입하는 방식을 사용해 왔습니다. 그런데 이 방식은 공식 사이트에서도 추천하지 않습니다. 그 이유를 정리해보겠습니다. 의존성 주입의 종류는 Constructor, Setter, Field 타입이 있습니다.
1. 의존성 주입
다음 코드는 의존성을 Constructor Injection(생성자 주입 방식)으로 주입해주는 코드입니다.
public Class ItemService{
private final ItemDao itemDao;
private final ItemPriceDao itemPriceDao;
@Autowired
public ItemService(ItemDao itemDao, ItemPriceDao itemPriceDao){
this.itemDao = itemDao;
this.itemPriceDao = itemPriceDao;
}
}
아래 코드는 Field Injection 방식으로 의존성을 주입하는 코드입니다.
public Class ItemService{
@Autowired
private final ItemDao itemDao;
@Autowired
private final ItemPriceDao itemPriceDao;
}
Field Injection 방식이 코드도 더 짧고 깔끔해보이는데 왜 Constructor Injection 방식을 권장하는 것 일까요? 그 이유를 봐보겠습니다.
[단일 책임의 원칙 위반]
의존성을 주입하기가 쉽습니다. @Autowired 선언 아래 수십개를 추가할 수 있습니다. 여기서 Constructor Injection을 사용하면 다른 Injection 타입에 비해 위기감 같은 걸 느끼게 해줍니다. Constructor의 파라미터가 많아짐과 동시에 하나의 클래스가 많은 책임을 떠안는다는 걸 알게됩니다. 이때 이러한 징조들이 리팩토링을 해야한다는 신호가 될 수 있습니다.
[의존성이 숨는다]
DI(Dependency Injection) 컨테이너를 사용한다는 것은 클래스가 자신의 의존성만 책임진다는게 아닙니다. 제공된 의존성 또한 책임집니다. 그래서 클래스가 어떤 의존성을 책임지지 않을 때, 메서드나 생성자를 통해(Setter나 Contructor) 확실히 커뮤니케이션이 되어야합니다. 하지만 Field Injection은 숨은 의존성만 제공해줍니다.
[DI 컨테이너의 결합성과 테스트 용이성]
DI 프레임워크의 핵심 아이디어는 관리되는 클래스가 DI 컨테이너에 의존성이 없어야 합니다. 즉, 필요한 의존성을 전달하면 독립적으로 인스턴스화 할 수 있는 단순 POJO여야합니다. DI 컨테이너 없이도 유닛테스트에서 인스턴스화 시킬 수 있고, 각각 나누어서 테스트도 할 수 있습니다. 컨테이너의 결합성이 없다면 관리하거나 관리하지 않는 클래스를 사용할 수 있고, 심지어 다른 DI 컨테이너로 전환할 수 있습니다. 하지만, Field Injection을 사용하면 필요한 의존성을 가진 클래스를 곧바로 인스턴스화 시킬 수 없습니다.
[불변성(Immutability)]
Constructor Injection과 다르게 Field Injection은 final을 선언할 수 없습니다. 그래서 객체가 변할 수 있습니다.
[순환 의존성]
Constructor Injection에서 순환 의존성을 가질 경우 BeanCurrentlyCreationExeption을 발생시킴으로써 순환 의존성을 알 수 있습니다.
- 순환 의존성이란? First Class가 Second Class를 참조하는데 Second Class가 다시 First Class를 참조할 경우 혹은 First Class가 Second Class를 참조하고, Second Class가 Third Class를 참조하고 Third Class가 First Class를 참조하는 경우 이를 순환 의존성이라고 부릅니다.
[Constructor Injection]
Constructor Injection은 필수적인 의존성 주입에 유용합니다. 게다가 final을 선언할 수 있으므로 객체가 불변하도록 할 수 있습니다. 스프링 4.3버전부터는 클래스를 완벽하게 DI 프레임워크로부터 분리할 수 있습니다. 단일 생성자에 한해 @Autowired를 붙이지 않아도 됩니다. 이러한 장점들 때문에 스프링 4.x 다큐멘테이션에서는 더이상 Setter Injection이 아닌 Constructor Injection을 권장합니다. Setter Injection을 사용한다면, 합리적인 디폴트를 부여할 수 있고 선택적인 의존성을 사용할 때만 사용해야한다고 말합니다. 그렇지 않으면 not-null 체크를 의존성을 사용하는 모든 코드에 구현해야합니다.
REFERENCE
https://zorba91.tistory.com/238
'Spring Boot' 카테고리의 다른 글
[Spring Boot] Swagger-UI 적용 (0) | 2020.05.31 |
---|---|
[Spring Boot] JUnit을 활용한 테스트 코드 작성(1) (0) | 2020.05.03 |
[Spring Boot] 타임리프(Thymeleaf) 엔진 (0) | 2020.03.04 |
[Spring Boot] 스프링부트 jenkins, docker, github사용하여 배포 (0) | 2020.03.01 |
[Spring Boot] 인텔리제이(intellij) lombok 설정 (0) | 2020.02.03 |