제어의 역전(Inversion of Control, IoC) 이란?
IoC (Inversion of Control) 용어의 최초 등장은 Gof Design Pattern의 저자 Ralph E. Johnson이 1988년에 저술한 'Designing Reusable Classes' 논문에서입니다. 이 용어는 처음 등장했지만, 이 논문을 읽었을 때 이해가 어려웠고, 현재로서도 여전히 어려운 개념입니다. 영어 언어의 복잡성이 일부 원인일 것입니다. 또한, 이후에 Gof Design Pattern 책에서 'Hollywood principle'이란 용어로 다시 등장합니다.
그럼에도 불구하고, 이 용어의 개념은 1988년이 처음 등장한 것이지만, 실제로 IoC의 아이디어는 1978년 팔로알토 연구소에서 발표된 현대의 GUI와 가장 유사한 컴퓨터를 제공하는 객체지향 언어인 Smalltalk와 MVC를 적용한 프레임워크를 공개하면서 시작한 것으로 생각됩니다.
IoC 개념을 이해하기 위해서는 '의존성(Dependency)'과 '의존성 역전(Dependency Inversion)' 그리고 '의존성 주입(Dependency Injection)'에 대한 개념을 먼저 이해해야 합니다. 그러므로, 이해를 돕기 위해 먼저 '의존성' 개념을 차근차근 살펴보도록 하겠습니다.
Dependency
의존성(Dependency)는 다음 게시글을 읽고 쉽게 알 수 있다.
https://hyoung-2y.tistory.com/28
Dependency를 자세히 그리고 쉽게 알아보자!
Dependency [그림 1]에서, Client A는 Service B에 의존합니다. 이 의존성은 A가 B를 멤버 변수로 가지고 있거나 로컬 변수로 사용하거나 B의 메소드를 호출하는 것을 의미합니다. 여기서 Service는 특정 기
hyoung-2y.tistory.com
이러한 상황에서 고차원 모듈이 저차원 모듈에 의존하고, 저차원 모듈이 다시 고차원 모듈에 의존하는 것을 '의존성 부패(Dependency Rot)'이라고 합니다. 아래에서는 이러한 의존성 부패를 없애는 일반적인 디자인 방법인 DIP(Dependency Inversion Principle)에 대해 설명하겠습니다.
Dependency Inversion Principle (DIP)
DIP에 대해서 다음글에 자세하게 설명되어 있다.
https://hyoung-2y.tistory.com/29
Dependency Inversion/Dependency Inversion Principle(DIP)를 자세히 그리고 쉽게 알아보자!
Dependency Inversion Principle (DIP)은 class들 간의 의존성 부패(Dependency Rot)를 해결하기 위한 일반적인 디자인 원칙입니다. 이 원칙은 Robert C. Martin이 1996년에 발표한 'The Dependency Inversion Principle'을 통해
hyoung-2y.tistory.com
DI
의존성 주입(Dependency Injection, DI)라는 말은 들어 봤을 수도 있지만, 다음 글을 통해 DI라는 것이 무엇인지 확인할 수 있습니다.
2023.10.28 - [개발 & CS지식] - Dependency Injection (DI) 는 무엇일까?!
Dependency Injection (DI) 는 무엇일까?!
의존성 주입(Dependency Injection, DI)라는 용어를 들어봤을지도 모릅니다. 이 용어를 처음 접하면 어려울 수 있지만, 사실 우리는 프로그래밍을 처음 배울 때부터 이미 이 개념을 사용해왔습니다. int
hyoung-2y.tistory.com
Inversion of Control (IoC)
DIP(Dependency Inversion Principle)와 DI(Dependency Injection)가 적용된 SwitchButton은 다음과 같이 사용될 수 있습니다. 아래는 SwitchButton을 사용하는 클라이언트 코드의 예시입니다.
main() {
let button = SwitchButton(switchHandler: Lamp())
}
[코드 6] SwitchButton을 사용하는 Client
[코드 6]"에서 클라이언트가 램프(Lamp)를 생성하고 SwitchButton에 주입하는 것을 볼 수 있습니다. 이로써 SwitchButton은 램프(Lamp)를 몰라도 될 뿐 아니라, 클라이언트 역시 램프를 알 필요가 없습니다. 클라이언트는 램프의 생성과 SwitchButton과의 관계 설정을 처리하고 있습니다. 이로 인해 시스템이 더 모듈화되고, 각 컴포넌트 간의 결합도가 낮아집니다. 클라이언트는 램프를 알 필요 없이 스위치 버튼과 상호 작용할 수 있게 되었습니다.
IoC가 적용되지 않은 전통적인 프로그램 흐름은 [그림 12]와 같이 엔트리 포인트에서 다음에 사용할 객체를 결정하고 생성하며, 생성된 객체의 메서드를 호출하고, 이 객체의 메서드 안에서 또 다음 사용할 객체를 결정하고 호출하는 방식으로 작동합니다. 각 객체는 프로그램의 제어 흐름을 결정하거나 필요한 객체를 구성하는 작업에 능동적으로 참여합니다. 즉, 서비스를 사용하는 클라이언트 측에서 모든 것을 제어하고 있는 구조입니다. 제어의 역전(Inverson of Control, IoC)은 이러한 제어 흐름을 반전시키는 것을 의미합니다.
[그림 13]은 제어의 역전된 흐름을 나타냅니다. 엔트리 포인트에서 IoC 컨테이너에게 모든 관계 설정 책임을 위임합니다. 이로 인해 컴파일 타임에서의 정적 클래스 의존성이 런타임에서의 동적 객체 의존성으로 변경됩니다. "[그림 14]"는 SwitchButton에 IoC 개념이 적용된 모습을 보여줍니다.
IoC(Container)를 사용하면 클라이언트가 필요한 객체를 요청할 때 IoC 컨테이너가 해당 객체를 생성하고 필요한 관계를 설정하여 전달합니다. 이렇게 하면 각 클래스가 다른 클래스에 대한 의존성을 알 필요가 없으며, 각 클래스가 컴포넌트가 될 수 있습니다. 이로써 모든 클래스가 분리되고 테스트 가능성(testability)도 향상됩니다. 또한, IoC(Container)만 변경하면 전혀 다른 동작을 하는 프로그램으로 쉽게 변환할 수 있습니다.
가끔 IoC(Container)를 Factory Pattern과 혼동하는 경우가 있지만, Factory는 단순히 객체를 생성하는 역할을 하는 것에 가깝습니다. 반면에 IoC(Container)는 객체 생성 뿐만 아니라 객체 간의 관계를 설정하고 제어의 역전 개념을 적용하여야 합니다. 그저 IoC(Container)를 Factory처럼 사용한다고 해서 제어가 역전되는 것은 아닙니다. IoC(Container)는 객체의 생명주기 관리, 의존성 주입, 설정 관리 등을 포함하는 더 복잡한 기능을 제공합니다.
Conclusion
지금까지 의존성과 제어에 대해 설명했는데, 이것은 애플리케이션 개발에 있어 기초적이면서도 반드시 필요한 기본 개념입니다. 모든 클래스에 대해 이러한 개념을 적용해야 하는 것은 아니지만, 재사용성과 유지 보수성을 향상시키는 데 도움이 됩니다. 물론, 코드의 복잡성은 증가할 수 있지만, 지속 가능한 소프트웨어를 개발하기 위해서는 의존성이 완전히 부패되기 전에 기본적인 관리가 필요합니다. 기본적인 관리란 레이어를 명확하게 정의하고, 각 레이어를 통제하며, 약속된 인터페이스를 통해서만 커뮤니케이션해야 한다는 것을 의미합니다. 즉, 적어도 각 레이어 간의 의존성 관리는 반드시 필요한 것으로 생각합니다. 물론, 간결한 코드와 우아한 코드의 균형을 맞추는 것은 쉽지 않겠지만, 의존성이 망가진 프로젝트를 유지보수하는 것보다 훨씬 나은 선택일 것입니다.