Spring IoC와 DI
IoC의 정의
IoC는 Inversion of Control의 약자로, 제어를 역전하는 디자인 원칙(Design Principle)입니다. 1) 프로그램의 흐름 제어나 2) 객체 의존성 관리와 같이, 소프트웨어 컴포넌트의 주된 역할(main purpose)이 아닌 부가적인 역할들을 개발자의 Custom Code 대신 프레임워크가 맡기 때문에 제어의 역전이라고 합니다.
IoC를 따르는 디자인 패턴들
- 프로그램의 흐름 제어를 역전한다는 것은 TUI(Text-based User Interface)와 GUI를 비교해서 설명할 수 있는데, TUI에서는 주로 시스템에 사용가능한 명령어(available commands)를 리스트업하거나 사용자가 입력한 값을 조회하기 위해 개발자가 시스템 코드를 호출했다면, GUI에서는 프레임워크가 사용자의 인풋에 따라 개발자의 코드를 호출합니다. Event loop 외에도 Callback, Scheduler도 IoC 원칙을 따르는 디자인 패턴들입니다.
- DI도 역시 IoC를 따르는 디자인 패턴 중 하나입니다. DI는 의존성 관리를 프레임워크에게 맡기는 방법입니다. Spring Framework는 IoC Container가 객체들의 생애주기와 의존성을 관리하게 함으로써 제어를 역전합니다.
IoC의 목적
객체 지향적인 디자인에서 느슨한 결합을 구현하기 위함입니다. 소프트웨어의 컴포넌트들, 자바로 치면 Bean들이 스스로 각각의 의존성을 관리하는 것은 강한 결합이기 때문에 변경에 대한 유연성이 떨어집니다. 따라서 Spring Framework가 그 의존성을 생성하고 주입하는 등 관리한다면 느슨한 결합도 달성할 수 있을 뿐만 아니라 테스트를 하기 위해서도 IoC는 필수라고 볼 수 있습니다. IoC가 없다면 독립적인 테스트를 위해 의존 객체 대신 Mock 객체를 주입하는 것이 불가능하기 때문입니다.
DI의 정의
DI는 Dependency Injection의 약자로 IoC를 구현하기 위한 디자인패턴 중 하나입니다. DI는 필요한 객체를 클래스 외부에서 생성해서 제공하는 것을 말합니다. 주입을 하는 방법은 크게 세 가지가 있습니다. 1) 생성자 주입 2) Setter 주입 3) 메소드 주입
DI의 구현방법 3가지
- 생성자 주입은 클라이언트 클래스의 생성자의 인자로 필요한 클래스를 받는 방법입니다.
- Setter 주입은 클라이언트 클래스가 의존하는 클래스를 Property로 가지기 때문에 Property 주입이라고도 합니다. Public한 Setter를 가지고 있음으로서 외부에서 의존성을 주입할 수 있게 합니다.
- 메소드 주입은 특정 메소드가 의존 객체를 인자로 받는 방법을 말합니다.
- 생성자 주입은 의존성이 필수이고 의존 객체가 고정적일 때 사용됩니다. 생성자 주입으로 구현하면 객체를 immutable하게 관리할 수 있으므로 thread-safe 한 등 안정적입니다.
- Setter 주입은 의존 객체가 거의 변하지 않지만 의존성이 필수가 아닐 때 사용합니다. 주의할 점은 Setter를 통해 의존 주입이 되지 않았을 때 Default 상태로도 잘 동작하는 케이스에만 사용하는 것이 좋습니다. 그렇지 않으면 코드 전역에서 Not-null 검사를 해야하기 때문입니다.
- 메소드 주입은 특정 메소드에서만 의존성이 필요할 때, 그리고 조건에 따라 다른 구현객체가 필요할 때 사용됩니다.
DI의 목적
DI의 목적은 객체지향적인 디자인 원칙 중 하나인 느슨한 결합입니다. 예를 들어 어떤 서비스가 데이터 접근을 위해 DB를 사용하다가 API를 사용하도록 바뀌는 시나리오를 가정해보겠습니다. 만약 처음에 어떤 서비스가 DBAccess 클래스를 직접 생성해서 사용할 경우, APIAccess 클래스를 사용하도록 변경되면 서비스도 수정해야 합니다.
그러나 “High-level 모듈이 Low-level 모듈에 의존하지 않는다. 둘 다 abstraction에 의존한다”는 DIP(Dependency Inversion Principle)에 맞춰 IDataAccess 인터페이스를 정의하고 DBAccess 클래스를 이 인터페이스의 구현체로 정의한 다음, 서비스의 생성자로서 DBAccess 인스턴스를 주입할 수 있게 구현하면 APIAccess인스턴스로 변경할 때 서비스를 수정할 필요가 없습니다.
참고자료