본문 바로가기
Frameworks/Springboot

[Springboot] 의존관계 자동 주입 방법 정리

by 젊은오리 2022. 7. 28.
728x90

[김영한님의 스프링 핵심 원리-기본편을 학습 후 정리한 내용입니다.]

우리는 이전에 Appconfig이라는 설정클래스를 하나 정의해서 @Configuration, @Bean을 통해서 직접 빈 등록을 했다. 이렇게 빈등록과 의존관계 설정을 수동으로 하는 방법도 있지만, 설정파일을 써내려가기에는 현실적으로 너무 복잡할 뿐 아니라 귀찮다,, 따라서 스프링이 지원하는 의존관계 자동주입에 대해서 알아보자. 

1. 생성자주입 

생성자를 통해서 의존관계를 주입하는 방법이다. 아래 코드를 보면 OrderServiceImpl객체를 new해서 생성할 때 memberRepository와 DiscountPolicy가 주입되는 것을 볼 수 있다.

사실 실무에서는 몇몇의 케이스를 제외하고는 대부분 운영중간에 의존관계를 바꿀 일이 없다고 한다. 따라서 생성자 호출시에 딱 1번만 호출되는 것이 보장되는 생성자주입을 많이 사용한다.

@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired // 생략가능
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

※ 참고>> 생성자가 딱 1개만 있으면 @Autowired를 생략할 수 있다.

 

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

setter를 통해서 의존관계를 주입하는 방법이다. 

@Component
public class OrderServiceImpl implements OrderService{

    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

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

 

3. 필드주입

필드에 바로 주입하는 방법이다.

아래와 같이 코드가 간결하다는 장점이 있지만 외부에서 변경이 불가능하기 때문에 테스트를 하기 힘들다는 단점이 있다.

@Component
public class OrderServiceImpl implements OrderService{

    @Autowired MemberRepository memberRepository;
    @Autowired DiscountPolicy discountPolicy;

 

그래서 뭐가 좋은데?

과거에는 setter주입과 필드주입을 많이 사용했지만, 최근에는 스프링을 포함한 DI프레임워크 대부분이 생성자 주입을 권장한다. 그이유는, 앞에서도 말했지만 의존관계 주입은 한번 만들어지면 종료시점까지 변경할 일이 거의 없기 때문에 생성자 주입이 가장 안전한 방법이다. 또한 setter주입을 사용하면, setter메서드를 public으로 해둬야 하는데, 이는 변경하면 안되는 변수들에 대해서 실수로 변경을 할 가능성이 있기 때문에 좋은 설계가 아니다. 따라서 객체생성시에 딱 1번만 호출되는 생성자를 통해서 주입하는 것이 가장 좋은 방법이다. 그런데 생성자 주입시에는 주입해주는 객체들마다 다 저장해줘야해서 약간의 귀찮음?이 있을 수 있다. 이는 롬복을 사용해서 해결할 수 있다.

롬복라이브러리를 이용하면 아래와 같이 @RequiredArgsConstructor를 사용해서 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다. 

[롬복 사용 전]

@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired // 생략가능
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

 

[롬복 사용 후]

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

 

자동, 수동 중에 어떤 주입방법을 사용하는게 좋을까?

사실 개발자 입장에서 스프링 빈을 등록하는데 있어서 @Component 하나 넣어주면 끝나는 일을 @Configuration이 붙은 설정정보에서 @Bean을 일일이 적어주는 과정은 매우 번거로운 일이다. 또한 스프링이 알아서 OCP, DIP를 지키면서 자동으로 빈 등록을 해주기 때문에 자동 기능을 쓰지 않을 이유가 전혀 없다. 

그렇다고 자동기능만을 쓰라는 말은 아니다. 수동 빈 등록은 기술 지원 로직들을 구현할 때 적극 사용하는 것이 좋다. 웹 어플리케이션의 핵심 비즈니스 로직에 관련된 "업무 로직"이 아닌,, 그 밑단에 업무로직을 지원하기 위해 사용되는 AOP, DB연결등 과같은 하부기술을 구현하는 로직을  기술 지원 로직이라고 한다. 기술 지원 로직들은 애플리케이션 전반에 걸쳐서 광범위하게 영향을 미치기 때문에 문제가 발생하면 명확하게 어디에서 발생한지가 애매한 경우가 있다. 그래서 문제가 발생했을 때를 대비해서 애초에 기술지원로직들은 수동으로 빈을 등록해서 가시적으로 명확하게 드러내는 것이 좋다.(유지보수에 좋다.)

728x90

댓글