사내에서 node 서버를 java 로 전환하는 프로젝트가 곧 시작된다. 아직 java라는 언어도, spring 이라는 프레임워크도 익숙하지 않아 프로젝트를 시작 하기 전 잠깐 스터디를 해보려고 하는데… spring에 대해서 처음부터 하나하나 조사해 정리하는 건 재미없다.
그래서 spring의 핵심 기능을 찾아보았고 그중 하나가 DI(의존성 주입)라고 한다. 스프링에서 DI 하는 방법은 아래와 같이 총 3가지가 있다고 한다.
- 필드 주입
- 수정자 주입
- 생성자 주입
그런데 이 3가지 방식 중 생성자 주입 방식을 권장한다고 한다. (실제로 필드 주입을 사용할 경우 인텔리제이에서는 오류나 경고 메세지가 뜬다.)
왜 그럴까?
왜 그런지 알기 위해서는 @componentScan을 통해 빈을 생성하고 di 하는 방식을 살펴보아야 한다.
의존성 주입 절차
- bean들을 컨트롤할 bean factory (application context 이하 context라 부름) 생성
- context 를 위한 설정 값들을 읽고 파싱하여 셋팅
- class loader 객체를 생성하고, class loader 객체의 메소드를 사용해 모든 클래스들을 recursive하게 돌면서 bean으로 등록해야하는 클래스 리스트를 만듬 (bean class list)
- 생성한 bean class list를 for문 돌면서 각 bean 클래스 생성을 위한 메타 데이터(bean definition) 들을 정리하고, 해당 클래스의 필드에서 autowired 어노테이션이 붙은 필드 리스트 및 개수 등을 정리
- 4번에서 정리된 bean class들의 메타 데이터 리스트들을 bean factory 객체 필드에 set (bean factory는 bean 들의 총괄 매니저 같은 거니까 정보를 알고 있어야겠지!)
- 이제 메타 데이터 리스트들을 돌면서 메타 데이터를 참고해서 드디어 bean 객체들을 생성
- 모두 생성했다면 앞서 정리한 autowired 된 필드 리스트들을 돌면서 생성된 객체를 참조하도록 set
- 드디어 어플리케이션 로직 시작
생성자 주입 방식을 추천하는 이유
위의 '의존성 주입 절차' 에서 6번과 7,8번을 유심히 보자.
모든 bean들을 생성 한 후에 autowired로 어노테이션된 필드들에 앞서 생성된 bean 객체를 이어주고, 그 다음에 드디어 어플리케이션 로직이 시작된다.
그러다 보니 필드 주입이나 수정자 주입을 사용한 상황에서는 어플리케이션 로직상의 순환 참조되는 잘못된 설계를 파악하지 못한채로 로직 시작 전에 이미 빈을 생성해버렸기 때문에, 로직이 실행되고나서야 어플리케이션이 뻑(?) 나 버리는 것이다.
하지만 생성자 주입을 하게 되면 bean 객체를 막 생성하려는 시점에 아직 생성되지 않은 빈을 찾게 되면서 오류가 발생하기 때문에, 로직이 실행되기 전에 순환참조 같은 잘못된 설계를 사전 방지할수 있게 되는 것이다.