본문 바로가기

IT 살이/04. 기술 - 프로그래밍

개발 프레임워크 만들기 대장정 06- Unity 컨테이너 구조

이제 Unity 컨테이너를 좀 알아보도록 하자. Unity, Unity 컨테이너, Unity Application Block이라는 표현을 섞어서 사용하지만 모두 같은 개념으로 보면 된다. 사실 이런 프레임워크를 다 이해하지는 못해도 사용할 수는 있다는 것은 지금까지 SI 프로젝트에서의 경험을 통해서 보면 잘 알 것이다. 그러나 우리는 프레임워크를 이해하고 만들려는 사람들이다. 따라서 이 구조를 이해할 필요가 있고 결국에는 그 구조를 바탕으로 해서 나름의 생각대로 설계할 수 있길 바란다.

아직 필자도 이 블럭을 모두 살펴본 것은 아니다. 사실은 지금까지의 경험에 의해 아마 그럴 것이다라는 추정을 가지고 확인해가면서 글을 쓰고 있다. -_-;;  Unity Application Block을 설치하면 함께 생기는 문서에 있는 내용을 좀 옮겨 보겠다.  "Design of the Unity Applicaion Block"절을 참고하고 있다.

Unity 컨테이너의 설계 목표는 다음과 같단다.

  • 디커플링(decoupling)을 통해서 모듈화를 목표로 했다.
  • 애플리케이션 개발시 테스트를 쉽게 할 수 있도록 했다.
  • 새로운 객체를 생성하고 기존 생성된 객체를 관리하는데 필요한 dependency injection 컨테이너 메커니즘을 제공한다.
  • 컨테이너를 이용해서 개발하기 위해 필요한 직관적이고 컴팩트한 API를 제공한다.
  • 제너릭 파라미터를 받아들이는 메소드 오버라이딩같은 코딩 언어(C#)의 폭넓게 지원한다.
  • 생성자, setter 속성, 메소드의 어트리뷰트 기반의 dependency injection을 구현한다.
  • 사용자 정의 또는 3th 파티 컨테이너 확장을 지원한다.
  • 엔터프라이즈급의 LOB( Line Of Business) 애플리케이션에 필요한 퍼포먼스를 보장한다.

이런 목표들이 지금 무슨말인지는 다 이해가 가지 않을 수 있다.  지금은 그냥 넘어가더라도 "Unity 컨테이너는 생성자( constructor), setter 속성, 메소드를 호출할때 '원한다'면 dependency injection을 수행할 수 있다"는 것은 한번 더 봐 두고 가자. dependency injection을 '원한다'는 것을 표시하는 방법은 "어트리뷰트(attribute)"를 통해서 한다는 것도 한번 더 알아두고 가자.

할 것 많다....^^. Dependency injection ( 프로그래밍 패턴이다)도 알아야 하고, 언어적으로는 제너릭도 알아야 하고 어트리뷰트 기반의 프로그래밍도 알아두면 좋을 것 같고....에구구...

여튼 이런 목표를 달성하기 위해서 연구 연구하다보니... Unity 컨테이너는 다음과 같은 구조를 갖게 되었단다.

큰 그림을 살펴보자. 그림에서 점선 화살표는 타입간의 관계를 나타낸다. 그리고 실선 화살표는 메소드 호출을 표시하고 있다. 사각형으로된 된 부분은 타입( 인터페이스 , 클래스)이다. IUnityContainer, UnityContainerBase, UnityContainer, Builder, UnityDefaultStrategiesExtention, UnityDefaultBehaviorExtention.. 구조의 윤곽만을 잡기위해서 중요한 핵심 타입만을 보여주고 있다. Unity 컨테이너에는 포함되어 있지 않지만 인스턴스를 생성하기 위해서 Object Builder 모듈에 포함된 Builder라는 클래스와도 커뮤니케이션을 하고 있다.

그리고 점선으로 주석 처리된 박스에서는 타입에서 어떤 종류의 데이터를 관리하고 있고 어떤 일을 하는 지를 보여주고 있다. UnityContainer 객체가 생성될때 초기화되는 과정에서 컨테이너가 관리하는 객체들도 함께 초기화된다. 이런 객체들을 관리하는 것은 Unity 컨테이너에서이지만 생성하는 것은 ObjectBuilder가 하는 일이다.

컨테이너 초기화

그럼 Unity 컨테이너가 초기화되면서 어떤 객체들을 생성하는지 알아보자. 그림에서 UnityContainer 클래스와 연결된 주석 박스 부분이다.

  • NamedTypesRegistry 클래스 : 등록된 타입의 리스트를 가지고 있단다. 매핑 정보뿐만 아니라 매핑에 필요한 타입들(베이스 타입, 실제 타입 ) 자체도 이렇게 별도로 등록되어 관리되고 있나보다.
  • List<UnityContainerExtension> : 컨테이너에 추가된 UnityContainerExtension 객체에 대한 참조를 가지고 있는 제너릭형의 List 객체란다. UnityContainerExtension이 뭔지는 아직 나도 잘 모르겠다...나중에 또 보자.
  • Locator : 등록된 타입의 인스턴스를 찾는 녀석
  • LifetimeContainer : 등록된 객체들의 수명을 관리하는 녀석도 있나 보다.  컨테이너가 없어질때 등록된 모든 객체들도 이 녀석이 dispose시킨단다.
  • StagedStrategyChain : 컨테이너에 2개의 인스턴스가 있다는데, 이 녀석은 ObjectBuilder가 등록된 타입의 인스턴스를 생성할때 사용하는 빌드 전략(build strategies)과 빌드 계획(build plans)을 가지고 있단다. 물론 나도 빌드 전략과 빌드 계획이 뭔지 아직 모른다.
  • PolicyList : ObjectBuilder가 빌드하는 동안 객체와 클래스를 검색할때 사용하는 정책들(policies)이 있단다. 그것들에 대한 참조를 가지고 있단다.

컨테이너 초기화 동안, UnityContainer 객체에는 다음 두 인스턴스가 컨테이너에 추가된다 : UnityDefaultStrategiesExtension, UnityDefaultBehaviorExtension . 그림에서 회색의 박스이다.

UnityDefaultStrategiesExtension : 이 녀석은 컨테이너가 초기화되는 동안에  StagedStrategyChain이라는 인스턴스 두개를 컨테이너 컨텍스트(container context)객체를 통해서 컨테이너에 추가한다. StagedStrategyChain 객체 하나는 전략 체인(stratege chain) 다른 하나는 빌드 계획 전략 체인(build plan strategy chain)을 표시한다. 이런 전략 체인들은 ObjectBuilder가 contructor injection, property injection, method call injection을 수행할때 사용한다.

UnityDefaultStrategiesExtension은 또한 현재 컨테이너 컨테스트를 통해서 세개의 어트리뷰트 즉 InjectionContructor, Dependency, IndectionMethod 어트리뷰트를 추가한다. 이 어트리뷰트들은 ObjectBuilder가 객체를 생성할때 추가로 생성할 객체들이 있는지를 인식할 수 있는 어트리뷰트들이다.

내용이 너무 벅찰질도 모르겠다. Dependency injection과 관련된 객체들이다. 나중에 이 패턴을 설명해야 할 것이다. 지금은 몰라도 너무 실망하지 말라. 뒤에 나올 것이다.

UnityDefaultBehaviorExtension이라는 녀석은 컨테이너에서 발생하는 이벤트에 등록될 핸들러 메소드를 가지고 있단다.

컨테이너에서는 다음과 같은 2가지 등록 이벤트가 발생한다.

  • Registering : 컨테이너에 타입을 등록할 때 발생하는 이벤트
  • RegisteringInstance : 컨테이너에 타입의 인스턴스를 추가할때 발생하는 이벤트

그러나 이벤트는 컨테이너에서 부터 발생하지만 컨테이너 외부에서는 직접 컨테이너 내부에 접근할 수는 없는 듯하다. 그래서 ExtensionContext라는 객체를 통해서 접근하도록 설계되어 있는 듯하다. 앞뒤 상황을 보고 코드를 살펴본 결과 다음과 같은 구조로 되어 있는 듯한다.

Default 라는 표현이 포함된 것을 보니 오른쪽 녀석들도 확장이 될 수 있는 녀석들이란 것을 알 수 있다. 타입이 등록되면 UnityDefaultBehaviorExtension에서는 기본적으로 무슨 일을 하나 보다. 살짝 코드를 보니 적절한 정책을 생성해서 컨테이너 컨텍스트 객체에 설정하고 있다. 또한 타입 조회에 필요한 locator 엔트리도 생성해서 컨텍스트 객체에 설정하고 있고, 객체 수명 관리자(LifetimeManager)라는 것도 설정하는 코드가 보인다. 으으....뭔말인지....^^

오퍼레이션

앞에서는 Unity 컨테이너의 초기화동안 구성되는 데이터 구조를 봤다. 컨테이너에는 개발자가 사용할 수 있는 공개 메소드들도 있단다. 다음 두 종류로 분류할 수 있다.

  • 타입 매핑 또는 타입을 등록할 수 있는 메소드 : 이 메소드들은 Registering, RegisteringInstance 이벤트를 발생시킨다.
  • 객체를 얻을 수 있는 메소드 : Resolve(), ResolveAll(), BuildUp() 메소드가 포함되어 있다. 이 메소드들은 모두 컨테이너 내부에 있는 private 메소드 DoBuildUp() 메소드를 호출한다. 이 메소드는 ObjectBuilder가 객체를 생성할때 필요한 객체들을 만들어서 ObjectBuilder에게 건네주고 그로부터 생성된 객체를 건네받아 앞의 메소드로 건네준다.

복잡하다. 뭔말인지...더 이상 깊게 들어가봤자. 지금으로서는 무리일것 같다. 대신에 다음 포스트에서는 hello 예제 프로그램을 만들어 보도록 하겠다.

벌써 새벽이다. 내일 또 출근할 것을 생각하니 마음이 급해져서, 좀 날림으로 글을 썼다. 나중에 시간되면 다시 읽어보고 부족한 부분이 있다면 보충하도록 하겠다.