프레임워크 vs 라이브러리
둘의 차이는 제어의 흐름을 누가 갖고 있느냐? 이다
프레임워크는 사용자의 코드가 프레임워크의 흐름에서 필요할 때 불러 사용하고
라이브러리는 사용자의 코드 흐름에 맞춰 사용자가 필요할 때 라이브러리를 불러 사용한다.
스프링은 프레임워크이고, 개발자가 클래스를 짜놓으면 스프링이 필요할 때 객체 생성, 초기화, 사용, 소멸(라이프사이클)을 관리한다.
이렇게 제어의 흐름을 개발자가 아닌 프레임워크가 갖는 설계 원칙을 IoC(Inversion of Control)라고한다.
IoC Container

스프링에서 사용하는 BeanFactory를 상속받은 ApplicationContext는 스프링이 IoC 기능을 위해 객체상태를 관리하는 컨테이너다
어떤 요청이 들어와서 어떤 객체가 컨테이너에 올라와있다면 해당 객체를 사용하고, 없으면 만들어서 컨테이너에 올려준다.
컨테이너에서 관리하는 객체(클래스의 인스턴스)를 빈이라고 하며, @Bean, @Component 등의 어노테이션을 이용하면 설정 메타데이터를 스프링에서 파악해 필요할 때 컨테이너에 올려주고, 사용한다.
DI(Dependency Injection)
의존관계 주입이라고 하며 스프링을 사용해봤다면 @Autowried 어노테이션이 제공하는 기능이다.
의존성을 외부로부터 주입받아 사용하는 클래스에서는 객체의 생성은 책임지지 않고, 해당 클래스의 로직에만 집중할 수 있게한다.
DI + DIP
객체를 주입받을 때 추상화에 의존(Dependency Inversion Principle)하면 주입받는 객체가 어떻게 작동하는지에 대한 결합도를 낮추고 유연성을 높이는 설계가 가능해 주로 같이 사용한다.
다음과 같은 타이어 클래스들이 있다고 예시를 들어보겠다.
class Tire{
String name;
Tire(String name){
this.name = name;
}
}
class HankookTire extends Tire{
HankookTire(){
super("hankook");
}
}
No DI
Car 클래스가 Tire를 만드는 것까지 책임지는 의존관계 주입이 적용되지 않은 상태이다.
class Car{
HankookTire tire;
buy(){
tire = new HankookTire();
}
}
DI
이렇게 구체화 된 객체를 주입받아도 DI이지만
class Car{
HankookTire tire;
buy(Hankook tire){
this.tire = tire;
}
}
DI + DIP
보통은 추상화에 의존하는 Tire로 받는 DIP를 세트로 사용한다.
class Car{
Tire tire;
buy(Tire tire){
this.tire = tire;
}
}