프로젝트에서 기능 구현을 위해 여러 클래스에서 @Autowired를 통해 필드주입을 사용했고
이러한 의존성 주입 코드를 다시 검토 과정에서 다음과 같은 의문점이 생겼다.
Spring boot에서 필드 주입을 사용하면 위와 같이 @Autowired에 밑줄이 가있는데 마우스를 올려보면
"Field injection is not recommended"라는 말과 함께 필드 주입은 추천하지 않는다고 알려준다.
왜 스프링 부트에서는 필드 주입을 추천하지 않는 것일까?
우선 이 해답을 얻기 위해 의존성 주입이란 무엇이고 의존성 주입 방법에 대해 알아야 했다.
Spring Di, IoC와 의존성 주입의 개념
스프링 DI(Dependency Injection), IoC(Inversion of control)
스프링을 공부하면서 의존성 주입(DI), 제어의 역전(IoC)과 같은 용어들을 자주 들을 수 있는데 실제 이를 적용하는 과정에서 정확한 의미를 알지 못하고 단순 구현에만 집중했었기 때문에 정확한
yoinberry.tistory.com
위 블로그에서 포스팅 한대로 의존성 주입 방법에는 필드 주입, 수정자(setter) 주입, 생성자 주입이 있다.
기존의 스프링 부트에서는 AppConfig라는 별도의 클래스를 생성해 클래스를 빈으로 등록하고 의존성 주입을 관리했지만
스프링 부트에서는 이러한 클래스를 생성하지 않아도 쉽게 설정이 가능하다. 그 이유를 간단하게 설명하자면 애플리케이션
이 시작됨과 동시에 @Component 어노테이션이 붙은 클래스들을 찾아 빈으로 등록해 주며 이후 의존성을 주입할 객체에
@Autowired 어노테이션을 이용해 등록되어 있는 빈을 조회하여 주입이 가능한 것이다. 이때 빈이 존재하지 않거나
같은 타입의 빈이 두 개 이상 존재할 경우 예외가 발생한다. 다음 예시를 통해 필드 주입과 생성자 주입의 방법을 알아보자.
Field Injection(필드 주입)
@Service
public class ItemService implements ItemServiceInter {
@Autowired
private ItemMapperInter inter;
@Override
public int getTotalCount() {
return inter.getTotalCount();
}
}
Constructor Injection(생성자 주입)
@Service
public class ItemService implements ItemServiceInter {
private final ItemMapperInter inter;
@Autowired
public ItemService(ItemMapperInter inter) {
this.inter = inter;
}
@Override
public int getTotalCount() {
return inter.getTotalCount();
}
}
* spring 4.3 버전 이후부터 생성자가 1개만 있을 경우 @Autowired 생략이 가능하다.
실제로 코드를 보면 필드 주입은 간결하기 때문에 사용하기 쉽지만 그만큼 다양한 문제점이 있다. 우선 필드 주입은
final 키워드를 사용하지 못해 객체의 불변성 확보가 어렵고 setter나 생성자를 통한 의존성 주입과 다르게 Spring에서
지원하는 의존성 주입 방식이기 때문에 Spring에 의존도가 높아 Spring DI 컨테이너 밖에서는 작동하기 어렵다.
이로 인해 별도의 테스트 코드 작성 시 어려움이 있다. 무엇보다 이 외 큰 문제가 되는 점은 순환 참조 문제이다.
순환 참조는 두 가지의 클래스가 서로 의존 관계를 가지며 서로 간의 호출을 반복하다 에러를 발생시킨다. 하지만 생성자
주입을 사용하면 이러한 순환 참조를 방지할 수 있는데 이는 의존성 주입 시 빈 주입 시점이 다르기 때문이다. 필드나
setter를 통한 의존성 주입은 의존성이 주입될 객체가 생성되지 않은 시점에 빈을 생성하여 참조하기 때문에 실제로
이 객체가 사용되기 전까지 순환 참조 문제를 알 수 없다. 반면에 생성자를 통한 의존성 주입은 객체 생성 시점에 빈을
먼저 조회하여 순환 참조 발생이 예상된다면 애플리케이션 구동 과정에서 예외를 발생시켜 사전에 방지할 수 있다.
추가적으로 생성자 주입 코드 작성 시 Lombok에서 제공하는 @RequiredArgsConstructor 어노테이션을 통해
더욱 간단한 코드 작성이 가능하다. @RequiredArgsConstructor는 final이 붙거나 @NotNull 이 붙은 필드의
생성자를 자동 생성해 준다. 예시 코드는 다음과 같다.
@Service
@RequiredArgsConstructor
public class ItemService implements ItemServiceInter {
private final ItemMapperInter inter;
@Override
public int getTotalCount() {
return inter.getTotalCount();
}
}
Reference
- https://jackjeong.tistory.com/entry/Spring-%EC%83%9D%EC%84%B1%EC%9E%90-%EC%A3%BC%EC%9E%85-vs-%ED%95%84%EB%93%9C-%EC%A3%BC%EC%9E%85-Autowired
- https://yaboong.github.io/spring/2019/08/29/why-field-injection-is-bad/
- https://wildeveloperetrain.tistory.com/139
- https://wildeveloperetrain.tistory.com/26
- https://mangkyu.tistory.com/125
- https://ahndding.tistory.com/9
'study > spring boot' 카테고리의 다른 글
[spring boot] static resource Cache-Control 설정 (0) | 2023.06.05 |
---|---|
[spring boot] Ajax을 활용한 비동기 서버 통신 (0) | 2023.05.29 |
[spring boot] Jsp 적용 과정 (패키징 배포 방식 JAR에서 WAR로 변경) (0) | 2023.04.21 |
스프링 @RestController를 통한 json 데이터 반환 (0) | 2023.04.12 |
스프링 DI(Dependency Injection), IoC(Inversion of control) (0) | 2023.04.11 |
댓글