[Spring] IoC, DI 컨테이너
IoC (Inversion of Control)
정의
IoC란 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을 의미한다.
위 정의만 읽고 IoC가 무엇인지 와닿지 않을 수 있다. 아래 예시를 보고 이해해보자.
우리는 기존에 코드를 작성할 때, 각 객체에서 자신이 필요한 객체를 생성하고 실행하도록 했었다.
ManagerController 클래스가 파일 입출력에 관련된 객체(FileInputView, FileOutputVIew)를 직접 생성하고 run()
메서드 안에서 사용하는 것을 볼 수 있다.
이 코드의 문제점은 무엇일까?
만약 우리가 파일로부터 입출력 하는 것이 아니라 콘솔로부터 입출력을 받아야 하는 상황이 생겼다고 해보자. 이를 위해서는 ManagerController의 코드를 아래와 같이 변경해야 한다.
위와 같이 요구사항이 변경됐을 때, 클라이언트인 ManagerController에 변경이 발생한다. (OCP 위반)
이는 ManagerConroller가 인터페이스가 아닌 구현체에 직접 의존하고 있기 때문에 발생하는 것이다. (DIP 위반)
그렇다면 요구사항이 추가되거나 변경되더라도 기존 코드를 유지할 수 있는 방법이 있을까?
-> ManagerController에서 구현체(ConsoleInputView, ..)에 의존하지 않고 인터페이스(InputView, OutputView)에만 의존하게 하면 된다.
ManagerController를 구현체가 아닌 인터페이스에만 의존하도록 변경했다. 그런데 당연히 이렇게 하면 안된다.
위 코드는 final을 넣었기 때문 빨간줄이 나왔지만 final이 없더라도 run()
메소드 안에서 inputView와 outputView를 사용할 때 구현체가 없기 때문에 문제가 발생할 것이다. 이를 해결하기 위해서는 외부로부터 객체를 주입(DI)받아야 한다.
외부로부터 구현 객체를 주입 받도록 코드를 수정했다. 이제 ManagerController는 어떤 InputView, OutputView가 들어오는 지 알지 못한 채로 자신의 로직(run()
)을 실행하게 된다.
위 코드가 정상적으로 돌기 위해서는 외부에서 InputView, OutputVIew 객체를 생성하고 ManagerController에 주입해줘야 한다.
AppConfig라는 클래스에서 InputView, OutputView의 실제 객체를 생성하고 ManagerController에 주입해주는 모습을 볼 수 있다.
-
이처럼 외부에서 의존성을 주입해 주는 것을 DI라고 한다. DI는 의존적인 객체를 직접 생성하거나 제어하는 것이 아니라 단순히 특정 객체에 필요한 객체를 외부에서 연결해주는 것을 의미한다.
-
참고 : 코드를 보면 알 수 있듯이 FileInputView, FileOutputView가 중복해서 생성된다. Spring DI 컨테이너를 사용하면 싱글톤으로 이들을 관리할 수 있다.
AppConfig는 실제 실행 흐름과는 전혀 관련이 없는 객체이다. 이와 같이 외부에서 실제 프로그램의 대한 제어 흐름을 관리하는 것을 제어가 역전되었다고 해서 IoC라고 한다. 그리고 여기서 실제로 객체를 생성하고 생명 주기를 관리해주는 친구(ex-AppConfig)를 IoC 컨테이너(DI 컨테이너)라고 한다.
정리
오늘은 IoC, DI에 대해서 알아봤다. 기존에는 각 객체가 자신이 필요한 객체를 직접 생성하고 실행시켰다. 때문에 요구사항이 변경된다면 클라이언트 코드의 변경이 불가피했다. IoC 컨테이너를 사용하게 되면 실행 흐름과 관련없는 외부에서 객체를 생성하고 주입해주기 때문에 이런 문제가 해결된다.
Spring도 여기서 설명한 개념을 그대로 사용한다. 그리고 각 빈(객체)들을 싱글톤으로 관리해준다. 이에 대해서는 다음에 알아보자.