Spring Framework Triangle이라 불리는 스프링 프레임워크 핵심 3요소 IoC(역전 제어), AOP(관심 지향 프로그래밍), PSA(서비스 추상화)를 다룰 예정입니다. 그 전에 DI(의존성 주입)라는 사전 개념이 수반되어야 합니다. 그래서 이번 포스팅에서는 DI에 대해 알아보고 다음 포스팅에서 Spring Framework Triangle을 알아보겠습니다.
- Spring Framework에서 의존성을 주입받는 방법(DI) 3가지
- (예정)Spring Framework의 핵심 3요소 - IoC(DI), AOP, PSA
DI / Dependency Injection - 의존성 주입
A가 B에 의존한다는 의미는 B가 어떠한 이유로 변경이 발생하면 그 영향이 A에 미친다는 것입니다. 그래서 클래스들간에 직접적인 의존 관계를 맺는 것보다 느슨하게 인터페이스를 통해 의존관계 맺어, 결합도를 낮추는 것이 중요합니다. 이로써 재사용이 가능하고 확장 가능성 있는 객체를 만들어 둠으로써 모듈 간의 결합도를 낮출 수 있습니다. 이때 필요한 의존 관계는 컨테이너가 책임을 갖고 외부에서 동적으로 설정해줍니다.
- 의존성 주입의 장점1
- 의존 관계 설정이 컴파일시가 아닌 실행시에 이루어져 모듈들간의 결합도를 낮출 수 있다.
- 코드 재사용을 높여서 작성된 모듈을 여러 곳에서 소스코드의 수정 없이 사용할 수 있다.
- 모의 객체 등을 이용한 단위 테스트의 편의성을 높여준다.
의존성 주입 방법은 3가지가 있습니다. 생성자 주입, 멤버 필드 주입, Setter 메서드 주입이 있습니다.
각 방법에 대한 예제를 먼저 보고 설명을 이어가겠습니다.
/**
* Constructor Injection
*/
@Controller
public class BookController {
private final BookRepository bookRepository;
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
/**
* Field Injection
*/
@Controller
public class BookController {
@Autowired
private BookRepository bookRepository;
}
/**
* Setter Injection
*/
@Controller
public class BookController {
private BookRepository bookRepository;
@Autowired
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
Constructor-Based Dependency Injection
생성자 주입은 생성자에 의존성 주입을 받고자 하는 field를 나열하는 방법으로, 권고되는 방법의 하나 입니다.
- 장점
필수적으로 사용해야 하는 레퍼런스 없이는 인스턴스를 만들지 못하도록 강제함
- Spring 4.3 이상부터는 생성자가 하나인 경우 @Autowired 를 사용하지 않아도 됨
- Circular Dependency / 순환 참조2 의존성을 알아 차릴 수 있음
- 생성자에 점차 많은 의존성이 추가 될 경우 리패토링 시점을 감지 할 수 있음
- 의존성 주입 대상 필드를 final로 불변 객체 선언할 수 있음
- 테스트 코드 작성시 생성자를 통해 의존성 주입이 용이함
- 단점
- 어쩔 수 없는 순환 참조는 생성자 주입으로 해결하기 어려움
- 이 경우에는 나머지 주입 방법을 사용하도록 하나
- 가급적이면 순환 참조가 발생하지 않도록 하는 것이 더 중요
- 어쩔 수 없는 순환 참조는 생성자 주입으로 해결하기 어려움
Field-Based Dependency Injection
member field에 @Autowired annotation을 선언하여 주입받는 방법입니다.
- 장점
- 가장 간단한 선언 방식
- 단점
의존 관계가 눈에 잘 보이지 않아 추상적이고, 이로 인해 의존성 관계가 과도하게 복잡해질 수 있음
- 반대로 Constructor injection과 Setter injection은 의존성을 명확하게 커뮤니케이션 함
- 이는 SRP / 단일 책임 원칙에 반하는 안티패턴
- DI Container와 강한 결합을 가져 외부 사용이 용이하지 않음
단위 테스트시 의존성 주입이 용이하지 않음
- 의존성 주입 대상 필드가 final 선언 불가
Setter-Based Dependency Injection
setter 메서드에 @Autowired annotation을 선언하여 주입받는 방법입니다.
- 장점
- 의존성이 선택적으로 필요한 경우에 사용
생성자에 모든 의존성을 기술하면 과도하게 복잡해질 수 있는 것을 선택적으로 나눠 주입 할 수 있게 부담을 덜어줌
- 생성자 주입 방법과 Setter 주입 방법을 적절하게 상황에 맞게 분배하여 사용
- 단점
- 의존성 주입 대상 필드가 final 선언 불가
마무리
필드 주입 방법은 deprecated 이므로 이를 제외한 생성자 주입과 setter 주입 방법 중에서 적절히 상황에 맞게 사용하면 좋습니다.
끝으로 마틴 파울러의 Inversion of Control Containers and the Dependency Injection pattern 글은 다음 포스팅에서 다룰 IoC와도 연관이 되지만 DI를 한 번 정리하는데 많은 도움이 되므로 같이 읽어보시면 더 좋습니다.