Home 다양한 의존관계 주입
Post
Cancel

다양한 의존관계 주입

다양한 의존관계 주입

이전 포스팅에서 스프링의 컴포넌트 스캔의존관계 주입에 대해 알아 보았다. 스프링은 @Autowired 어노테이션을 통해 편리하게 의존관계를 주입 받을 수 있도록 기능을 제공한다. 이번 포스팅에서는 스프링이 지원하는 다양한 DI 방식을 알아보려 한다. 크게 4가지 방식이 있다.

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

생성자 주입

먼저 생성자 주입에 대해 알아보자. 이 방식은 이름 그대로 생성자를 통해서 의존관계를 주입 받는 방법이다.

1
2
3
4
5
6
7
8
9
@Component
public class MemberService {
    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

(생성자가 딱 1개만 있으면 @Autowired를 생략해도 된다.)

생성자 주입의 특징은 딱 1번만 호출되는 것이 보장 된다는 것이다. 그리고 final 키워드를 통해 불변 객체로 사용할 수 있다.

  • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장
  • 불변, 필수 의존관계에 사용

수정자 주입(setter 주입)

수정자 주입은 setter 메서드를 통해 의존관계를 주입받는 방법이다.

1
2
3
4
5
6
7
8
9
@Component
public class MemberService {
    private MemberRepository memberRepository;

    @Autowired
    public void setMemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

수정자 주입의 특징은 의존관계를 선택적으로 주입 받거나 로직 중간에 변경이 필요한 경우 사용할 수 있다.

  • 선택, 변경 가능성이 있는 의존관계에 사용

필드 주입

필드 주입은 생성자나 수정자 메서드 없이 필드에 바로 주입받는 방법이다.

1
2
3
4
5
@Component
public class MemberService {
    @Autowired
    private MemberRepository memberRepository;
}

코드가 간결하다는 특징이 있지만 이 경우 외부에서 의존관계를 변경할 수 없다는 단점이 존재한다. 그로인해 테스트가 어려워지는 치명적인 단점이 존재한다.

또한 DI 프레임워크가 없으면 의존관계를 주입받을 수 없는 단점도 존재한다. 이로 인해 필드 주입은 권장하지 않는다.

일반 메서드 주입

이 방식은 일반 메서드를 통해 주입을 받는 방식이다.

1
2
3
4
5
6
7
8
9
10
@Component
public class MemberService {

    private MemberRepository memberRepository;

    @Autowired
    public void init(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

한번에 여러 필드를 주입 받을 수 있다는 장점이 있지만 일반적으로 잘 사용하지 않는다.

Lombok을 활용한 생성자 주입

과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 최근 스프링을 포함한 대부분의 DI 프레임워크는 생성자 주입을 권장하고 있다. 이는 아래와 같은 특징들 때문이다.

우선 생성자 주입을 설명하며 불변이라는 단어를 사용하였다. 실제 애플리케이션을 개발하면 의존관계는 종료시점까지 변경되는 일이 거의 없다. 오히려 대부분은 종료시점까지 변경되면 안되는 경우가 많다.

그리고 수정자 메서드의 경우는 누군가 실수로 변경할 수 있기 때문에 이러한 setter 메서드를 열어두는 것은 좋지 않다.

결론적으로 의존관계를 불변하게 설계해야 하는데 생성자 주입은 딱 1번만 호출되는 것이 보장됨은 물론이고 불변하게 설계가 가능하다!

1
2
3
4
5
6
7
8
9
@Component
public class MemberService {
    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

최근 트렌드는 롬복을 사용하여 위의 코드를 아래와 같이 작성한다.

1
2
3
4
5
@Component
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository;
}

final 키워드가 있기 때문에 위 코드는 초기화를 위해 생성자를 필요로 한다. @RequiredArgsConstructor 어노테이션은 이렇게 final 키워드가 붙은 필드들을 초기화하는 생성자를 자동 생성해준다.

정리

최근에는 생성자를 딱 1개 두고, @Autowired를 생략하는 방법을 주로 사용한다.

여기에 @RequiredArgsConstructor를 통해 필요한 생성자를 자동 생성하여 코드를 간결하게 사용한다.

참고

This post is licensed under CC BY 4.0 by the author.

컴포넌트 스캔과 의존관계 주입

빈 생명주기(Bean Lifecycle)

Comments powered by Disqus.