Eternity's Chit-Chat

aeternum.egloos.com



Framework - 2부 Concept & Principle

의존성 역전(Dependency Inversion)과 프레임워크
앞에서 살펴 본 바와 같이 프레임워크의 핵심은 추상 클래스라고 할 수 있다. 그렇다면 추상 클래스(또는 인터페이스)의 어떤 특징이 프레임워크의 재사용성을 향상시키는 것일까?

객체-지향 이전의 구조적 분석/설계와 같은 전통적인 소프트웨어 개발 방법의 경우 상위 레벨 모듈이 하위 레벨 모듈에, 그리고 상위 정책이 구체적인 세부적인 사항에 의존하도록 소프트웨어를 구성한다. 이해를 돕기 위해 핸드폰 과금 시스템에서 가입자가 문자 서비스를 사용할 경우의 요금 계산 로직을 떠올려 보자. 여기에서의 상위 정책은 DOMAIN EVENT의 타입에 따라 DISPATCHER AGREEMENT에서 POSTING RULE을 선택하는 일련의 흐름이다. 실제 요금을 계산하는 DOMAIN EVENT, ACCOUTING ENTRY, POSTING RULE 타입은 구체적인 세부 사항에 속한다.

<그림1>은 통화 요금을 계산하는 상위 정책인 PeriodicServiceContract가 세부적인 SmsStandardPostingRule, SmsSendingEvent, MonetaryEntry와 강하게 결합되어 있는 전통적인 의존 관계를 표현한 것이다.


<그림 1> 정책이 세부적인 사항에 의존하는 전통적인 의존 관계

<그림 1>의 설계에 나타난 가장 큰 문제점은 다양한 종류의 POSTING RULE과 DOMAIN EVENT, ACCOUNTING ENTRY를 처리해야 하는 PeriodicServiceContract가 문자 메시지에 특화된 구체적인 클래스들에게 의존한다는 점이다. 따라서 통화 요금 계산을 위해 새로운 POSTING RULE, DOMAIN EVENT, ACCOUNTING ENTRY 타입을 사용해야 할 경우 PeriodicServiceContract를 재사용할 수 없다. 즉, 상위 레벨의 정책이 하위 레벨의 구체적인 세부사항에 의존하도록 구조를 설계할 경우 다양한 컨텍스트에서의 재사용이 불가능해진다.

이와 같은 의존성 문제를 문제를 해결할 수 있는 방법이 없을까? 방법은 의외로 간단하다. 상위 레벨의 정책을 재사용하기 위해서는 상위 레벨의 정책이 하위 레벨에 대해 독립적이어야 한다. 즉, 정책이 상세에 의존하지 않도록 의존성을 역전시켜야 한다.

상위 레벨의 정책이 의존하고 있는 세부적인 클래스들로부터 EXTRACT INTERFACE를 한다. 이제 상위 레벨 정책이 하위 레벨의 구체적인 클래스에 직접적으로 의존하지 않게 되며 상세와 정책 모두 추상적인 인터페이스에 의존하게 된다. 이와 같은 방법은 전통적인 분석/설계 방법에서 다루는 의존성의 방향을 역전시킨다. 이처럼 재사용 가능한 객체 지향 설계를 만들기 위해 인터페이스 또는 추상 클래스를 사용해서 의존성의 방향을 바꾸는 것을 의존성 역전 원리(DIP, Dependency-Inversion Principle)라고 한다. <그림 2>는 의존성을 역전시킨 후의 핸드폰 과금 시스템의 구조를 표현한 것이다.


<그림 2> 정책과 상세 모두 인터페이스에 의존하도록 의존성을 역전

이제 <그림 3>과 같이 상위 정책인 PeriodicServiceContract와 추출된 인터페이스를 별도의 패키지로 묶어 보자. DOMAIN EVENT와 ACCOUNTING ENTRY의 인터페이스는 상위 정책의 필요에 의해 결정되기 때문에 상위 정책에 속한다. 즉, 인터페이스와 구현 클래스를 별도의 패키지로 분리해야 한다. 이처럼 재사용을 위해 인터페이스와 구현 부를 별도의 패키지로 분리하는 것을 SEPARATED INTERFACE 패턴이라고 한다. 상위 정책과 인터페이스를 포함하는 패키지는 전체 애플리케이션에 있어 프레임워크로서의 기능을 제공한다.


<그림 3> 상위 정책과 인터페이스를 포함하는 프레임워크

<그림 4>와 같이 상위 정책을 표현하는 패키지에 속한 인터페이스 또는 추상 클래스를 구현하는 새로운 POSTING RULE, DOMAIN EVENT, ACCOUNTING ENTRY를 추가해서 상위 정책의 설계를 재사용할 수 있다. 이것이 애플리케이션이 프레임워크의 설계와 코드를 재사용하는 방법이다.


<그림 4> 프레임워크를 재사용하는 애플리케이션

사실, 좋은 객체 지향 설계의 증명이 바로 이와 같은 의존성의 역전이다. 프로그램이 어떤 언어로 작성되었는가는 상관없다. 프로그램의 의존성이 역전되어 있다면, 이것은 OO 설계를 갖는 것이다. 그 의존성이 역전되어 있지 않다면, 절차적 설계를 갖는 것이다. 의존성 역전의 원칙은 객체 지향 기술에서 당연하게 요구되는 많은 이점 뒤에 있는 하위 수준에서의 기본 메커니즘이다. 재사용 가능한 프레임워크를 만들기 위해서는 이것의 적절한 응용이 필수적이다. 이 원칙은 또한 변경에 탄력적인 코드를 작성하는 데 있어 결정적으로 중요하다. 추상화와 구체적인 사항이 서로 고립되어 있기 때문에, 이 코드는 유지보수하기가 훨씬 쉽다.

- Robert C. Martin, 소프트웨어 개발의 지혜

제어 역전(Inversion of Control)과 프레임워크
의존성 역전은 의존성의 방향뿐만 아니라 제어 흐름의 주체 역시 역전 시킨다. 앞서 설명한 것처럼 상위 정책이 구체적인 세부사항에 의존하는 전통적인 구조에서는 상위 정책의 코드가 하부의 구체적인 코드를 호출한다. 즉, 애플리케이션의 코드가 재사용 가능한 라이브러리나 툴킷의 코드를 호출한다. 그러나 의존성을 역전 시킨 객체 지향 구조에서는 반대로 프레임워크가 애플리케이션에 속하는 서브 클래스의 메소드를 호출한다. 따라서 프레임워크를 사용할 경우 개별 애플리케이션에서 프레임워크로 제어 흐름의 주체가 이동된다. 즉, 의존성을 역전시키면 제어 흐름의 주체 역시 역전된다. 이를 제어 역전(Inversion of Control)의 원리, 또는 할리우드(Hollywood) 원리라고 한다.


설계 수준의 재사용은 애플리케이션과 기반이 되는 소프트웨어 간에 제어를 바꾸게 한다. 툴킷을 사용하여 애플리케이션을 작성하면, 애플리케이션은 재사용하는 툴킷의 코드를 호출한다. 그러나 프레임워크를 재사용할 때는 프레임워크가 제공하는 주(main) 프로그램을 재사용하고 이 주 프로그램이 호출하는 코드를 애플리케이션 개발자가 작성하는 것이다. 이미 특정 이름과 호출 방식이 결정된 오퍼레이션을 작성해야 하지만 결정해야 하는 설계 개념은 줄어들고 애플리케이션 별 구체적인 오퍼레이션의 구현만 남게 된다.

-  GOF, 디자인 패턴

프레임워크에서는 일반적인 해결책만 제공하고 애플리케이션에 따라 달라질 수 있는 특정한 동작은 비워둔다. 그리고 이렇게 완성되지 않은 채로 남겨진 동작을 훅(hook)이라고 하며, 훅의 구현은 애플리케이션의 컨텍스트에 따라 달라진다. 훅은 프레임워크 코드에서 호출하는 프레임워크의 특정 부분이다. 재정의된 훅은 제어 역전 원리에 따라 프레임워크가 원하는 시점에 호출된다. 만약 프레임워크를 처음 사용한다면 제어 흐름이 손가락 사이로 스멀스멀 빠져나가는 듯한 느낌에 불안해질 수도 있다. 그러나 이러한 제어의 역전이 프레임워크의 핵심 개념인 동시에 코드의 재사용을 가능하게 하는 힘이라는 사실을 이해해야 한다.


<그림 5> 프레임워크에 정의된 제어 흐름에 따라 호출되는 애플리케이션 객체


덧글

  • 핼리 2015/03/11 18:29 # 삭제

    잘읽었습니다. ㅎㅎ
  • 후회하지말자 2015/08/08 22:14 # 삭제

    정말 좋은 글이네요..
  • 이터너티 2015/08/09 13:42 #

    감사합니다 ^^
※ 로그인 사용자만 덧글을 남길 수 있습니다.