의존관계 자동 주입

권영태·2024년 1월 11일
0

스프링

목록 보기
18/18

출처 : 스프링 핵심 원리 - 기본편

의존관계 주입 방법

의존관계 주입 방법은 여러가지가 있다.

  • 생성자 주입
  • 수정자 주입(setter 주입)
  • 필드 주입
  • 일반 메서드 주입

1. 생성자 주입

제일 먼저 지난 시간까지 사용했던 생성자를 통한 의존관계 주입이다.
이름 그대로 생성자를 통해서 의존관계를 주입 받는 방법이고, 생성자 호출시점에 딱 1번만 호출되는 것이 보장되며, 주로 불변,필수 의존관계에 사용한다.

@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

@Autowired 애너테이션을 통해 생성자를 통해 의존관계를 주입한다고 명시해주면 생성자 주입 방식을 사용할 수 있다.
(생성자가 딱 1개만 있다면 @Autowired를 생략해도 자동 주입된다. 단, 스프링 빈에만 해당된다.)

2. 수정자 주입(setter 주입)

필드의 값을 변경하는 수정자 메서드를(ex. setter()) 통한 의존관계 주입 방식이다.
선택, 변경 가능성이 있는 의존관계에 사용하는게 좋다.

@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
    
    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy){
    	this.discountPolicy = discountPolicy;
    }

생성자 주입 방식은 빈 등록과 동시에 의존관계 주입도 한다.
반면, 수정자 주입 방식은 빈 등록 후 의존관계 주입이 이루어진다.

💡 스프링 라이프 사이클
1. 빈 전체 등록
2. 의존관계 주입(@Autowired)

옵션 처리

참고로 @Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생한다.
하지만 주입할 스프링 빈이 없어도 동작해야 할 때가 있다.
그럴 땐 아래 3가지 방식 중 상황에 맞게 채택해서 사용하면 된다.

  • @Autowired(required=false): 주입할 대상이 없으면 수정자 메서드 자체가 호출되지 않는다.
  • org.springframework.lang.@Nullable: 자동 주입할 대상이 없으면 null이 입력된다.
  • Optional<>: 자동 주입할 대상이 없으면 Optional.empty가 입력된다.

3. 필드 주입

말 그대로 필드에 바로 의존관계 주입하는 방법이다.
코드가 간결하다는 장점이 있지만, 외부에서 변경이 불가능해서 테스트 하기 힘든 치명적인 단점이 존재한다.
또한, DI 프레임워크가 없으면 아무것도 할 수 없으므로 되도록 사용하지 말자!

  • 애플리케이션의 실제 코드와 관계 없는 테스트 코드
  • 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용하는건 괜찮다.
@Component
public class OrderServiceImpl implements OrderService{

   @Autowired
   private final MemberRepository memberRepository;
   @Autowired
   private final DiscountPolicy discountPolicy;
}

4. 일반 메서드 주입

일반 메서드를 통해 의존관계 주입 받을 수 있으며, 한번에 여러 필드를 주입 받을 수 있다.
다만, 필드 주입과 같이 잘 사용하지 않는 방식이다.

@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public init(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

결론적으로, 상황에 맞게 생성자 주입 또는 수정자 주입 방식을 채택해서 사용하면 되지만 되도록 생성자 주입 방식을 채택하자.

최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입 방식을 권장한다고 한다.
생성자 주입을 권장하는 이유는 다음과 같다.

불변

  • 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계가 변경되선 안된다.
    즉, 불변해야 된다.
    만약 수정자 주입을 채택하면, 수정자 메서드를 public으로 열어두어야 한다.
    이후 누군가 실수로 수정자 메서드를 변경할 수도 있고, 잘못된 곳에서 호출할 수도 있다.
    그래서 수정자 주입 방식도 좋은 방식은 아니다.
    하지만 생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로, 이후에 호출될 일이 없어 불변하게 설계할 수 있다.

누락

  • 예로 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에서 다음과 같이 수정자 주입 방식인 경우가 있다.
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
    
    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy){
    	this.discountPolicy = discountPolicy;
    }
    
    ...
@Test
void createOrder(){
	OrderServiceImpl orderService = new OrderServiceImpl();
    orderService.createOrder(1L, "itemA", 10000);
} 

이렇게 테스트를 실행하면 실행은 되지만 NullPointException이 발생한다.
바로 memberRepositorydiscountPolicy 모두 의존관계 주입이 누락되었기 때문이다.

수정자 주입 방식이 아닌 생성자 주입 방식을 사용하면 주입 데이터를 누락 했을 때 컴파일 오류가 발생한다.

그리고 누락을 피할 수 있는 또 다른 방식도 있다. 바로 final이다.
생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다.
그래서 생성자에서 혹시라도 값이 설정되지 않은 오류를 컴파일 시점에서 막아준다.

이러한 이유들이 불변하고 누락을 피하기 쉬운 생성자 주입 방식을 사용하는것이 권고되는 이유이다.

정리

  • 생성자 주입 방식은 프레임웤드에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법 중 하나.
  • 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다.
    생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
  • 항상 생성자 주입을 선택하자!!

생각 정리

스프링을 처음 공부할 때 제일 편했던 필드 주입만 사용했던 적이 있다.
굳이 다른 생성자나 메서드들을 만들 필요가 없었기 때문이다. 하지만 요즘은 생성자 주입 방식을 주로 채택해 사용하고 있는데 제일 좋은 방식으로 코딩하고 있었던 내 자신이 뿌뜻해진다.
그래도 상황에 맞게 수정자 주입 방식도 사용해볼려는 노력을 해봐야겠다!

profile
GitHub : https://github.com/dudxo

0개의 댓글