시작하기 전에
 
MEF 는 이미 CodePlex 사이트의 Wiki 에 코드를 중심으로 설명이 잘되어 있습니다. 그렇기 때문에 저도 CodePlex 의 사이트를 참고하여 나름대로 각색하여 작성을 하고자 합니다. 레퍼런스가 이미 CodePlex 에 충분하지만, 저는 나만의 시각에서 바라보고 느낀 바를, 그리고 소스 코드를 만들어 가고자 합니다^^ (사실 Wiki 의 설명은 미약할 다름입니다^^;)
 
CodePlex 의 Wiki 를 먼저 보실 분은 최신 버전이 적용이 되지 않은 예제도 있으니 이런 부분은 조심해서 리뷰하시기 바랍니다.
 
 
어플리케이션에 MEF 호스팅하기
 

[그림1] Composition Container
 
Composable Part
 
MEF 에 어플리케이션을 호스팅하기 위해서는 몇 가지의 반복적인 절차를 거치면 됩니다. 먼저 MEF 를 호스팅할 수 있는 컨테이너(Container) 를 만들어야 합니다. 컨테이너는 MEF 에서 상위 그룹에 존재하며 MEF 의 파트(Part) 를 관리하며 핵심 역할을 담당합니다.
 
MEF 의 컨테이너는 배치(Batch) 를 구조적으로 구성하고 캡슐화 합니다. 배치(Batch) 의 각 구성 단위를 파트(Part) 라고 부릅니다. 각 파트(Part) 는 닷넷 어셈블리에서 타입(Type) 이 될 수 있으며, 타입(Type) 을 배치(Batch) 로 등록할 수 있습니다.
 
파트(Part) 는 메인 호스트(Main Host) 가 반드시 포함되어야 하며, 아래의 소스 코드에서 보는 것처럼 this 키워드가 바로 메인 호스트(Main Host) 가 됩니다.
 
Contract
 
이러한 Composable Part 는 다른 컴포넌트와 의존 관계를 갖지 않습니다. 모든 외부 기능(Export) 는 계약(Contract) 를 맺게되며 이것이 필요할 경우에는 Import 할 수 있습니다.
 
MEF 의 컨테이너는 Contract 정보의 타입 정보나 메타 데이터를 통해 Export 와 Import 를 매칭합니다.
 
 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
    }
 
    public void Run()
    {
        var container = new CompositionContainer();
        var batch = new CompositionBatch();
        batch.AddPart(this);
        container.Compose(batch);
    }
}

 
이러한 파트(Part) 를 제어하는 작업은 어플리케이션 차원에서 유일해야 하므로 스레드(Thread) 작업에 안전하도록 lock 으로 블로킹(Blocking) 되어있습니다.
 
이 작업은 메인 호스트(Main Host) 를 등록하는 과정에 불과하며 실행 시에 아무런 결과가 없습니다.
 
 
외부로 기능 노출하기 (Export)
 
이제 어떤 플러그인(Plugin) 을 외부로 노출해야 합니다. 외부로 노출하는 이유는 내/외부에서 노출된 확장 기능을 가져다 쓰기 위해서입니다. 확장 기능을 노출하기 위해서는 단지 Export Attribute 을 정의해 주면 됩니다.
 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
namespace MEFLab_Hosting_MEF
{
    class Program
    {
        static void Main(string[] args)
        {
               Program p = new Program();
               p.Run();
        }
 
        public void Run()
        {
               var container = new CompositionContainer();
               var batch = new CompositionBatch();
               batch.AddPart(this);
               container.Compose(batch);
        }
    }
 
    public interface IHelloWorld
    {
        void Say();
    }
 
    [Export(typeof(IHelloWorld))]
    public class HellowWorld : IHelloWorld
    {
        public void Say()
        {
               Console.WriteLine("Hello MEF!");
        }
    }

 
인터페이스(Interface) 를 구현한 HelloWorld 클래스는 Export Attribute 를 정의합니다. Export 의 파라메터는 메타 데이터(Meta Data)로 사용되기 때문에 타입(Type) 을 정의해 주어야 합니다.
 
혹자는 왜 인터페이스를 정의해야 하느냐고 궁금해 하기도 합니다. 정답을 드리자면, 반드시 인터페이스를 정의하지 않아도 됩니다. 하지만 특히 MEF 에서 인터페이스는 다형적이고 표준적인 메타 데이터를 제공하기 위해 인터페이스를 선언하여 사용하는 것이 좋을 것 같네요^^
 
 
외부 기능 가져오기 (Import)
 
Export Attribute 으로 외부로 노출된 확장 기능은 Import Attribute 으로 가져올 수 있습니다. 단, 배치(Batch) 의 파트(Part) 로 등록이 되어 있어야 합니다. 내부적으로 Import 의 타입을 생략할 경우 Export 의 타입의 메타 데이터를 통해 Import 의 개체를 쿼리(Query) 하여 생성하게 됩니다.
 

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
    }
 
    [Import]
    IHelloWorld HelloWorldCompoent { get; set; }
 
    public void Run()
    {
        var container = new CompositionContainer();
        var batch = new CompositionBatch();
        batch.AddPart(new HellowWorld());
        batch.AddPart(this);
        container.Compose(batch);
 
        HelloWorldCompoent.Say();
    }
}
 
public interface IHelloWorld
{
    void Say();
}
 
[Export(typeof(IHelloWorld))]
public class HellowWorld : IHelloWorld
{
    public void Say()
    {
        Console.WriteLine("Hello MEF!");
    }
}

 
이 예제에서 알 수 있듯이 코드에서 모듈(Module) 이나 타입(Type) 의 관계를 크게 신경 쓰지 않았습니다. Export 와 Import 를 통해서 의존(Dependency) 는 어느 정도 해소된 것처럼 보입니다. (인터페이스만 잘 활용하면 MEF 없이도 충분히 가능합니다)
 
이것이 끝이라면 정말 재미가 없겠죠. 차차 나오게 될 내용이지만, 이러한 Export 와 Import 를 활용하고 Lazy Load, 그리고 메타 데이터를 이용한 쿼리(Query) 를 활용하게 되면 복잡한 기능을 획기적으로 단순화 할 수 있습니다.
 
 

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

[MEF] 6. Lazy Exports  (0) 2009.04.13
[MEF] 5. Catalog 사용  (0) 2009.04.09
[MEF] 4. Import 선언  (0) 2009.04.07
[MEF] 3. Export 선언  (0) 2009.03.29
[MEF] 1. Managed Extensibility Framework 이란?  (2) 2009.03.16
MEF (Managed Extensibility Framework) 란?
 
Menaged Extension Framewkr(이하 MEF) 란? 가장 쉽게 얘기하자면, 어플리케이션과 컴포넌트의 재사용성을 높일 수 있는 프레임워크입니다. 기존의 어플리케이션은 하나의 목적을 하나의 어플리케이션으로 구현한 정적인(Statically) 어플리케이션이라면, MEF 는 보다 동적인(Dynamically) 어플리케이션을 구축할 수 있는 새로운 라이브러리를 제공합니다.
 
기존의 정적인 어플리케이션은 새로운 요구사항으로 기능을 확장할 필요가 있을 경우 새로운 빌드 버전을 필요로 합니다. 그리고 새로운 빌드 버전은 기존 어플리케이션과 확장된 기능간에 종속적인 관계를 탈피할 수 없었습니다. 이미 프로그램 언어를 사용하여 코드를 작성하는 분들이라면 반복적이거나 확장에 대해 많은 고민을 한번쯤 해보았을 것입니다.
 

[그림1] MEF 의 컨셉 이미지
 
MEF 는 플랫폼의 저레벨(Low Level) 에서 공통된 추상적인 기능을 제공하고 여러 곳에 흩어진 이중 작업을 감소시킬 수 있습니다.
 
몇 달 전 마이크로소프트에서는 Application Framework Core 팀이라 부르는 팀이 만들어졌다고 합니다. 이 팀은 ASP.NET, Windows Forms, WPF, Silverlight 의 여러 플랫폼에서 동작하기 위한 BCL(Base Class Library) 과 같은 역할을 한다고 합니다.
 
 
MEF 는 플러그인 모델(Plugin Model)
 
MEF 는 플러그인 모델(Plugin Model) 이라고도 부릅니다. 플러그인 모델을 잘 모르신다면, 전기 콘센트의 플러그를 연상하시면 됩니다. 전기 콘센트의 플러그는 그 수가 제한되어 있지만 어댑터를 추가로 구매하여 꽃으면 몇 개의 콘센트를 꽃을 수 있습니다. 그리고 TV 나 컴퓨터를 사용하고 싶다면 콘센트에 꽃기만 하면 TV 와 컴퓨터를 동작시킬 수 있습니다.
 

[그림2] MEF 는 플러그인 모델(Plugin Model) 이다. (출처는 여기)
 
만약 더 좋은 TV 나 컴퓨터가 생겼다면 플러그를 빼고 더 좋은 TV 나 컴퓨터의 플러그를 콘센트에 꽂기만 하면 됩니다.
 
이처럼 MEF 는 어플리케이션의 확장을 위한 보다 유연한 아키텍처를 제공하며, 약간의 개념만 이해한다면 사용하기도 쉽습니다.
 
 
어디서 많이 본 듯한 아키텍처?
 
맞습니다. 저도 MEF 를 처음 보는 순간 느꼈습니다. MEF 의 외관은 마치 DI(Dependency Injection) 과 IoC(Inversion of Control) 의 그것과 매우 유사해 보였습니다.
 
용어 설명
 
DI(Dependency Injection) / 의존성 주입
객체간의 의존성과 결합도를 낮추기 위한 기술
 
IoC(Inversion of Control) / 역제어
인스턴스의 관리를 컨테이너에서 인스턴스의 생성과 소멸의 생명주기를 관리
 
하지만 MEF 는 DI 나 IoC 와 전혀 다릅니다. 위에서 얘기한대로 MEF 는 플러그인 모델입니다. 어플리케이션의 확장을 위한 프레임워크이며, DI 와 IoC 는 잊어버리십시오! 아래의 단원에서 DI 와 IoC 를 잊을 수 있는 분명한 근거를 설명하도록 하겠습니다.
 
 
플러그인 모델(Plugin Model) 로 뭘 할 수 있나요?
 
MEF 의 플러그인 모델(Plugin Model) 은 보다 나은 확장성을 제공해 줍니다. 마치 Visual Studio 의 Addin 을 연상하시면 됩니다. Visual Studio Addin 은 Visual Studio 의 기능을 쉽고 강력하게 활용할 수 있도록 도와주며 전혀 새로운 기능을 추가할 수 있는 플러그인(Plugin) 입니다. 그럼 MEF 의 플러그인 모델(Plugin Model) 은 어떠한 장점이 있을까요?
 
MEF 의 플러그인 모델은 아래와 같은 장점을 제공합니다.
 
l 확장 기능에 대한 인스턴스를 생성하는 방법(SingleCall, Singleton)을 제공하며, 인스턴스의 회수할 수 있어야 합니다.
l 상호작용을 할 수 있습니다. 확장 기능간에 메시지의 전달이 쉽고 용이해야 합니다. Global Service 처럼 상호간의 인터페이스가 제공되어야 하고 이러한 메시지에 응답할 수 있어야 합니다.
l 확장 기능은 Lazy Load 할 수 있어야 합니다. 확장 기능은 원하는 시기에 로드해야 합니다. 마치 VSIP(Visual Studio Integration Package) 의 VSCT 와 같이 인터페이스나 이벤트는 노출이 되어 있지만 실제로 확장 기능이 로드된 것은 아닙니다.
l 확장이 용이해야 합니다. 예를 들어, 확장 기능이 특정 폴더(Addin 폴더라고 가정)로 복사된다면 이를 감지하여 인터페이스를 제공하거나 확장 기능이 활성화 되어야 합니다.
l 교체가 쉬워야 합니다. 새로운 기능이 추가된 확장 기능은 교체가 쉬워야 하며, 필요 없어진 확장 기능을 언로드(Unload) 할 수 있어야 합니다.
 
MEF 는 여러분의 정적인 어플리케이션에 동적인 생명력을 불어넣어 줄 수 있으며, 그 근간은 바로 플러그인 모델(Plugin Model) 입니다.
 
이러한 플러그인 모델은 근본적인 상호간의 의존(Dependency) 관계를 보다 자연스럽게 해결할 수 있으며, 유연하고 매끄럽게 다룰 수 있을 것으로 기대합니다.
 
MEF 설치하기
 
MEF 는 .NET Framework 4.0 에 포함될 예정입니다. 하지만 아쉽게도 .NET Framework 4.0 CTP 버전에는 포함되어 있지 않기 때문에 Visual Studio Team System 2010 의 VPC 이미지에서도 테스트할 수 있는 환경이 갖추어지지 않았습니다.
 
현재 2009-03-16 기준으로 MEF 는 Preview 4 버전이며 CodePlex 에서 소스코드와 바이너리를 다운로드 받을 수 있습니다.
 
MEF (Managed Extensibility Framework) 다운로드
 
 
 
시작이 반이다! 친절한 HelloWorld 따라하기
 
자… 이제 MEF 도 설치했고 언제나 그렇듯 HelloWorld 를 콘솔에 출력하는 코드를 작성해 보도록 합시다. 소스 코드에 대한 설명은 다음 단원으로 넘기도록 하고, 오늘은 MEF 가 어떻게 동작하는지 눈대중만 익히시면 될 것 같습니다.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
namespace ConsoleApplication1
{
       class Program
       {
             static void Main(string[] args)
             {
                    Component com = new Component();
            
                    var catalog = new AggregateCatalog();
                    catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
 
                    var container = new CompositionContainer(catalog);
                    var batch = new CompositionBatch();
                    batch.AddPart(com);
                    container.Compose(batch);
 
                    com.HelloWorldComponent.Say();
             }
       }
 
       public class Component
       {
             [Import]
             public IHelloWorld HelloWorldComponent { get; set; }
       }
 
       public interface IHelloWorld
       {
             void Say();
       }
 
       [Export(typeof(IHelloWorld))]
       public class HelloWorld : IHelloWorld
       {
             public void Say()
             {
                    Console.WriteLine("Hello MEF");
             }
       }
}
 
 
출력 결과는
 

[그림3] 소스 코드 실행 결과
 
혹자는 HelloWorld 를 찍는데 왜 이렇게 많은 코드가 필요한가에 불만이 있을지도 모릅니다. 맞습니다. 단순히 결과가 HelloWorld 가 출력되기만 바란다면 이렇게 긴(?) 코드는 필요가 없을지도 모릅니다. 하지만 위의 소스 코드는 보다 큰 어플리케이션을 만들기 위한 초석이며 실제로 큰 어플리케이션에서는 훨씬 적은 코드로 복잡한 요구사항을 구현할 수 있을지도 모르는 일입니다^^
 
 
MEF 는 아직은 Preview
 
언제나 그렇듯이 새로운 프레임워크는 새로운 패러다임을 가져옵니다. 그리고 보다 나은 아키텍처와 개발 방법을 제시합니다. 물론 MEF 도 더 깊이 살펴볼수록 그 놀라운 기능에 감탄을 하게 될 것입니다.
 
하지만 아직 MEF 는 Preview 입니다. MEF 의 플러그인 모델은 충분히 활용 가치가 있지만 아직은 실무에서 즉시 도입하기엔 많은 고민이 따를 것 같습니다. 그리고 이런 애로 사항이 아마로 정식 버전에서는 해결되리라 생각합니다. 시간이 되면 이 부분도 한번 다루어 보도록 하겠습니다.

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

[MEF] 6. Lazy Exports  (0) 2009.04.13
[MEF] 5. Catalog 사용  (0) 2009.04.09
[MEF] 4. Import 선언  (0) 2009.04.07
[MEF] 3. Export 선언  (0) 2009.03.29
[MEF] 2. Parts 와 Contracts 선언  (0) 2009.03.22
Kirill Osenkov 은 새로운 Visual Studio 2010 의 Visual Studio 2010 의 언어와 IDE 를 다루는 약 30분 정도 동영상을 촬영하여 공개하였습니다. 이 동영상은 매우 기초적인 내용만을 다루며, 기능에 대한 상세한 부분은 다루지 않는다고 합니다.
 
동영상에서 보는 Visual Studio 2010 은 WPF Shell 을 적용한 IDE 로 진행합니다. 그리고 이미 Visual Studio 2010 CTP 를 사용해본 분이라면 눈치 채셨겠지만, 동영상의 Visual Studio 2010 은 최근의 Internal Build 버전이라고 합니다. 그렇기 때문에, Visual Studio 2010 CTP 의 불안정한 WPF Shell 의 모습과 비교할 때 더욱 안정적이고 신선한 모습입니다.

이제 Visual Studio 2010 의 베타 버전이 임박한 듯 합니다.
 
Visual Studio 2010 Screencast: C# 4.0 Language + IDE + WPF Shell + Editor