[스프링 Core] 도대체 DI(Dependency Injection) 란 무엇인가?
스프링의 핵심 3대 요소 AOP
, IOC
, DI
를 들어보신 적이 있을 것 입니다.
오늘은 DI
가 도대체 무엇인지 확실하게 설명해드리겠습니다…!!
그리고 스프링 컨테이너가 IoC
인 이유를 알아봅시다.
문제 인식 🤔
public class Driver {
private Avante avante;
public Driver() {
this.avante = new Avante();
}
public void drive() {
avante.start();
}
}
위 클래스는 2가지 문제점이 있습니다.
- 두 클래스가 강하게 결합
- 객체들간의 관계가 아니라 클래스간의 관계가 맺어짐
두 클래스가 강하게 결합
Driver
클래스는 Avante
클래스와 강하게 결합되어 있는 문제점이 있습니다.
이말은 만약에 Avante
가 아닌 Model3
를 소유하게 된다면 Driver
클래스의 생성자 변경이 필요합니다.
클래스 간의 관계가 맺어짐
올바른 객체지향적 설계라면 객체들 간에 관계가 맺어져야 합니다.
객체들간에 관계가 맺어졌다면, Driver
가 Avante
인지 Model3
인지
신경쓰지 않고, drive()
를 호출 할 수 있어야 합니다.
DI는 디자인 패턴이다. 🎨
이러한 문제를 해결하기 위해 DI
가 등장했습니다.DI
를 사용하기 위해서는 다형성이 필요합니다.
Avante
, Model3
를 하나로 표현하는 추상화 작업(인터페이스)이 필요합니다.
DI
란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴 입니다.
두 객체 사이에 인터페이스를 두어서 클래스 레벨에서는 의존관계가 고정되지 않도록 합니다.
두 객체 간에 🏃♂️ 런타임 시에 관계를 동적으로 주입하여 느슨한 결합을 할 수 있습니다.
이게 무슨말인지 이해가 안될 수 있기 때문에 코드로 보여드리겠습니다.
Car
public interface Car {
void start();
}
Avante
public class Avante implements Car {
@Override
public void start() {
System.out.println("아반떼에 시동을 걸었습니다.");
}
}
Model3
public class Model3 implements Car {
@Override
public void start() {
System.out.println("모델3에 시동을 걸었습니다.");
}
}
이런식으로 설계하면,
우리는 이제 Driver
클래스에서 강하게 결합되어 있는 Avante
를 Car
로 대체(추상화)할 수 있습니다.
강한 결합을 제거하기 위해 외부에서 Car
를 주입받습니다.
Driver
public class Driver {
private Car car;
public Driver(Car car) {
this.car = car;
}
public void drive() {
car.start();
}
}
이것이 바로 DI 입니다.
Dirver
는 Car
라는 인터페이스를 의존하기 때문에Car
의 구현체인 Avante
, Model3
중 무엇이 사용되는지 신경쓰지 않고 역할을 다할 수 있습니다.
스프링에서 DI
이러한 역할을 스프링 프레임워크가 완벽하게 지원을 해줍니다.
스프링 애플리케이션을 실행하는 시점에 빈(Bean
)으로 등록된 객체를 생성하고, 의존성에 맞게 알아서 주입해줍니다.
public class BeanFactory {
public void driver() {
// Bean 생성
Avante avante = new Avante();
// 의존성 주입
Driver driver = new Driver(avante);
}
}
스프링은 특정 위치로 부터 클래스를 탐색하고, 객체를 생성하여 객체 관계까지 설정해 줍니다.
이러한 이유로 스프링은 DI 컨테이너라고 불립니다.
그리고 이러한 개념은 더 나아가 IoC(Inversion of Control
)라고 불립니다.
어떠한 객체를 사용할지에 대한 책임은 스프링에게 넘어갔고, 개발자는 수동적으로 주입받는 객체를 사용하기 때문입니다.!!! 👍
DI
를 사용하면
자연스럽게 DIP
(의존성 역전 원칙)을 지킬 수 있습니다.
DIP?
객체에서 어떤 Class 참조할 때
Class를 직접 참조하지 말고
Class의 상위 요소(추상 클래스 or 인터페이스)를 참조하라는 원칙
정리
운전면허가 있는 여러분은 자동차 모델에 신경쓰지 않고 운전을 할 수 있습니다.
자동차 모델에 따라서 운전방법이 달라지면 안됩니다.
그렇게 되면 자동차 모델에 따라서 운전하는 모든 방법을 알아야 합니다.
이처럼 Driver
클래스가 특정한 구현 클래스(Avante
, Model3
)에 의존한다고 해서 역할이 변경되면 안됩니다.
객체지향의 다형성을 활용하여
역할(인터페이스)과 구현(인터페이스를 구현)을 명확하게 분리 하자!!
DI의 이점
- 두 객체 간의 결합도를 낮춘다.
- 객체의 유연성이 올라간다.
- 테스트 작성이 용이함.
- 자동체 모델에 신경쓰지 않고
drive()
할 수 있기 때문
- 자동체 모델에 신경쓰지 않고
DIP
를 준수하게 된다.
참고
'0+ 스프링 > 0+ 스프링 Core' 카테고리의 다른 글
[스프링 Core] 스프링 빈 초기화(@PostConstruct, @PreDestroy) (0) | 2023.06.08 |
---|---|
[스프링 Core] @Configuration를 사용하면 싱글톤을 유지할 수 있는 이유(CGLIB) (0) | 2023.06.02 |