프로젝트 개발 중에 스프링을 사용할 때 생성자 주입방식을 적극 지향하고 있는데 그 이유를 명확히 설명하기 어려웠다. 뿐더러 장점을 정확히 알고 사용했다기보다는 스프링이 권장하는 방식? 이라는 이유때문에 사용했는데 특징은 무엇인지 장점과 단점에 대해 명확히 구분하여 사용해야하는데 라는 당연한 생각이 들어 알아보았다. 생성자 주입 방식 외에도 2가지가 더 있기때문에 주입 방식들에 대해 비교하여 공부하고 잊지 않기 위해 포스팅해보려고 한다.

위의 그림은 Spring의 삼각형이라 불리며, Spring의 핵심 구조를 나타낸다.
이 중 의존성 주입을 알아 볼 것이다.
의존성 주입 (Dependency Injection)
의존성 주입은 스프링 프레임워크의 3가지 핵심 프로그래밍 중에 하나이며, 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴 중 하나이다. 필요한 객체를 직접 생성하는 것이 아닌 외부로부터 객체를 받아서 사용하는 것으로, 인터페이스를 사이에 두고 클래스에서는 의존 관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 결합도를 낮출 수 있게 하는 기법이다. 이를 통해서 객체간의 결합도를 낮추고 코드의 재활용성을 높일 수 있다.
// 예제 코드
public class UserController {
private UserService userService;
public UserController() {
userService = new UserService;
}
}
Spring에서는 의존성을 주입하는 방법이 세 가지가 있다. 아래의 세가지 주입 방법을 알아보자.
생성자 주입 (Constructor Injection)
- 객체를 생성할 때 생성자를 통해 의존 관계를 주입하는 방법이다.
- 생성자의 호출 시점에서 1회 호출 되는 것이 보장된다. 즉, 한번만 주입이 가능하다.
그렇기 때문에 주입받는 객체가 변하지 않을 경우나 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용한다. - 주입하려는 의존성 객체를 명시하기 때문에 의존 관계 객체를 명확히 알 수 있다.
- 주입 받을 객체를 final로 선언 가능하며, 생성자가 1개인 경우 @Autowried를 생략해도 자동 주입이 가능하다.
- 테스트 코드 작성에 쉽다.
왜? UserController의 테스트 코드를 작성해야 할 때, 테스트용 클래스를 생성해서 주입해주면 된다.
아래의 테스트코드 작성에 대한 예제 코드를 확인할 수 있다. - 하지만 의존성 주입 객체가 늘어날수록 복잡성도 함께 증가할 수 있다.
// 예제 코드
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
}
// 테스트코드 작성에 대한 예제 코드
@Test
void controller_test() {
UserController userController = new UserController(new TestService());
// ...
}
class TestService {
public TestService() {
super();
}
// ...
}
수정자 주입 (Setter injection)
- 필드에 Setter 메서드를 통해 의존 관계를 주입하는 방법이다.
- 의존 관계가 선택적이며, 객체 생성 후에 의존 관계를 변경 가능하다.
- @Autowired 어노테이션이 있는 수정자들을 자동으로 의존 관계를 주입한다.(@Autowired 생략 불가능)
- 하지만 @Autowired로 주입할 대상이 없는 경우에는 오류가 발생한다.
주입 할 대상이 없어도 동작하도록 하려면 @Autowired(required=false)를 통해 설정할 수 있다.
// 예제 코드
public class OrderController {
private final OrderService orderService;
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
필드 주입 (Field Injection)
- 필드에 @Autowired 어노테이션을 사용하여 의존 관계를 주입한다.
- 그래서 코드가 간결하고 읽기 쉽다.
- 하지만 직접 필드에 의존 관계를 주입하는 게 아닌 어노테이션을 통해 의존 관계를 주입하기 때문에
외부에서 변경이 불가능하다. - 스프링 프레임워크에게 주입받아 사용하기 때문에 순수 자바 코드로 테스트하기에 어렵다.
// 예제 코드
public class OrderController {
@Autowired
private final OrderService orderService;
}
이들의 차이점을 정리해보자.
필드 주입은 @Autowired 어노테이션을 통해 의존성을 주입하는 방식이다.
그래서 사용하면 편리하고 코드가 간결하다는 장점이 있다.
하지만 어노테이션으로 의존성을 주입하기 때문에 외부에서 변경이 쉽지 않다.
또한 어노테이션 사용을 남발하면 final 선언이 불가능하므로 런타임 시 의존성 주입이 변경될 수 있는 단점이 있다.
생성자 주입은 사용할 의존 객체를 생성자를 통해 주입하는 방식이다.
필요한 의존성을 명시하기 때문에 어떤 의존성이 필요한지 쉽게 파악이 가능하며, 파라미터만 수정하면 되서 유지보수가 쉽다. 또한 생성자주입은 객체를 생성하는 시점에서 빈을 주입하기때문에 순환참조를 발생시킬 수 있다.
의존성을 추가하다 보면 A>B 를 참조하면서, B>A를 참조하는 경우가 발생하여 무한 루프에 빠지게 되어 제대로 시작되지 않을 수 있다. 하지만 final 선언이 가능하여, final을 사용하여 런타임 시 처음 초기화 이후 재할당 할 수 없도록 함을 보장하여 순환 참조를 방지할 수 있다.
하지만 많은 의존성이 필요할 경우, 생성자의 길이가 길어질 수 있어 가독성이 떨어질 수 있는 단점이 있다.
이렇듯 스프링에서의 핵심 중 하나인 의존성 주입을 정리하며 생성자 주입에 대해서 그리고 외의 방법에 대해 알아보았다. 남들이 다 이렇게 쓰니까 또는 그렇길 권장하니까 라는 맹목적인 이유만을 가지고 개발에 임하지 않고 왜?? 라는 생각을 가지고 그 이유를 알고 사용해야 한다는 좋은 교훈을 다시 상기시키게 되는 계기가 되었다. 또한 프로젝트 개발하면서 문제가 발생하지 않으면 모를 수도 있어 이후에 수정에 번거로움이 일어날 수 있었을 내용을 다뤄 다른 분들께 도움이 조금이나마 되었길 바라며... 포스팅을 마친다.
[참고 및 자료]
[Spring] 생성자 주입 vs 필드 주입 vs 수정자 주입
의존성 주입; Dependency Injection Spring에서 의존성을 주입하는 방법은 3가지가 있다. 생성자 주입; Constructor Injection 필드 주입; Field Injection 수정자 주입; Setter Injection 결론부터 말하자면 '생성자 주입
yeonyeon.tistory.com
[Spring] 의존성 주입의 정의 및 의존성 주입 3가지 방식 (생성자 주입, 수정자 주입, 필드 주입)
안녕하세요. 개발자 Jindory입니다. 오늘은 의존성 주입에 대해서 정리하고 의존성 주입을하는 3가지 방법에 대해서 알아보는 글을 작성해보고자 합니다. # 글 작성 목적 Spring Framework의 특징인 의
jindory.tistory.com
'Spring' 카테고리의 다른 글
| Log4j & Logback & Log4j2의 차이 (0) | 2023.08.25 |
|---|---|
| Filter vs Interceptor vs AOP (0) | 2023.07.16 |
| Mapper(Mybatis 라이브러리)를 활용한 DB와의 통신 (0) | 2023.06.26 |
| Spring Bean Scope란? (0) | 2023.06.18 |
| Spring AOP vs AspectJ (0) | 2023.05.23 |