카테고리 없음

IoC와 DI

k-hamin 2024. 8. 20. 15:47

IoC(Inversion of Control)

제어의 역전에 대해 알아보자.

 

내가 차를 몰고 가는 것은 내가 차를 제어하는 것이고, 

직접 차를 운전하는 대신 운전기사를 고용한다면 이것을 제어의 역전이라고 한다.

 

 

핵심 아이디어는 "어떻게"가 아닌 "누가" 객체의 생성 및 관리 책임을 가지느냐에 있다.

누구에 의해 객체가 생성되고 관리되는지 초점을 맞추자

 

 

 

코드를 통해 알아보자-..

public interface Tire {
	String getBrand();
}
public class KoreaTire implements Tire{
	@Override
	public String getBrand() {
		return "Korea";
	}
}
public class China implements Tire{
	@Override
	public String getBrand() {
		return "China";
	}
}

 

위처럼 Tire 인터페이스를 구현한 KoreaTire, ChinaTire 클래스가 있다고 하자.

public class Car {
	Tire tire;
	public Car() {
		tire = new KoreaTire();
	}
	public String getTireBrand() {
		return "타이어 : "+tire.getBrand();
	}
}

 

Car 클래스는 Tire을 포함한다.

 

만약, Tire를 KoreaTire가 아닌 chinaTire로 바꾸고 싶다면 어떻게 해야 할까?

 

내가 직접 tire에 다른 타이어 클래스를 넣어주어야 한다.

 

이처럼 Car가 Tire 인터페이스 구현체를 선택하는 상황을 제어권을 가지고 있다고 한다.

 

이것을 CarTire의 서브 클래스 의존한다고 표현한다.


 

그럼 어떻게 제어의 흐름을 역전시킬 수 있을까?

여기서 말하는 제어의 역전이 무엇일까?

 

제어의 역전은 개발자가 직접 객체를 제어하는 것이 아닌 객체에 대한 제어권을 컨테이너로 역전하는 것이다.

 

이때, 개발자가 직접 객체를 제어한다는 것은

new 연산자를 통해 객체를 생성하고, 객체의 의존성을 맺어주고, 초기화해 주는 등을 말한다.

 

제어의 역전 x

 

1. 개발자 혹시 특정 코드가 직접 객체를 생성하고

2. 해당 객체의 생명 주기 및 다른 관련 동작들은 명시적으로 제어하며

3. 객체 간의 관계나 의존성은 코드 내에 직접 정의

 

제어의 역전 o

 

1. 프레임워크가 객체 생성 및 생명 주기 관리하고

2. 개발자는 단지 필요한 인터페이스, 규약을 따르며 구현한다.

3. 객체 간의 의존성은 외부의 정의되며, 프레임워크가 이러한 의존성을 주입한다.

 

제어의 역전을 만들어보자

public class AppConfig {
	public Tire tire() {
		Tire tire = new ChinaTire();
		return tire;
	}
	public Car car() {
		Car car = new Car(tire());
		return car;
	}	
}

위와 같이 Tire 인터페이스의 구현체를 결정해 주는 AppConfig 클래스를 생성해 주면 된다. 

Tire를 제어하는 책임을 분리하는 것이다.

public class Car {
	Tire tire;
	public Car(Tire tire) {
		this.tire = tire
	}
	public String getTireBrand() {
		return "장착된 타이어 : "+tire.getBrand();
	}
}

이제 Car 클래스는 Tire에 어떤 구현 클래스가 들어오는지 상관 쓰지 않아도 된다.

 

이를 통해 Car클래스는 단일 책임 원칙 SRP가 지켜졌다.

또한, Tire 구현체가 들어와도 상관없으니 확장에는 열려있고 변경에는 닫혀있는 원칙 OCP 도 지켜졌다.

그리고 Car는 구현클래스가 아닌 Tire 인터페이스라는 추상화에 의존하고 있으니 DIP도 지켜졌다.

 

이렇게 제어의 역전을 만드는 것이 IoC이다.

 

 

 

DI(Dependency Injection)

의존 관계 주입에 대해 알아보자

 

의존관계란,

 

B가 변경되었을 때 그 영향이 A에게 전달된다는 것이다.

Tire가 바뀌었을 때 Car에 영향이 가는 것처럼 말이다.

 

의존 관계에는 방향이 존재한다.

 

위는 A가 B에 의존하고 있고 그 반대는 성립하지 않는다.

 

즉, A가 변경되더라도 B는 영향을 받지 않는다.

 

DI란, 의존 객체와 그것을 사용하는 주체인 클라이언트 객체를 런타임시에 연결해 주는 작업이다.

 

코드를 통해 살펴보자...

public class Car {
	Tire tire;
	public Car(Tire tire) {
		this.tire = tire
	}
	public String getTireBrand() {
		return "장착된 타이어 : "+tire.getBrand();
	}
}

위 Car 클래스는 아직 tire의 구현체가 무엇인지 모른다

 

애플리케이션 실행 시,

public class AppConfig {
	public Tire tire() {
		Tire tire = new KoreaTire();
		return tire;
	}
	public Car car() {
		Car car = new Car(tire());
		return car;
	}
}

AppConfig의 car()을 호출할 시

Car의 생성자에 Tire 인터페이스의 구현체로 KoreaTire가 들어가게 된다.

이 것을 의존성 주입 DI라고 한다.


 

스프링 프레임 워크는 좋은 객체 지향 개발을 위해 IoC, PSA, AOP, POJO를 제공한다.

그럼 스프링은 어떻게 IoC와 DI를 구현하고 있을까?

...

30초 후에 공개

....