이번 시간에는 IoC, DI, 컨테이너에 대해서 알아보는 시간을 가졌다.
우선 IoC(Inversion of Control)는 제어의 역전이라고 한다.
개념은 개발자가 직접 호출하는게 아닌, 프레임 워크가 필요한 코드를 호출해주는 것을 의미한다.
말 그대로, 제어권이 역전되었다고 해서 IoC라고 한다~~
이전의 문제가 있던 코드는 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성했었다.
(예를 들어, 이전 코드에서 MemberService 구현체가 memoryMemberRepository를 직접 생성했던..)
그리고 연결, 실행도 했었다.
즉, 구현 객체가 프로그램의 제어 흐름을 스스로 조종했다.
개발자가 필요한 것이 있으면 new를 통해서 생성하고, 호출하고.. 이런식으로 진행되는 것은
개발자 입장에서는 자연스러운 흐름이다.
반면에, AppConfig가 등장을 하고, 구현 객체는 자신의 로직을 실행하는 역할만 담당한다.
그리고 프로그램의 제어 흐름은 이제 AppConfig가 가져간다.
예를 들어서, OrderServiceImpl은 필요한 인터페이스들을 호출하지만, 어떤 구현 객체들이 실행될지 모른다.
다시 말해, OrderServiceImpl의 코드만 봐서는 프로그램이 어떻게 흘러가고, 어떤 구현체가 들어올지 모른다.
왜냐하면, AppConfig에서 new를 통해서 생성하고, OrderServiceImpl에는 생성자를 통해서 주입만 되기 때문이다.
그래서 프로그램에 대한 제어 흐름에 대한 권한은 모두 AppConfig가 가지고 있다.
심지어 OrderServiceImpl도 AppConfig가 생성을 한다.
이렇듯, 프로그램의 제어 흐름을 직접 제어하는 것이 아니라, 외부에서 관리하는 것을 제어의 역전(Inversion Of Control, IOC)라고 한다.
그리고 프레임 워크와 라이브러리를 비교했는데,
프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임 워크다.
(예를 들어, 테스트 프레임워크인 JUnit이 있다.)
반면에, 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것은 프레임워크가 아니라 라이브러리이다.
테스트 코드를 보면, 개발자는 join 메소드의 로직만 개발했는데, 이 코드에 대한 실행과 제어권을 JUnit이라고 하는 테스트 프레임 워크가 가져가면서, 개발자 대신 실행을 해준다.
JUnit은 자신의 라이프 사이클이 있기 때문에, @BeforeEach를 먼저 실행하고, 그 다음에 위의 사진의 join 메소드를 실행해준다.
내가 작성한 코드가 직접 제어의 흐름을 담당하는 라이브러리의 예시로는,
자바 객체를 xml로 바꾸거나, json으로 바꾸는 것은 라이브러리를 불러서 개발자가 직접 호출하는 것이기 때문에,
이런것을 라이브러리의 예시로 볼 수 있다.
의존관계의 주입 (Dependency Injection, DI)를 설명하자면,
구현체인 OrderServiceImpl은 할인 인터페이스인 DiscountPolicy에 의존한다. 실제 어떤 구현 객체가 들어올지는 모른다.
의존관계는 정적인 클래스 의존 관계와, 실행 시점에 결정되는 동적인 객체 의존 관계, 이 두가지를 분리해서 생각해야 한다.
OrderServiceImpl 코드만 보고서는, discountPolicy에 고정할인 정책이 들어올지, 정률할인 정책이 들어올지는 분석이 불가능하다.
이런 것을 동적인 객체 인스턴스 의존 관계라고 한다.
애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고, 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 의존관계 주입이라고 한다.
객체 인스턴스를 생성하고, 그 참조값을 전달해서 연결이 된다.
참조를 통해서 인스턴스가 연결이 되기 때문에, 위의 사진에서 예를 들면, memberRepository라는 참조값이 연결이 되는것을 확인할 수 있다.
의존관계 주입을 사용하면, 클라이언트 코드를 변경하지 않고, 클라이언트 호출하는 대상의 타입 인스턴스를 변경할 수 있다.
예를 들어, 할인 정책이 바뀌었을 때도, FixDiscountPolicy에서 RateDiscountPolicy로 변경할 때, 클라이언트 코드를 변경하지 않아도 되는 것과 같다.
의존관계 주입을 사용하면, 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.
동적인것을 바꾼다는 의미는, 예를 들어, Repository에서 memory 저장소로 할 지, JDBC 저장소로 할지와 같이 수정하는 것을 말한다.
IoC 컨테이너와, DI 컨테이너에 대해서,
쉽게 말해서, IoC를 해주는 컨테이너, DI를 해주는 컨테이너라고 생각하면 된다ㅋㅋㅋ
다시 말해, AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC 컨테이너 또는 DI 컨테이너라고 한다. 최근에는 IoC 컨테이너 보다는, 의존관계 주입에 초점을 두어서 주로 DI 컨테이너라고 불린다고 한다.
다른 말로는, 애플리케이션 전체 구성을 조립한다고 해서 어셈블러라고 불리기도 하고,
오브젝트를 만들어낸다고 해서 오브젝트 팩토리 등으로 불리기도 한다.
댓글