
스프링을 처음 배울 당시 의존성 주입이라는 개념을 알고부터 본인은 @Autowired어노테이션만 있으면 온갖 서비스 클래스에서 메소드들을 막 가져다 쓸 수 있는 필드 인젝션의 매력에 흠뻑 빠졌었다.
2년 전 가을쯤이었나 하루는 커피숍에서 커피를 마시고 있는데 좀 떨어진 자리에서 개발자로 추정되는 인물들이 대화하는 내용이 들렸다. 대충 들어보니 '우리 회사 부장님이 독립성이 어쩌니까 골뱅이 오토 그거 쓰지 말라고 했는데 짜증난다.'라는 내용이었다.
그때 좀 생각해보고 의존성 주입에 대한 이야기라는 걸 알았고, 그때부터 필드인젝션의 사용을 줄이긴 했지만...그래도 아직 기한에 쫓기다보면 가끔 필드 주입 방식을 사용하기도 한다.
그렇다면 왜 필드 주입 방식이 지양되고, 생성자 주입 방식이 권장되는지 알아보도록 하자.
의존성을 주입할 때는 보통 3가지 방법중 하나를 택하게 되는데 생성자 주입, 수정자 주입, 그리고 마지막으로 필드 주입이 있다. 세가지 주입 방법에 대해 간단히 확인 해 보고 가자.
@Controller
public class Controller {
// final로 선언 가능
private final Service service;
@Autowired
public Controller(Service service) {
this.service = service;
}
}
생성자 주입 입니다. 말그대로 생성자를 통해 의존성을 주입 받는다.
@Controller
public class Controller {
private Service service;
@Autowired
public void setService(Service service) {
this.service = service;
}
}
수정자 주입은 생성자 주입과 비슷 하게 생겼지만, 생성자가 아닌 setter 메서드를 통해 주입받는다는 차이가 있다.
@Controller
public class Controller {
@Autowired
private Service service;
}
필드 인젝션은 단순하게 주입받을 객체에 @Autowired 어노테이션만을 붙이면 끝이다.
Spring Framwork reference에서 권장하는 방법은 생성자를 통한 주입 방식이다. @Autowired 어노테이션만으로 간단하게 의존성을 주입할 수 있는데 왜 생성자 주입 방법을 권장하는걸까? 필드 주입이나 수정자 주입과 다르게 생성자 주입 방법이 주는 장점에 대해 알아보도록 하자.
생성자로 의존성을 주입할 때 final로 선언할 수 있고, 이로인해 런타임에서 의존성을 주입받는 객체가 변하지 않는다.
수정자 주입이나 필드 주입을 이용하게되면 불필요하게 수정의 가능성을 열어두게 되고,
이는 객체지향설계의 5가지 원칙 중 OCP(Open-Closed Principal, 개방-폐쇄의 원칙)를 위반하게 된다.
그러므로 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 좋다.
또한, 필드 주입 방식은 null이 만들어질 가능성이 있는데, final로 선언한 생성자 주입 방식은 null이 불가능하다.
스프링 프레임워크를 사용함으로서 얻는 가장 큰 이점은 스프링의 IOC 를 쉽게 이용 할 수 있다는 점이다.
스프링의 DI 컨테이너는 의존하는 bean 간에 느슨한 결합을 제공해준다, @Autowired를 이용한 필드 인젝션을 하면 스프링을 통해서만 의존성 주입이 가능하기 때문에 해당 Bean들이 스프링의 DI 컨테이너와의 강한 결합을 하게 된다.
모순적이게도 Spring DI container를 사용하는 이유와 정 반대인 상황에 직면한다.
생성자 주입을 사용하게 되면 테스트 코드를 좀 더 편리하게 작성할 수 있다.
독립적으로 인스턴스화가 가능한 POJO(Plain Old Java Object)를 사용하면, DI 컨테이너 없이도 의존성을 주입하여 사용할 수 있다.
이를 통해 코드 가독성이 높아지며, 유지보수가 용이하고 테스트의 격리성과 예측 가능성을 높일 수 있다는 장점이 생기게 된다.
위와 같은 이유로 필드 주입이나 수정자 주입 보다는 생성자 주입의 사용이 권장된다.
생성자 주입방식을 사용할 때 생성자를 일일이 만드는 것이 불편할 수 있지만 생성자를 자동으로 생성해주는 @RequiredArgsConstructor를 사용한다면 간단하게 생성자를 생성할 수 있다.
@Controller
@RequiredArgsConstructor
public class Controller {
private final Service service;
}