안녕하세요. 요즘 코로나로 이슈가 많습니다. 다들 몸관리 잘 하시기 바랍니다. 오늘은 이전 포스팅에 이어서 Spring의 핵심 기술중 DI와 IoC에 데해서 알아보도록 하겠습니다.
IoC (Inversion of Control)
IoC는 영어 그대로 해석하면 제어의 역전이라고 말합니다.
A라는 class가 B라는 클래스의 메서드를 사용하기로 했습니다. 그러면 아래와 같은 코드를 작성해야 합니다. 이런 관계를 A가 B를 의존하고 있다고 합니다. 왜냐하면 B의 로직에 따라 A가 영향을 받기 때문입니다.
public class A {
private B b;
public A() {
b = new B_1();
}
public void useB() {
b.method_1();
b.method_2();
}
}
interface B {
void method_1();
void method_2();
}
class B_1 {
public void method_1() {
System.out.println("B_1`s method_1");
}
public void method_2() {
System.out.println("B_1`s method_2");
}
}
class B_2 {
public void method_1() {
System.out.println("B_2`s method_1");
}
public void method_2() {
System.out.println("B_2`s method_2");
}
}
이렇게 코딩을 했을 경우 문제점은 어떤게 있을까요 ? 바로 A가 B를 관리하게 된다는 점입니다.
지금 A class는 본인이 가지고 있는 method의 로직만 신경쓰면 되는 게 아닙니다. 지금 A class는 B interface의 어떤 구현 클래스를 사용할지를 결정하는 기능을 맡고 있습니다. A class는 본인의 로직에 대한 책임뿐만 아니라 B class의 구현에 대한 책임마저 지고 있습니다. 이런 책임을 굳이 A class가 가지고 있을 필요가 있을까요? 그렇지 않습니다. 이렇게 의존관계에 대한 책임을 제 3자에게 위임하는 것을 IoC라고 합니다.
3자 class를 만들어 위임해보도록 하겠습니다. B class는 가만히 둘것이고 Factory라는 class를 만들고 Factory class에서 A는 B class의 정보를 읽어와 주입받는 방식입니다.
public class Factory {
public A a() {
return new A();
}
public B b() {
return new B_1();
// return new B_2();
}
}
public class A {
private B b;
public A() {
b = new Factory().b();
}
..이하 동일..
}
위 구성을 보시면 이제 Factory class에서 B에 대한 return을 받아 A의 맴버변수 B에 주입합니다. 현재는 B_1 class를 구현으로 사용하고 있으나 추후 변경된다면 class A는 변경할 필요가 없으며 Factory Class의 B에 대한 return 부분을 return new B_2()
로 변경하면 원하는 바를 이룰 수 있습니다. 이렇게 책임에 대한 분리가 이루어 지게 되었습니다.
장점
이렇게 IoC를 이용했을 때 장점은 무엇일까요?
객체지향적으로 Single Responsibility Principle을 지킬 수 있게 되었습니다. 즉, A class는 B의 의존관계를 선택하는 책임에서 벗어났습니다. B class의 변경에 대해서 A class는 변경이 필요없게 된 것입니다. 만약 B_1이 이제 사용되지 않고 B_2를 사용하게 된다면 우리는 Factory Class에서 변경하지 A class는 전혀 변경을 하지 않아도 됩니다.
DI(Dependency Injection)
위에서 살펴본 IoC의 경우 가장 기본적인 IoC의 구현입니다. Spring과 같은 Container에서는 생성과 관계설정, 사용, 생명주기 관리 등을 추가로 해줍니다. 이렇게 Spring에서 IoC 역할을 하는 모듈을 Spring 컨테이너, DI(Dependency Injection) 컨테이너라고 부르고 있습니다.
두 개의 클래스 또는 모듈이 의존관계에 있다고 말할 때는 항상 방향성이 존재합니다. 즉, 누가 누군가에게 의존하는 관계에 있다는 식이어야 한다는 것입니다.
위 UML 모델은 의존성을 표현한 것입니다. A는 B에 의존하고 있다는 표현입니다.
의존한다는 것은 B의 기능이 추가되거나 변경되거나, 형식이 바뀌거나 하면 그 영향이 A에게로 전달된다는 것입니다. 위의 IoC 예제처럼 A가 B를 사용할 때가 대표적인 예 입니다. 의존한다는 것은 방향성을 가지고 있습니다. A는 B에 의존하고 있지만 B는 A에 의존하고 있지 않습니다. 즉 B는 A의 변화에 영향을 받지 않는다는 뜻입니다.
그리고 의존성 주입(Dependency Injection)은 구체적인 의존 오브젝트와 그것을 사용할 주체, 클라이언트 오브젝트를 런타임 시에 연결해주는 작업을 말합니다.
이러한 의존성 주입(DI)에는 지켜야할 조건이 3가지 있습니다.
- 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그렇게 하기 위해서는 인터페이스에만 의존하고 있어야 한다.
- 런타임 시점의 의존관계는 팩토리나 컨테이너 같은 제 3의 존재(Factory 등)에 의해 결정되어야 한다.
- 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공해줌으로써 만들어진다.
1번의 반례의 경우를 보도록 하겠습니다. 아래 코드는 1번 조건을 위배한 경우입니다. 코드에 실제로 이용할 B의 구현 클래스가 노출되어있습니다. 이렇게 되면 이전 우리가 IoC에서 보았던 것처럼 강하게 결합되어있는 것입니다.
private B b;
public A() {
b = new B_1();
}
위 코드는 이미 설계 시점에 B_1 이라는 구체적인 클래스의 존재를 알고 있습니다. 이는 런타임 의존성를 A 클래스가 이미 알고 있다는 의미입니다. 그리고 이를 런타임 의존성이 드러나지 않도록 만든 코드는 DI를 만족시킨 코드입니다.
public class A {
private B b;
public A(B b) { // B_1 or B_2 injected from DI Container
this.b = b;
}
}
이렇게 생성자의 파라미터로 오브젝트의 레퍼런스를 전달하는 식으로 코드를 작성하면 A 클래스 입장에서 런타임시에 전달 받은 인자와 의존관계를 가지게 되는 것입니다.
이렇게 DI 컨테이너에 의해서 런타임 시에 의존 오브젝트를 사용할 수 있도록 그 래퍼런스를 전달받는 과정이 마치 메소드를 통해 DI 컨테이너가 A에게 주입해주는 것과 같다고 해서 이를 의존관계 주입이라고 부릅니다.
의존관계 주입의 핵심은 설계 시점에 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제3의 존재(Object Factory 등)가 있다는 것이다.
장점
DI Container를 사용하는 기법을 써서 코드를 구성하면 아래와 같은 장점을 가지게 됩니다.
코드에는 런타임 클래스에 대한 의존관계가 나타나지 않습니다. 그리고 인터페이스를 통해 결합도가 낮은 코드를 만들게 됩니다. 다른 책임을 가진 사용 의존관계에 있는 대상이 바뀌거나 변경되더라도 자신은 영향을 받지 않으며, 변경을 통한 다양한 확장 방법에 자유롭다는 게 DI의 장점입니다.
마무리
오늘은 이렇게 IoC의 개념과 Spring에서 사용하는 Container인 DI Container의 개념에 대해서 알아보았습니다.
이해가 잘 안되시는 부분이 있으시다면 망설이지 말고 덧글 달아주세요.
그리고 도움이 되셨다면 아래 광고한번 눌러주세요.
큰 도움이 됩니다.
감사합니다.
참조
많은 부분을 토비의 스프링을 참조하였습니다. 좀 더 자세하게 Spring의 핵심 요소를 공부하고 싶으신 분들은 해당 책을 강력 추천 드립니다. 해당 포스팅은 위 책의 1장. 오브젝트와 의존관계
와 8장. 스프링이란 무엇인가?
를 참조하였습니다.
토비의 스프링 3.1
'language, framework, library > Spring' 카테고리의 다른 글
[Spring] Spring AOP - 기본 이론편 (0) | 2020.06.20 |
---|---|
[Spring] Spring의 IoC/DI Container (0) | 2020.03.21 |
[Spring] Spring의 정의와 기본 개념 (2) | 2020.02.21 |
[Spring Boot] Spring Boot Actuator, Application을 모니터링 하자 (0) | 2019.09.14 |
[Spring Boot] Session과 Cache의 기본 저장소 ! (1) | 2019.09.10 |
댓글