DI - Dependency Injection
의존하는 클래스에 대한 인스턴스를 직접 생성하지 않고, 컨테이너로부터 생성된 빈을 setter나 생성자를 통해 외부로부터 주입받는 것을 의미한다. DI를 사용하면 결합도가 낮아진다.
그로 인해 코드 수정이 쉬워지며, 테스트 코드 작성도 쉬워진다. IoC를 실현하는데 DI가 필수이며 IoC를 사용하면 객체 생명 주기 같은 프로그램 제어권을 프레임워크가 관리하도록 넘길 수 있다. 스프링 프레임워크 버전 4.3 이후에는 @Autowired도 생략되면서 생성자에 사용한 Bean만 선언하면 된다.
직접적으로 클래스 내부에서 다른 객체를 생성하는 경우 결합도가 높은 코드가 되는데, 이후에 유지보수 측면에서 이런 코드는 적합하지 않은 코드이다.
스프링은 결합도는 낮고 응집도를 올릴 수 있는 기능을 제공하는데, 어노테이션을 통한 DI주입을 간편하게 만들어준다.
DI code
public static void main(String[] args) {
Person person = new Worker();
Company company = new CompanyImpl(person);
}
위의 예제는 DI(의존성 주입)의 예제이고, 생성자를 통해 외부에서 객체를 주입하고 있다.
말 자체는 어렵게 들릴 수 있지만, 위에서처럼 직접적으로 의존성을 만들지 않고, 외부에서 의존성을 가져오는 경우를 말합니다.
즉, 밖에서 나에게 의존성을 주입해주는 것을 DI(Dependency Injection) 라고 합니다. 따라서 DI는 IoC의 일종이라고 생각하시면 됩니다.
스프링 IoC 컨테이너가 관리하는 객체들을 Bean 이라고 부릅니다. 스프링은 이러한 Bean들의 의존성을 관리하고, 객체를 만들어 주며, Bean으로 등록을 해 주고, 이렇게 만들어진 것들을 관리합니다. 개발자가 이 부분까지 신경쓰지 않아도, 프레임워크가 알아서 해 주는 것입니다.
그리고 스프링 IoC 컨테이너가 위와 같은 관리를 해 줍니다. 이러한 Bean들을 담고 있는 스프링 IoC 컨테이너는 두 가지 중 하나를 사용합니다.
ApplicationContext는 BeanFactory를 상속받으므로 둘 다 같은 일을 하는 것임
이러한 스프링에서의 의존성 주입은 반드시 Bean으로 등록된 객체들 끼리만 가능합니다. 스프링 IoC 컨테이너는 Bean으로 등록되지 않은 객체에는 의존성 주입을 해 주지 않습니다.
그러면 특정 객체를 Bean으로 등록하는 방법은 무엇이 있을까요?
Pet Clinic 프로젝트에서 main 시작 부분을 보겠습니다.
@SpringBootApplication 어노테이션이 붙어 있는데, 이를 정의한 곳으로 가 보면..
@ComponentScan 어노테이션이 붙어있음을 확인할 수 있습니다.
즉, ComponentScan 은 어느 지점부터 컴포넌트를 찾아보라고 알려 주는 것인데, 이것을 사용한 SpringBootApplication 어노테이션이 PetClinicApplication 클래스에 붙어 있습니다.
따라서, 이 PetClinicApplication 클래스부터 하위 모든 패키지들을 훑어보면서 @Component 어노테이션을 활용한 모든 클래스들을 찾고, Bean으로 등록하게 됩니다.
예를 들어 OwnerController를 살펴볼까요? 앞서 IntelliJ에서 Bean으로 등록되었다는 표시를 확인할 수 있었는데요. @Controller 어노테이션이 붙어 있었습니다.
그러면 이 @Controller 를 정의한 곳으로 가 보겠습니다.
위처럼 @Component 어노테이션이 붙어있음을 알 수 있습니다.
따라서 스프링은 OwnerController가 @Component 어노테이션을 활용했으므로 이를 Bean으로 등록해 준 것입니다.