지난 6월 10일 VSTS 2010 팀에서 진행한 MEF(Managed Extensibility Framework) 세미나에서 보여드린 데모를 조금 수정하여 Visual Studio Gallery 에 공개하였습니다.

Visual Studio 2010 이 WPF 기반의 IDE 환경으로 탈바꿈하면서 특히 코드 에디터 쪽은 WPF 의 기능을 유감없이 보여주었습니다. 특히 코드 에디터는 MEF 를 통해 대부분의 기능이 만들어졌고, 이것을 확장할 수 있는 기능을 만드는 것이 얼마나 편해졌는지 보여주기 위한 데모였습니다.

세미나에서도 설명했지만 현재 Visual Studio 2008 까지 버전에서 이러한 확장 기능을 개발하기 위해서는 굉장히 많은 단계를 거쳐야 합니다. Visual Studio 2008 까지는 COM 을 기반으로 한 IDE 이기 때문에 적용되는 기술만 나열해도 절대 단순하지 않은 과정을 거쳐야 합니다. 마우스와 키보드와 같은 장치의 이벤트를 핸들링 해야 하고, GDI+ 를 이용해 화면에 드로잉을 하고 잔상과 깜빡임을 없애기 위해 더블 버퍼링 등의 기법이 조합되어야 했습니다. 그리고 나은 사용자 인터페이스를 위해 많은 기교를 부려야만 했죠.

   

멀리서 프레젠테이션을 시청하는 분들도 커서의 위치를 잘 보이도록 화살표가 커서를 따라 다닙니다. 
 

 

Visual Studio Gallery

Umc.Core.Tools.MousePresentationTracker.v1.0

   

   

현재는 단순하게 마우스를 따라다니는 데모를 급 개조했지만, 나중에는 생각해 놓은 기능들을 추가하려고 합니다. 어쨌든 이전에 공개했던 VSGesture 와 다른 컨셉으로 프레젠테이션을 위한 도구로 점차 기능이 추가될 예정입니다.

[Testing] Moq.NET (T/B Driven Development)

Agile Development 2009. 6. 29. 12:36 Posted by POWERUMC

 

Moq.NET

Moq 는 "Mock-you" 또는 "Mock" 로 부른다고 합니다. Moq.NET 3.0 은 C# 3.0 과 .NET Framework 3.5 를 통해 Linq Expression Tree 와 Lambda Expression(람대 표현식) 으로 직관적이고 생산적이라고 합니다.

이전에 봤던 웹 사이트 로그인 사용자 스토리를 다시 봅시다. 단, 이 예제에서는 복잡성을 만족하는 항목을 삭제합니다.

웹 사이트의 로그인 사용자 스토리

  • 사용자 아이디는 영문만 입력 가능하고 한글은 입력할 수 없다
  • 사용자 아이디는 최소 3자리, 최대 10자리까지 입력가능하고 초과시 경고 메시지를 보여준다
  • 사용자 비밀번호는 최소 5자리, 최대 20자리까지 입력 가능하고 초과시 경고 메시지를 보여준다

   

위의 사용자 스토리를 Mocking Framework 인 Moq.NET 을 이용하여 TDD와 BDD 형태로 테스트를 작성해 나갈 수 있습니다.

아래의 소스 코드는 Login 인터페이스를 정의한 코드입니다.

public interface ILogin
{

LoginResult Login(string id, string password);

LoginInputResult Valide(string id, string password);

}

   

public enum LoginResult

{

Authentication,

NoAuthentication

}

   

public enum LoginInputResult

{

Success,

MinLengthId,

MaxLengthId,

MinLengthPassword,

MaxLengthPassword

}

   

이제 Login 의 Behavior(행위) 측면에서 주도적인 개발을 하고자 합니다. BDD 이용하여 [그림1] 의 Clean up code 를 얻기 위한 목적이 아닙니다. 좀 더 TDD 에 가깝고, 디자인과 설계에 초점을 맞추고자 합니다.

class Program
{

static void Main(string[] args)

{

var id = "powerumc";

var pwd = "aaaa";

var mock = new Mock<ILogin>();

}

   

private static string minString(int length)

{

return Match<string>.Create( o => o.Length <= length );

}

   

private static string maxString(int length)

{

return Match<string>.Create( o => o.Length >= length );

}

}

   

위의 코드를 통해 Mocking Object 를 생성하도록 Mock<T> 생성자를 볼 수 있습니다. 바로 Mock<T> 를 통해 Mocking Object 를 생성합니다.

아래의 코드는 위의 로그인 사용자 스토리에서 입력되는 아이디/비밀번호의 유효성을 검사하도록 Mocking Object 를 설정합니다.

// Validate

mock.Setup( o => o.Valide(minString(3), It.IsAny<string>()))

.Returns(LoginInputResult.MinLengthId)

.Callback(()=>Console.WriteLine("ERROR> MinLengthId"));

mock.Setup( o => o.Valide(maxString(10), It.IsAny<string>()))

.Returns(LoginInputResult.MaxLengthId)

.Callback(()=>Console.WriteLine("ERROR> MaxLengthId"));

mock.Setup( o => o.Valide(It.IsAny<string>(), minString(3)))

.Returns(LoginInputResult.MinLengthPassword)

.Callback(()=>Console.WriteLine("ERROR> MinLengthPassword"));

mock.Setup( o => o.Valide(It.IsAny<string>(), maxString(20)))

.Returns(LoginInputResult.MaxLengthPassword)

.Callback(()=>Console.WriteLine("ERROR> MaxLengthPassword"));

   

아래의 코드는 유효성을 통과한 아이디/비밀번호로 로그인을 시도하고 결과값을 리턴하는 Mocking Object 입니다.

// Login

mock.Setup( o => o.Login(It.IsAny<string>(), It.IsAny<string>()))

.Returns(LoginResult.NoAuthentication)

.Callback(()=>Console.WriteLine("No Authentication"));

mock.Setup( o => o.Login(id, pwd))

.Returns(LoginResult.Authentication)

.Callback(()=>Console.WriteLine("Success Login"));

   

자, 그럼 가상의 Mocking Object 를 통해 인터페이스만으로 로그인 시도를 해보도록 하겠습니다.

var obj = mock.Object;

var loginInputResult = obj.Valide(id,pwd);

   

if( loginInputResult != LoginInputResult.Success ) return;

   

obj.Login(id,pwd);

   

입력 변수 값에 따라 비록 Mocking Object 지만, 테스트 시나리오를 충분히 검증할 수 있습니다.

Id="aaa", pwd="aaa" 일 경우는 아래와 같은 결과가 나타나겠죠~?

 

Id="powerumc", pwd="aaaa" 일 경우는 아래와 같이 모든 테스트 시나리오를 통과한 결과입니다.

 

그럼, Microsoft Research 프로젝트의 일환인 Pex 를 이용하여 Mocking Object 를 Testing 해보겠습니다. Pex 테스트 프로젝트를 생성하고 아래의 PexMethod 를 만들었습니다.

아래는 테스트 결과입니다. 총 31개의 테스트 케이스 중에 통과되는 1개의 테스트를 확인할 수 있습니다.

자 어떻습니다~? 전혀 구현 코드를 구현하지 않았음에도 인터페이스를 통해 TDD-테스트 주도 개발이 가능합니다. BDD-행위 주도 개발을 통해 번거로운 TDD 의 Red, Green, Refector 의 유한반복 사이클 없이도, 실제 인터페이스의 디자인과 설계에 초점을 맞추어 테스트를 진행하였습니다.

단순히 BDD 가 최고야~ 라는 말은 아닙니다. 우리가 BDD 를 통해 TDD 를 좀 더 쉽고, 근접하게 접근할 수 있습니다. 그리고 코드의 구현이 전혀 없이 오직 디자인과 설계에 초점이 맞추어져 있다는 사실을 알면 됩니다. 바로 Moq.NET 은 Mocking Object 를 통해 TDD 와 BDD 개발을 통해 좀 더 품질 좋은 WhiteBox Testing 의 기대효과를 누릴 수 있습니다.

데이터베이스의 데이터를 조회하여 테스트한다고 할 때에도, 반드시 데이터베이스가 존재하지 않아도 됩니다. 가상의 데이터베이스 커넥션 객체를 만들어서 쓰면 됩니다. 쇼핑몰 사이트에서 결재 프로세스를 테스트해야 하나요? 그렇다면 가상의 객체를 통해 설계된 결재 프로세스가 합당한지 Mocking Object 를 통해 테스트를 하면 됩니다. 그 이후에는 잘 설계된 인터페이스를 구현하기만 하면 되겠죠?

   

이 외에도 WikiPedia 에 Mock Library 의 종류가 있네요. 아무튼 테스트와 Moq.NET 에 대한 내용은 여기까지 하는 것으로 마치도록 하렵니다.

 

참고 문헌

http://en.wikipedia.org/wiki/Mock-object
http://behaviour-driven.org/

그렇다면 BDD (Behavior-Driven Development) !

TDD 는 그렇다고 치고, 이제는 BDD(Behavior-Driven Development-행위 주도 개발) 가 왠말이냐 -_-; 저 또한 Moq 에 생소한 나머지 여기까지 추적하게 되었습니다. 모두가 TDD 가 좋은 줄은 압니다. 종속적인 기능이나 코드가 정상적임을 증명하고 점진적으로 테스트 코드를 만듦으로써 자연스럽게 세부 설계를 생각하게 할 수 있습니다.

나에게 "TDD" 를 요구한다면 나에게 "시간"을 달라

어째든, BDD 는 소프트웨어 품질을 향상하기 위해 개발자간에 협력할 수 있는 Agile Software Development 기법입니다. BDD 의 목표는 TDD 를 수행하기 위한 것이며 TDD 의 접근법을 전환한 것입니다. TDD 의 딱딱한 어휘를 정리하고 설계나 디자인에 초점이 맞추어진 패러다임의 전환이라고 합니다. 그리하여 TDD 를 수행한다는 본질은 변하지 않지만, TDD 를 수행하기 위해 BDD 를 통해 행위 자체는 변할 수 있다는 것입니다.

더 자세한 내용은 아래의 Behavior-Driven Development 공식 사이트를 참고하십시오.

Behaviour-Driven Development
http://behaviour-driven.org/

또한 Agile Software Development 에서 각 이터레이션(Iteration)에서 수행하게 될 사용자 스토리를 통해 기능이나 구현에 대한 스팩을 정의할 수 있습니다. 즉, 애자일의 사용자 스토리는 바로 테스트를 수행하는 테스트 시나리오로 이어지게 됩니다. 헌데, TDD 로만 수행되는 테스트 시나리오는 실제로 변화에 능동적으로 대처하지 못할 수 있는 경우도 존재합니다.

예를 들어, 설계자는 개발자에게 아래의 스팩이 만족하는 로그인 기능의 "사용자 스토리" 를 정의합니다.

웹 사이트의 로그인 사용자 스토리

  • 사용자 아이디는 영문만 입력 가능하고 한글은 입력할 수 없다
  • 사용자 아이디는 최소 3자리, 최대 10자리까지 입력가능하고 초과시 경고 메시지를 보여준다
  • 사용자 비밀번호는 최소 5자리, 최대 20자리까지 입력 가능하고 초과시 경고 메시지를 보여준다
  • 사용자 비밀번호는 복잡성 만족도를 우측에 색깔과 메시지로 보여준다

위의 사용자 스토리에 만족하도록 TDD 를 수행해야 하는데, TDD 를 수행하기 위해서는 반드시 스토리의 우선 순위대로 진행해야 다음의 테스트 코드를 작성할 수 있습니다. 그런데 여기에 엄청난 함정이 있을 수 도 있습니다.

  • 초/중반의 우선순위의 사용자 스토리의 규모가 한 이터레이션의 주기와 맞먹는 경우라면 TDD 를 어떻게 수행할건가요?
  • 또는, 로그인 시나리오(에피소드, 테마) 안에 고객의 쉽지 않은 요구사항이 추가되었다면 어떻게 할건가요?

예를 들면, 설계가 진행 도중 아래와 같은 요구 사항이 추가가 되었다고 가정해 봅시다.

  • 로그인은 SSO(Single Sign On) 을 통해 로그인하며, SSO 중앙 서버를 통해 인증해야 합니다.

 

기가 막히군요. 아직 SSO 서버는 구축이 되지 않은 상태이고, 단일 시스템간에 명확한 프로토콜도 정의되지 않은 시점입니다. TDD 를 수행하기 위해 SSO 중앙 서버와 프로포콜이 구축되지 않는다면 더 이상 로그인과 관련된 작업은 진행할 수 없게 됩니다.

자! 바로 이런 경우 행위 주도 개발-BDD 가 빛을 발할 때입니다. BDD 의 행위 주도 개발은 인터페이스와 구현을 분리하고 인터페이스에 집중할 수 있습니다. 즉, 어떠한 구현 코드가 없이도 BDD 를 통해 인터페이스만으로 테스트나 코드를 통해 설계 작업을 진행할 수 있게 되는 것입니다.

구현 코드가 없이 인터페이스만으로 테스트를 진행한다니요? 이거 말장난 아닙니까? 아닙니다. 객체의 구현은 전혀 알 필요 없습니다. 단, 내부적인 테스트 시나리오를 알고 있는 것만으로 테스트를 진행하게 됨으로써, 인터페이스의 디자인이나 설계에 집중하게 됩니다.

이번에 Moq.NET 3.0 버전이 릴리즈 되었습니다. Moq.NET 는 Mocking Object 를 통해 특정 테스트를 진행하고 훨씬 TDD 기반에 근접한 테스팅을 가능하게 합니다. 즉, Mocking Object 는 실제 클래스나 개발이 완료되지 않는 시점에서부터 테스트를 가능하도록 합니다.

그런데 필자는 Moq.NET 를 이해하는 과정에서 내가 알고 있던 것보다 더 깊은 배경이 있었다는 것을 알게 되었습니다. 예를 들어, TDD 외에 BDD 또한 애자일 개발 방법론에 포함된다는 사실과, 조금은 낯설은 BDD-행위 주도 개발을 직접 체험해 보는 과정에서 말입니다. ^^ 

   

왜 TDD (Test-Driven Development) ?

TDD 가 좋으면서 쓰지 않는 이유는 뭘까요? 일반적으로 완벽한 TDD 를 수행하는 과정은 매우 힘듭니다. TDD 에 대한 이론을 들었을때는 확 가슴에 와닿지만, 이것을 몸소 체험하는 과정에서 개발자의 인내력의 한계를 올려놨다 내려놨다 하는 극한을 체험하게 합니다^^; 그러므로 일반적인 TDD 사이클인 Red, Green, Refactor 는 사람을 금방 지치게 만들죠^^; 한 사이클을 마치기 위해서는 많은 시간이 투자되어야 한다는 겁니다.

[그림1] TDD Process

하지만 우리가 TDD 를 수행하는 목적은 이러한 과정에서 코드에 대한 신뢰도를 향상시키고 품질을 향상시키고자 하는 것입니다. 그렇기 때문에 하고자 하는 목적이 보다 나은 품질을 보장하기 위해서라면 TDD 가 필요하다는 것이 머릿속으로만 느낌이 팍팍 옵니다^^;

이제 슬슬 스스로에게 딜레마다 옵니다. 좋은 줄은 알지만 누군가 나에게 TDD 를 강요한다면 아마도 전 "Oh~ No!" 라고 할 것 같네요 -_-;

 

WhiteBox Testing & BlackBox Testing

Moq 를 이야기 하기도 전에 정말 딴소리를 많이 하네요. 일반적으로 테스팅은 크게 WhiteBox Testing 과 BlackBox Testing 으로 구분할 수 있습니다. 이 두 가지 테스팅의 차이는 좁은 범위에서 내부적인 프로세스를 아느냐 모르느냐의 차이고요, 넓은 범위에서는 테스트 레벨의 차이라고 보시면 됩니다.

다음의 그림을 보면 좀 더 이해가 쉬울 겁니다.

[그림2] BlackBox Test 와 WhiteBox Test

즉 BlackBox Test 와 WhiteBox 테스트는 그 목표의 설정이 다르게 됩니다.

BlackBox Test 는 로그인 기능에 대해 요구사항이 있고, 그 기능이 반드시 가져야 할 스팩이 있을 것입니다. 요구사항을 통과하기 위해서는 로그인 기능의 스펙을 만족하면 되고, 실제 단위 테스트 등으로 그런 케이스를 통과를 하면 됩니다. 즉, 내부적으로 어떻게 동작하는지는 알아야 할 필요가 없으므로 수십/수백가지의 테스트 케이스를 통과하여 기능이 정상적으로 동작하는 것을 보장하기 위한 목표입니다.

WhiteBox Test 는 해당 기능에 대해 내부적인 구조를 기반으로 테스트를 진행하게 됩니다. 테스터는 이미 위의 로그인 기능이 내부적으로 어떤 프로세스로 진행되는지 알고 있으며, 예측 가능한 테스트 시나리오를 작성하여 테스트를 진행하게 됩니다. 이 테스트의 목적은 테스트 케이스를 통과하는 BlackBox Test 를 한 단계 뛰어넘어 잠재적인 오류까지 테스트를 통해 잡아내는 것이 목표입니다.
즉, 코드상의 Memory Leak 이나 SQL Injection, Stack Overflow 등 잠재적인 오류를 미리 찾는 경우도 있습니다. 이미 이런 기능은 Visual Studio 2005 이상부터 지원되는 정적 코드 분석(Static Code Analysis) 가 제공이 됩니다. 또, WhiteBox Test 는 코드 커버리지(Code Coverage) 의 수치를 높임으로써 테스트가 안된 코드의 양을 최소화 시키고, 궁극적으로 소프트웨어의 품질을 향상시키는데 있습니다.(Software QA)

새롭게 추가된 Extension Manager

이번 Visual Studio 2010 에서는 새로운 Visual Studio 확장 가능한 기능을 제공합니다. 이전의 Visual Studio Extensibility(VSX) 는 그 개발하기 위한 환경을 갖추는 것도 힘들었지만, 아마도 좋은 확장 기능을 찾는 것이 더 힘들었었지요.

이러한 Visual Studio 확장 기능과 Industry Partner 의 제품을 소개하고 검색할 수 있는 Visual Studio Gallery 웹 사이트가 아마도 가장 신뢰할 수 있고 유일한 곳이었습니다. 지금 보이는 웹 사이트는 Visual Studio 2010 과 서비스를 통합하기 위하여 얼마 전에 개편이 되었습니다. 그리고 새롭게 확장된 기능들로 카테고리도 새롭게 정비하고 추가가 되었네요.

[그림1] Visual Studio Gallery 웹 사이트 리뉴얼

 

새로운 표준적인 배포 패키징

그리고 Visual Studio 2010 은 확장 기능을 신뢰할 수 있고, 쉽게 배포하고 사용할 수 있는 배포 방법으로 VSIX 배포 패키징을 지원하게 되었습니다. VSIX 는 .NET Framework 에서 제공하는 패키징 방법으로 System.IO.Packaging 으로 신뢰할 수 있고 범용적인 ZIP 압축 방법을 통한 패키징 방법입니다.

System.IO.Packaging 은 .NET Framework 3.0 부터 지원하는 XPS Document 를 배포하기 위해 지원이 되었고 최근에는 실버라이트의 XAP 에도 표준적인 패키징 기술이 적용된 것으로 알고 있습니다.

System.IO.Packaging 은 아래의 링크를 통해 자세한 내용을 확인하세요.

A New Standard For Packaging Your Data
http://msdn.microsoft.com/en-us/magazine/cc163372.aspx

 

Visual Studio Extension Manager

Visual Studio 2010 에서는 확장 기능을 개발하기 위해 MEF Component 를 제공합니다. 개발자가 Visual Studio 의 코드 편집기를 가장 많이 사용하고, 그래서인지 Visual Studio 2010 의 WPF 기술은 코드 편집기가 주 타겟이 되었던 것 같습니다. 그래서 코드 편집기는 WPF 를 통해 화려한 모습으로 변신하였고 내부적인 코드 편집기의 아키텍처링도 완전히 바뀐 모습입니다.

새로운 코드 편집기는 기존의 인터페이스를 모두 버리고, Wpf 접두어가 붙힌 IWpfTextView 등의 인터페이스가 대거 등장하게 되었습니다. 그리고 대부분의 확장 기능은 코드 편집기를 쉽게 확장할 수 있는 인터페이스를 제공합니다. 여기에 MEF(Managed Extensibility Framework) 를 사용하여 인터페이스가 원하는 표준적인 메타데이터(Metadata) 만 선언해 주면 쉽게 확장 기능을 개발도 할 수 있습니다.

사설은 여기까지 마치고 Visual Studio 2010 의 Extension Manager 를 구경해 볼까요?

[그림2] Tools>Extension Manager

   

[그림3] 쉬운 확장 기능 검색과 설치

Online Gallery 를 통해 Visual Studio Gallery 사이트에 등록된 Visual Studio 2010 을 지원하는 컴포넌트를 자동으로 검색하고 DownLoad 버튼을 누르면 바로 설치를 할 수 있습니다.

[그림4] Demo Dashboard 확장 기능을 설치한 모습

아직은 많은 확장 기능 컴포넌트가 등록되지 않았지만, Beta 1 버전이 출시가 된지 몇 일 안된것을 고려한다면 그리 적은 양은 아닌 것 같네요. 현재까지 제공되는 확장 기능을 가지고 노는 것만으로 쏠쏠한 재미가 될 듯 하네요...

   

Visual Studio 2010 SDK 와 Readme

VIsual Studio Extensibility 2009. 5. 22. 09:30 Posted by POWERUMC
Visual Studio 2010 Beta 1 이 Microsoft MVP 에게 공개되는 것을 시점으로 지난 20일에는 일반인들에게도 공개가 되었습니다. 이미 구글 검색을 해보면 알겠지만 이번 Beta 1 릴리즈에 대한 소식이 미어 터질 정도로 쏟아져 나오고 있습니다.
 
Visual Studio 2010 Beta 1 SDK 도 Microsoft Download Center 에서 Shell-Integrated 만을 먼저 공개하였습니다. SDK 까지 내놓았다는 것은 이미 Visual Studio 2010 Beta 1 버전임에도 왠만한 IDE 의 아키텍트는 구현이 되었다는 증거겠죠?
 
Visual Studio 2010 SDK Beta 1
 
 
Microsoft MVP 에게는 이번 Beta 1 버전이 나오기 전에 신청자에 한해서 이와 유사한 버전이 공개가 되었었습니다. 이번 Beta 1 과는 빌드 버전이 크게 차이는 없습니다만 이번 Beta 1 에서는 내부적으로 훨씬 많은 기능 향상이 있었습니다. Parallel Debugging 도 완성이 되었고 Visual Studio Gallery 의 리뉴얼과 함께 Extension Manager 도 정상적으로 가동이 되는 듯 합니다.
 
특히나 이유 없이 뻗어버리던 거센 반항은 이번 Beta 1 에서는 겪어보지 못했습니다. (MSDN Forum 에서는 Hang 하신 분들이 몇 분 계시더군요)
 
그런데 문제는 내부적으로 크게 변화된 점은 눈에 띄지 않습니다만, VSX 쪽에서는 미미하게, 그리고 많은 폭으로 (도대체 무슨 의미? -_-) 변화가 찾아왔습니다. 이번 Visual Studio 2010 Beta 1 는 Managed Extensibility Framework 의 Plugin Architect 로 만들어졌으며 Managed Extensibility Framework 을 모르고서는 Visual Studio 2010 을 안다고도 할 수 없어졌습니다. 이미 Beta 1 의 상당한 확장 기능은 MEF 로 구현되었고 이번에 새롭게 추가된 Extension Manager 의 확장 기능도 MEF 가 그 기본이 되고 있기 때문입니다.
 
 
어쨌든 기존의 Visual Studio 2008 의 Package 들을 Visual Studio 2010 으로 마이그레이션 하면서 파악하기 힘든 오류를 턱턱 뱉어냈는데 다행히도 아래의 링크에서 그 문제 해결 방법을 가이드하고 있네요.
 
[그림1] Visual Studio 2008 Package 마이그레이션 오류
 
Visual Studio 2010 SDK Beta 1 Readme
 
 
일단 Visual Studio 2010 의 출발은 굉장히 시원시원 한 것 같습니다. 아직은 기존의 Visual Studio 2008 버전의 대부분이 Visual Studio 2010 으로 컨버팅 된 수준입니다만 2010 버전을 시점으로 IDE 도 Managed 환경으로 탈바꿈 할 준비를 하려는 모양입니다.
 
약간의 거부 반응을 일으켰던 2010 버전의 UI 도 이제는 포근해져가네요. 얼른 기존의 프로젝트를 2010 버전으로 변환해야겠네요^^
 
알아 두면 피가 되고 살이 되는 닷넷엑스퍼트 사람들의 포스트
 
기타 참고
 


Querying the CompositionContainer
 
CompositionContainer 의 Exported 구성 요소를 취득하기 위해서는 컨테이너에 하나의 구성 요소만이 존재해야 합니다. 쿼리(Query) 를 통해 이러한 객체들이 여러 개 존재할 경우 MEF 는 예외를 발생하게 됩니다.
 
바로 아래와 같은 경우이죠.
l 하나의 인스턴스를 요청할 때, 인스턴스를 찾지 못했을 경우
l 하나의 인스턴스를 요청할 때, 인스턴스가 여러 개일 경우
 
GetExportedObject
 
일반적으로 ExportAttribute 에 인자가 전달되지 않은 경우는 클래스의 타입이 키 값이 되어, 아래와 같이 구성 요소를 취득할 수 있습니다.
 
class Program
{
        static void Main(string[] args)
        {
               var container = new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
              
               var export = container.GetExportedObject<Export1>();
               export.Say();
 
               Console.ReadKey();
        }
}
 
[Export]
class Export1
{
        public void Say()
        {
               Console.WriteLine("Export1 Say..");
        }
}
 
 
만약, ExportAttribute 에 Contract Name 이 선언이 될 경우는 클래스의 타입 정보와 Contract Name 이 일치해야 구성요소를 취득할 수 있습니다.
 
class Program
{
        static void Main(string[] args)
        {
               var container = new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
              
               var export = container.GetExportedObject<Export2>("export2");
               export.Say();
 
               Console.ReadKey();
        }
}
 
[Export("export2")]
public class Export2
{
        public void Say()
        {
               Console.WriteLine("Export2 Say..");
        }
}
 
 
GetExport
 
만약 여러 개의 구성 요소의 인스턴스가 필요하다면 GetExport 메서드를 통해 Export 의 구성 정보를 가져오면 됩니다.
 
var exports = container.GetExport<Export1>();
exports.GetExportedObject().Say();
 
필요하다면 ExportAttribute 의 Contract Name 으로 질의(Query) 할 수 있습니다.
 
var exports = container.GetExport<Export2>("export2");
exports.GetExportedObject().Say();
 
아쉽게도 MEF Preview 4 까지 지원하던 C# 3.0 의 Expression 을 통해 질의하는 방법은 MEF Preview 5 이후 없어진 것이 아쉽네요.
 
GetExportedObjectOrDefault
 
일반적으로 MEF 에서는 질의(Query) 결과가 없을 경우 예외를 발생하게 되는데, GetExportedObjectOrDefault 메서드를 통해 결과가 없을 경우 Null 값으로 대체할 수 있습니다.
 
var obj = container.GetExportedObjectOrDefault<Export1>("A");
if( obj != null )
        obj.Say();
 
 

'Managed Extensibility Framework' 카테고리의 다른 글

MEF 는 Generic Type 을 지원하지 않는다!  (0) 2010.01.29
MEF Preview 6 공개  (0) 2009.07.20
[MEF] 9. Recomposition  (1) 2009.04.19
[MEF] 8. Strongly Typed Metadata  (0) 2009.04.16
[MEF] 7. Exports and Metadata  (0) 2009.04.16
Visual Studio Team System 2010 Beta 1 이 다가오는 월요일(2009-05-18) 에 MSDN 을 통해 공개가 되고 다운로드 할 수 있다고 합니다. 단, 일반인들에게는 20일에 다운로드가 가능하다고 합니다.
 
 
 
Visual Studio Platform 팀에서 개발 리드를 담당하고 있는 Pedro Silva 씨는 블로그에 이번 Beta 1 에는 Visual Studio SDK 도 포함이 된다고 합니다. SDK 도 포함이 된 것으로 추정하면 대부분의 Visual Studio 2010 의 기능은 이전 버전보다 완성도가 상당히 높아졌으리라 생각합니다. 이전 버전에서는 Visual Studio 2010 이 이전 버전과의 비호환적인 부분이 많았는데 어떻게 변했을지 더욱 궁금해지네요.
 
Visual Studio 2010 에 대한 공식 Product 는 여기 에서 참고하시면 됩니다.
 
References
 
 


[MEF] 9. Recomposition

Managed Extensibility Framework 2009. 4. 19. 22:47 Posted by POWERUMC

Recomposition

 

이전 포스트의 MEF 의 특징 중에 MEF 의 플러그인 모델(Plugin Model) 은 교체가 용이하다고 하였습니다. Composable Part 는 구성 요소로써 고유의 기능을 구현합니다. 그리고 MEF 는 각각의 Composable Part 를 조립하여 다양한 컴포넌트 또는 애플리케이션을 완성합니다.

 

어떠한 경우에는 특정한 구성 구성요소를 사용하다가 그것이 필요 없어질 경우 구성 요소를 언로드(Unload) 하거나 다른 구성요소로 교체할 필요가 있습니다.

 

MEF 의 이러한 유연함의 예를 들어보죠.

 

예를 들어, 어플리케이션의 로그(Log) 기능을 생각해볼 수 있습니다. 만약 어플리케이션이 동작하는 환경이 인터넷에 연결되지 않는 환경이라면 사용자의 컴퓨터 로컬에 로그를 기록하다가, 인터넷에 연결될 경우 외부 데이터베이스로 로그를 기록하는 시나리오를 가정할 수 있습니다. 그리고 이러한 기능 또는 요구 사항이 언제 변경될지도 모르는 일입니다.

 

일단 위의 시나리오를 구현하기 여러 가지 고려해야 할 사항이 있고, 기능 또는 요구 사항이 변경될 경우도 고려해야 합니다. 결국, MEF 는 이러한 변화에 굉장히 유연하게 대처할 수 있습니다.

 

MEF 에서 ImportAttribute AllowRecomposition 프로퍼티를 통해 구성 요소를 런타임(Runtime) 시 동적(Dynamic) 으로 교체할 수 있도록 합니다.

 

아래는 위의 예로 든 시나리오를 구현하기 위해 작성한 간단한 소스 코드 입니다.

 

ILogger 인터페이스

public interface ILogger

{

        void Write();

}

 

ILogger 인터페이스는 Write 메서드를 통해 로그를 기록할 수 있는 예입니다.

 

 

TextLogger 클래스

[Export(typeof(ILogger))]

public class TextLogger : ILogger

{

        public void Write()

        {

               Console.WriteLine("Logged TextLogger");

        }

}

 

 

DatabaseLogger 클래스

[Export(typeof(ILogger))]

public class DatabaseLogger : ILogger

{

        public void Write()

        {

               Console.WriteLine("Logged DatabaseLogger");

        }

}

 

 

LoggerContext 클래스

[Export]

public class LoggerContext

{

        [Import(AllowRecomposition=true)]

        public ILogger Context { get; set; }

 

        [ImportingConstructor]

        public LoggerContext(ILogger sender)

        {

        }

}

 

여기에서 ImportAttribute AllowRecomposition 프로퍼티의 값을 true 로 지정해 주었습니다. AllowRecomposition 프로퍼티가 true 일 경우 Imported 된 구성 요소를 동적(Dynamic)하게 교체할 수 있도록 합니다.

 

 

Main 어플리케이션

class Program

{

        static void Main(string[] args)

        {

               Program p = new Program();

               p.Run();

        }

 

        void Run()

        {

               var catalog    = new AggregateCatalog(new TypeCatalog(typeof(LoggerContext)));

 

               var container = new CompositionContainer(catalog);

                var batch = new CompositionBatch();

               var defaultPart = batch.AddPart(new TextLogger());

               container.Compose(batch);

 

               var obj = container.GetExportedObject<LoggerContext>();

 

               obj.Context.Write();

              

               batch = new CompositionBatch();

               batch.RemovePart(defaultPart);

               batch.AddPart(new DatabaseLogger());

               container.Compose(batch);

 

               obj.Context.Write();

        }

}

 

이 코드는 처음에 TextLogger 를 사용하다가, 필요 없어진 TextLogger 를 제거하고 DatabaseLogger 로 교체하는 코드입니다.

 

아래는 위의 소스 코드를 실행한 결과입니다.

 

[그림1] 소스 코드 실행 결과

 

 

Wow! Recomposition

 

사실 이러한 것이 기존에는 기능의 정의 또는 요구 사항에 따라 직접 구현할 수 있었지만, MEF 의 특징인 플러그인 모델(Plugin Model) 은 이러한 고민에 대해 좋은 방법을 제공해 줍니다. 개발자는 자신이 구현해야 할 기능에 더 충실하고, 비즈니스 로직에 대한 고민만을 하면 됩니다. 그리고 정책에 따라 그것을 집행하는 결정권을 가진 자는 구현된 구성 요소를 조립하고 교체하여 기존의 정적인(Static) 어플리케이션에게 동적인(Dynamic) 유연함을 제공합니다.

 

기존에 정적인 어플리케이션은 변화에 따라 유지 보수를 위해 지속적인 많은 리소스가 필요하였지만, 플러그인 모델(Plugin Model) 의 동적인 어플리케이션은 그러한 변화에 능동적으로 대처할 수 있는 큰 기쁨을 줄 수 있을 것입니다.

'Managed Extensibility Framework' 카테고리의 다른 글

MEF Preview 6 공개  (0) 2009.07.20
[MEF] 10. Querying the CompositionContainer  (0) 2009.05.18
[MEF] 8. Strongly Typed Metadata  (0) 2009.04.16
[MEF] 7. Exports and Metadata  (0) 2009.04.16
[MEF] 6. Lazy Exports  (0) 2009.04.13