이전까지 개발한 프로그램을 바탕으로 IoC와 DI, 컨테이너의 개념을 정리해본다.
제어의 역전 - IoC (Inversion of Control)
제어의 역전이란, 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 말한다.
기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 의존관계의 구현체를 직접 생성, 연결, 실행했었다.
이렇게 객체를 직접 생성 및 호출한다는 것은 프로그램의 제어 흐름을 직접 통제한다는 의미고, 개발자 입장에서는 이것이 자연스러운 흐름이었다.
하지만 AppConfig와 같은 구성 영역이 등장한 후로, 구현 객체는 자신의 로직을 실행하는 역할만 담당한다.
구현 객체에게 필요한 의존성은 모두 AppConfig와 같은 외부로부터 주입받는다.
OrderServiceImpl의 경우를 생각해보면, OrderServiceImpl은 자신이 사용하는 MemberRepository와 DiscountPolicy의 구현체로 무엇이 적용되는지 알 길이 없다.
그저 인터페이스의 추상화된 기능만을 이용해 자신의 로직을 실행하는데만 집중할 뿐이다.
즉, OrderServiceImpl과 같은 구현 객체는 MemberRepository의 구현체로 무엇을 이용할지, DiscountPolicy의 구현체로 무엇을 이용할지 등의 제어권이 없다.
이 같은 상황을 프로그램 흐름에 대한 제어권이 (개발자가 작성한) 코드로부터 외부로 넘어갔다고 하여 제어의 역전 (IoC, Inversion of Control)이라 한다.
프레임워크 vs 라이브러리
IoC 발생을 기준으로 프레임워크와 라이브러리를 구분할 수 있다.
- 프레임워크: 프레임워크가 내가 작성한 코드를 제어하고 대신 실행한다면, 그것은 프레임워크다.
- JUnit이 프레임워크에 해당한다. 개발자는 정해진 사용법에 따라 테스트 메서드를 작성할 뿐, 이를 실행하고 관리하는 것은 JUnit이 맡는다.
- 라이브러리: 반면에 내가 작성한 코드가 직접 제어의 흐름을 담당하고, 라이브러리 코드를 갖다 쓰기만 할 뿐이라면, 그것은 프레임워크가 아니라 라이브러리다.
의존관계 주입 - DI (Dependency Injection)
의존관계 주입이란, 런타임에 구현 객체에게 필요한 의존성을 외부에서 주입해주는 것을 말한다.
의존관계는 정적인 클래스 의존 관계와, 실행 시점에 결정되는 동적인 객체 의존 관계를 분리해서 생각해야 한다.
정적인 클래스 의존관계
클래스 간의 의존관계는 클래스의 import 코드만 보고도 쉽게 파악할 수 있다.
클래스 의존관계는 정적이어서, 어플리케이션을 직접 실행하지 않아도 분석할 수 있다.
아래 클래스 다이어그램을 살펴보자.
+) 클래스 다이어그램은 정적인 클래스 의존관계를 표현하므로 정적 다이어그램에 속한다.
클래스 다이어그램을 살펴보면, OrderServiceImpl은 MemberRepository와 DiscountPolicy를 의존한다는 것을 알 수 있다.
이는 OrderServiceImpl 코드의 import 구문을 통해서도 확인할 수 있다.
하지만, MemberRepository와 DiscountPolicy에 실제로 어떤 구현 객체가 주입될지는 클래스 의존관계만으로는 파악할 수 없다.
따라서 실제로 어떤 객체가 생성되고 참조되는지는 동적인 객체(인스턴스) 의존 관계를 살펴보아야 한다.
동적인 객체 의존관계
객체 의존관계는 어플리케이션 실행 시점에 실제 생성된 객체 인스턴스와, 그들 간의 참조가 연결된 의존관계다.
+) 객체 다이어그램은 동적인 객체 의존관계를 표현하므로 동적 다이어그램에 속한다.
객체 의존관계는 클래스 의존관계와 달리, 각 인터페이스의 구현체로 무엇을 생성했고 연결했는지에 따라 달라진다.
예시 그림의 경우, 회원 저장소는 메모리로, 할인 정책은 정액 할인 정책으로 결정되어 연결된 모습이다.
회원 저장소와 할인 정책에 무엇을 주입할지에 따라 객체 의존관계는 얼마든지 달라진다.
이렇게 외부에서 실제 구현 객체를 생성하고 클라이언트에 그 의존성을 주입하는 것을 의존관계 주입(DI, Dependency Injection)이라 한다.
의존관계 주입을 사용하면 클라이언트 코드를 변경하지 않고도 클라이언트가 호출하는(의존하는) 대상의 인스턴스를 변경할 수 있다.
즉, 정적인 클래스 의존관계를 변경하지 않고도 객체 의존관계를 쉽게 변경 가능한 것이다.
IoC 컨테이너, DI 컨테이너
IoC 컨테이너, DI 컨테이너의 의미는 간단하다.
AppConfig와 같이 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 DI 컨테이너, IoC 컨테이너라 한다.
AppConfig가 외부에서 의존성을 주입해주고, 그럼으로써 IoC를 발생시킨다는 의미로 이해하면 편하다.
다만, IoC는 범용적으로 쓰이는 표현이기도 하고, 의존관계 주입에 초점을 맞추어 주로 DI 컨테이너라 한다.
또는 전체 어플리케이션을 조립한다고 하여 어셈블러, 구현 객체 생성을 전담한다고 하여 오브젝트 팩토리 등으로 불리기도 한다.
DI 컨테이너는 여러가지가 있고, 스프링도 DI 컨테이너에 속한다.
다음 포스트부터는 작성한 프로그램을 스프링 기반으로 전환해본다.
'[inflearn] 스프링 핵심 원리 - 기본편 > 섹션 3 - 객체 지향 원리 적용' 카테고리의 다른 글
19. 스프링으로의 전환 (0) | 2023.10.25 |
---|---|
17. 좋은 객체 지향 설계의 5가지 원칙 (SOLID)의 적용 (0) | 2023.10.25 |
16. 새로운 구조와 할인정책 적용, 전체 흐름 정리 (0) | 2023.10.24 |
15. AppConfig 리팩터링 (0) | 2023.10.14 |
14. 관심사의 분리 (0) | 2023.10.13 |