🧷 [Spring] 의존성 주입(DI) 이란?
위 포스팅 글에서 나는 의존성 주입을 공부하며 @Autowired
를 사용하는 방법을 배웠다.
그런데 프로젝트를 하다보니 많은 다른 글들에서 @Autowired
대신 @RequiredArgsConstructor
를 사용하는 것을 권장하는 게 아니겠는가.
내가 잘못된 방법으로 개발을 하고 있었던 걸까? 오늘은 @RequiredArgsConstructor
에 대해 알아보도록 하겠다.
기존에 의존성 주입을 할 때 나는 @Autowired
를 사용해 다음과 같은 코드를 짰다.
@Controller
public class TestController {
@Autowired
private TestService testService;
}
그런데 @Autowired
와 같은 필드 주입 방식은 권장되지 않고, 스프링 공식 문서를 보면 생성자를 통한 의존성 주입(DI)을 권장하고 있다.
Spring Team recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”.
그 이유는 @Autowired
(필드 주입)의 몇 가지 단점들 때문인데,
@Autowired
를 사용하면 클래스 간의 강한 결합으로 이어질 수 있고, 순환 참조가 일어날지도 모른다. 또한 final을 붙일 수 없어서 객체의 변경이 가능해 위험하다.
순환참조란, 둘 이상의 클래스나 빈(Bean)이 서로를 참조하는 상황을 의미한다.
순환참조가 발생하면, 객체 생성 시점에서 무한루프에 빠지게 되어 프로그램이 정상적으로 동작하지 않을 수 있다.@Service public class AService { @Autowired private BService bService; } @Service public class BService { @Autowired private AService aService; }
위에서 @Autowired
를 사용해 작성한 코드를 생성자 주입으로 변경해보자.
@Controller
public class TestController {
private final TestService testService;
// 생성자 주입
public TestController(TestService testService) {
this.testService = testService;
}
}
lombok에서 제공하는 @RequiredArgsContructor 어노테이션을 활용하면 선언만으로도 생성자 주입이 가능한 코드를 작성할 수 있다.
@Controller
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
}
필드 주입처럼 보이지만 @RequiredArgsConstructor
는 생성자로 의존성 주입을 한다.
(Lombok이 컴파일 타임에 자동으로 1 에서 쓰인 생성자 코드를 추가해 줌)
간단히 말해, 필드 주입의 단점을 보완한다고 생각하면 편하다.
순환 참조 방지 : 생성자 주입은 생성자에 필요한 인자를 먼저 확인한 후 인자에 해당하는 빈을 생성하기 때문에 순환 참조가 발생할 경우 여기서 막을 수 있다.
객체 변이 방지 : final 키워드 선언이 가능하기 때문에 객체가 생성될 때 의존성을 한번에 주입받으므로 객체의 불변성을 보장한다.
그 외에도 테스트 코드 작성에 용이하고, 코드에 악취를 제거한다는 장점이 있다.