저번 포스팅에서 언급했다시피 DI(Dependency Injection)란, 객체 간의 의존 관계를 개발자가 직접 코드 내에서 명시적으로 생성하고 관리하는 대신, 스프링 컨테이너가 객체를 생성하고 필요한 의존성을 주입해 주는 방식입니다
의존성 주입의 종류로는 필드 주입(Field Injection), 수정자 주입(Setter Injection), 생성자 주입(Constructor Injection) 방법이 있습니다.
이름 그대로 다음과 같이 필드에 바로 주입하는 방법입니다.
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private DiscountPolicy discountPolicy;
}
스프링 컨테이너 말고는 외부에서 주입할 수 있는 방법이 없어 순수한 자바 환경에서 테스트하기 힘들다는 치명적인 단점이 있습니다.
Setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법입니다.
@Service
public class OrderServiceImpl implements OrderService {
private DiscountPolicy discountPolicy;
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
의존관계 주입은 한번 일어나면 애플리케이션 종료 시점까지 의존관계를 변경할 일이 거의 없습니다.
하지만 수정자 주입은 메서드를 public으로 열어두어야 하기 때문에 누군가 실수로 변경할 수도 있고, 변경하면 안 되는 메서드를 열어두는 것은 좋은 설계 방법이 아닙니다.
@Test
void createOrder() {
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder("itemA", 10000);
}
위 코드에서 수정자 주입을 통해서 OrderServiceImpl
객체에 discountPolicy
를 주입해 주지 않아도 OrderServiceImpl
객체는 생성이 가능합니다. 하지만 생성된 객체로 createOrder
메서드를 호출하면 discountPolicy
를 주입받지 않았기 때문에, Null Pointer Exception이 발생하게 됩니다.
주입이 필요한 객체가 주입이 되지 않아도 얼마든지 객체를 생성할 수 있다는 것 또한 문제입니다.
다음과 같은 문제들을 해결할 수 있는 방법이 아래에서 설명할 생성자 주입입니다.
이름 그대로 생성자를 통해서 의존 관계를 주입 받는 방법입니다.
@Component
public class OrderServiceImpl implements OrderService {
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
1. 누락 방지
수정자 주입을 사용할 경우에는 주입 데이터를 누락했을 때, 런타임에 Null Pointer Exception이 발생했지만, 생성자 주입을 사용하면 다음처럼 주입 데이터를 누락했을 때 컴파일 오류가 발생합니다. 이는 개발자가 IDE를 통해 누락된 의존성을 쉽게 파악하고 수정할 수 있게 합니다.
@Test
void createOrder() {
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder("itemA", 10000);
}
2. final 키워드
생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있습니다. 위에서 필드 주입, 수정자 주입과는 반대로 생성자 주입은 생성자의 호출 시점에 1회 호출되는 것과 한 번 초기화된 후 변경되지 않음을 보장하기 때문에 주입된 객체가 이후에 변경되지 않음을 확실히 할 수 있습니다. 또한 객체의 주입이 필요한 경우에 강제하기 위해 사용할 수 있습니다.
3. 테스트 코드 작성의 편리함
스프링 컨테이너에서는 객체 생성 및 의존성 주입을 자동으로 처리할 수 있기 때문에 필드 주입이나 수정자 주입을 사용하는 경우 있지만, 순수 자바 코드 환경의 테스트에서는 이러한 기능을 사용할 수 없기 때문에, 객체 생성 시에 직접 의존 객체를 전달하는 생성자 주입 방식을 사용하는 것이 좋습니다.
항상 생성자 주입을 기본으로 선택하고, 필요에 따라 필수 값이 아닌 경우에는 수정자 주입을 옵션으로 사용하는 것이 좋습니다. 이는 객체의 불변성을 보장하고, 의존성 주입을 명확하게 하여 코드의 안정성과 가독성을 높입니다.
필드 주입은 사용하지 않는 것이 좋으며, 생성자 주입과 수정자 주입을 적절히 조합하여 필요한 의존성을 설정하는 것이 좋은 설계 방법입니다.