최근 2년 동안 다양한 개발 분야의 기술들이 물망에 오르는 굉장히 뜻 깊은 해였습니다. 2년 전이면 Microsoft 강성재 차장과 함께 처음으로 "Visual Studio 한국 공식 팀"을 창설하면서 http://vsts2010.net 이 탄생한 시기이군요. 2008년 12월에 팀이 창설되고, 2009년 1월 5일이 팀 블로그 2주년이 되는 날이었군요.

바로 저희 "Visual Studio 한국 공식 팀" 블로그에서 한홀 한홀 정성스럽게 작성된 포스트들이 2년 여간의 기술 흐름을 대변해 주고 있으며, 그리고 2011년의 기술도 짐작해 볼 수 있는 짧지만 굵은 변화의 흐름과 함께 여기까지 온 것 같습니다.

우리 팀이 함께 해왔던 핵심 키워드의 태그는 무엇이었을까요?

  • Visual Studio 2010
  • .NET 4.0, .NET Framework 4.0
  • ASP.NET MVC
  • C# 4.0
  • C++0x, C++/CLI
  • Parallel Computing
  • WCF
  • Cloud
  • Application Lifecycle Management

   

그리고 위의 태그들에 대해 더 자세히 살펴보더라도 생소한 기술과 이름, 아키텍처, 환경 등이 2년 동안 격변을 일으키며 변화를 해왔다는 사실입니다.

2011년 이전까지는 여러분들에게 선택권이었던 것들이, 이제는 필수가 되어야 한다고 해도 과언이 아닐 겁니다. 비즈니스 요구사항의 단면을 보면 업무적인 요인, 시대적인 배경 등인데, 이 시대적인 배경에는 트랜드+시장+기술+… 이 있을테고요. 그리고 '우리가 이 시대적인 배경 중 '기술'에 한 배를 타고 흐르고 있는가…?' 에 다시 한번 생각해 볼만 합니다.

예전 2010년 6월 1일 REMIX10 세미나에서 여러분에게 말씀 드린 마지막 문구가 다시금 생각이 나네요.

http://www.techdays.co.kr/2010spring/remix10/session3_1.html

   

여러분의 생존전력은 바로 아래에 해답이 있습니다. 여러분들에게 필요한 것, 그리고 그 가능성이 있다고 판단하시면 2011년 생존을 위하여 달려보는 것은 매우 멋진 2011년 한 해가 될 것입니다.

   

.NET 프레임워크

.NET Framework .NET 의 과거와 현재, 그리고 미래
.NET Framework .NET Framework 4.0 의 특징
.NET Framework .NET Framework 4.0 마이그레이션 이슈
.NET Framework .NET 스마트클라이언트 한계 극복 [1]
.NET Framework .NET 스마트클라이언트 한계 극복 [2]
CLR 1. Hello 世界
CLR 2. CLR! CLR! CLR!
CLR 3. MSCorLib & Metadata
CLR 4. Assembly
CLR 5. Assembly - Strongly named assemblies
CLR 6. Assembly - GAC(Global Assembly Cache)
CLR 7. System.Object
CLR 8. System.Object (2)
CLR 닷넷4.0에서 네이티브코드와 매나지드코드의 동거 part 1.
CLR 닷넷4.0에서 네이티브코드와 매나지드코드의 동거 part 2-1.
CLR 닷넷4.0에서 네이티브코드와 매나지드코드의 동거 part 2-2. 네이티브 랩퍼 만들기
Managed Extensibility Framework [MEF] 1. Managed Extensibility Framework 이란?
Managed Extensibility Framework [MEF] 2. Parts 와 Contracts 선언
Managed Extensibility Framework [MEF] 3. Export 선언
Managed Extensibility Framework [MEF] 4. Import 선언
Managed Extensibility Framework [MEF] 5. Catalog 사용
Managed Extensibility Framework [MEF] 6. Lazy Exports
Managed Extensibility Framework [MEF] 7. Exports and Metadata
Managed Extensibility Framework [MEF] 8. Strongly Typed Metadata
Managed Extensibility Framework [MEF] 9. Recomposition
Managed Extensibility Framework [MEF] 10. Querying the CompositionContainer
Managed Extensibility Framework MEF Preview 6 공개
Managed Extensibility Framework MEF 는 Generic Type 을 지원하지 않는다!
Managed Extensibility Framework MEF 에 Generic Type 을 지원하기 위해서..?
Managed Extensibility Framework MEFGeneric 코드 플랙스에 공개합니다.

   

애자일 개발

Agile Development [Better Code]TDD의 개념이 완벽히 녹아 들어간 VSTS 2010
Agile Development [Better Code]Visual Studio 2010 Code Analysis Enhancements - 1.개요
Agile Development [Better Code]Visual Studio 2010 Code Analysis Enhancements - 2. Rule Sets Feature
Agile Development [Better Code]PEX, Automated Whitebox Testing for .NET - 1. 개요
Agile Development [Better Code]Visualize Code Relationships
Agile Development [Testing] TDD (Test-Driven Development-테스트 주도 개발)
Agile Development [Testing] BDD (Behavior-Driven Development?행위 주도 개발)
Agile Development [Testing] Moq.NET (T/B Driven Development)
Agile Development [Better Code]Visual Studio Code Analysis Enhancements - 3. Data Flow Rules and Phoenix Engine
Agile Development 애자일에 대한 고찰
Agile Development [ALM-Test] 1. 왜 단위 테스트를 해야 하는가?
Agile Development [ALM-Test] 2. 한국적인 애자일 모델의 필요성
Agile Development [협업 1] 협업 도구의 통일성과 협업 인프라 관리
Agile Development [ALM-Test] 3. 테스터에 대한 오해와 진실
Agile Development [ALM-Test] 4. 테스터(SDET) 의 역할
Agile Development [ALM-Test] 5. 테스트 계획
Agile Development [ALM-Test] 6. Load Runner vs Visual Studio 2010 테스팅 비교 분석 - http://willstory.tistory.com/4 제공
Agile Development [ALM-Test] 7. TDD vs 계약기반 테스트
Architect Development Architect Development ?
Architect Development 몽당연필과 함께하는 VSTS 2010 모델링 0/4
Architect Development 몽당연필과 함께 하는 VSTS 2010 모델링 1/4
Architect Development Windows Server AppFabric - Velocity 란?
Architect Development WCF=SOA 에 대한 고찰

   

ASP.NET 4.0

ASP.NET 4.0 [ASP.NET 4.0] 1. Core Service - Extensible Output Caching
ASP.NET 4.0 [ASP.NET 4.0] 2. AJAX - Declarative Client Template Rendering
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Dynamic Data(1)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Dynamic Data(2)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Designer & Deployment
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Core Services
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - New Features in the Microsoft Ajax Library
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(1)
ASP.NET 4.0 Razor in WebMatrix
ASP.NET 4.0 Razor in WebMatrix(2) 코드의 재 사용
ASP.NET 4.0 Razor in WebMatrix(3) ? WebMatrix Helper
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(2)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(3)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(4)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(5)
ASP.NET MVC M, V 그리고 C의 각방생활(1) - ASP.NET MVC vs ASP.NET WEB FORM
ASP.NET MVC M, V 그리고 C의 각방생활(2) - ASP.NET MVC와 인사나누기
ASP.NET MVC M, V 그리고 C의 각방생활(3) - 초간단 사이트 만들기(1)
ASP.NET MVC M, V 그리고 C의 각방생활(4) - 유효성 검사
ASP.NET MVC M, V 그리고 C의 각방생활(5) - 초간단 사이트 만들기(2)
ASP.NET MVC M, V 그리고 C의 각방생활(6) - 유효성 검사(2)
ASP.NET MVC M, V 그리고 C의 각방생활(7) - 함께 즐겨요~ jQuery
ASP.NET MVC M, V 그리고 C의 각방생활(8) - jQuery와 탭메뉴 그리고 파샬뷰
ASP.NET MVC M, V 그리고 C의 각방생활(9) - jqGrid 사용해보자
ASP.NET MVC M, V 그리고 C의 각방생활(10) - jqGrid를 이용한 paging과 sorting
ASP.NET MVC ASP.NET MVC 3 Preview 1 이 릴리즈 되었습니다.
ASP.NET MVC M, V 그리고 C의 각방생활(11) - jqGrid로 데이터 추가,편집,삭제해보기
ASP.NET MVC M, V 그리고 C의 각방생활(12) - 테스팅 그거, 아무나 하나?
ASP.NET MVC JailBreak From Controllers and Actions
ASP.NET MVC VSTS2010 에서 Razor 코드 하이라이팅 지원하기

   

C# 4.0

C# [C# 4.0] Named and Optional Parameters
C# [C# 4.0] Duck Typing
C# [C# 4.0] New Extension Method "Zip"
C# [C# 4.0] Generic Covariance And Contra Variance
C# Welcome to Dynamic C#(1) - 첫만남.
C# Welcome to Dynamic C#(2) - Wanna be a polyglot.
C# Welcome to Dynamic C#(3) - 마음이 넒어진 C#
C# Welcome to Dynamic C#(4) - 극과극 비교체험.
C# Welcome to Dynamic C#(5) - Return to Dynamic.
C# Welcome to Dynamic C#(6) - Return to Dynamic (2)
C# Welcome to Dynamic C#(7) - 아낌없이 표현해 주는 나무
C# Welcome to Dynamic C#(8) - DLR이 나무를 사랑하는 이유
C# Welcome to dynamic C# 외전(1) - Generate From Usage.
C# Welcome to dynamic C# 외전(2) - Generic Method.
C# Welcome to dynamic C# 외전(3) - 감시하는 자와 감시당하는 자.
C# Welcome to Dynamic C#(9) - Dynamic Returns Again.
C# Welcome to Dynamic C#(10) - Dynamic Returns Again.(2)
C# Welcome to Dynamic C#(11) - The Phantom of The Dynamic
C# Welcome to Dynamic C#(12) - dynamic은 외로운 아이.
C# Welcome to Dynamic C#(13) - 아직도 가야할 길.
C# Welcome to Dynamic C#(14) - 철지난 만우절에 낚여서 파닥파닥.
C# Welcome to Dynamic C#(15) - A/S for dynamic.
C# Welcome to Dynamic C#(16) - dynamic이라도 이건 안되는 거임.
C# Welcome to Dynamic C#(17) - 필요한 말만 하고 삽시다.
C# Welcome to Dynamic C#(18) - 이름을 붙이면서 벌어진 일들.
C# Welcome to Dynamic C#(19) - 위너 고르기.
C# Welcome to Dynamic C#(20) - 어르신과 대화하는 법.
C# Welcome to Dynamic C#(21) - 인덱스의 힘.
C# Welcome to Asynchronous C#(0) - C#의 전설.
C# Parallel Programming [C# 4.0] Parallel Extension - [1] 병렬 처리
C# Parallel Programming [C# 4.0] Parallel Extension - [2] 병렬 처리 아키텍처
C# Parallel Programming [C# 4.0] Parallel Extension - [3] TPL(Task Parallel Library)
C# Parallel Programming Welcome to Parellel world(1) - Here comes a new challenger!
C# Parallel Programming Welcome to Parallel C#(1) - 굿바이, 그리고 안녕~~?
C# Parallel Programming Welcome to Parallel C#(2) - 계속 되는 개념 찾기.
C# Parallel Programming Welcome to Parallel C#(3) - 작업의 기본.
C# Parallel Programming Welcome to Parallel C#(4) - 작업의 기본 Part 2.
C# Parallel Programming Welcome to Parallel C#(5) - 병렬작업에서 예외가 생기면 어케...?
C# Parallel Programming Welcome to Parallel C#(6) - To be continue...
C# Parallel Programming Welcome to Parallel C#(7) - Excuse me.
C# Parallel Programming Welcome to Parallel C#(8) - 취소 쉽게 하기.
C# Parallel Programming Welcome to Parallel C#(9) - 백지장은 맞들지 말엉.
C# Parallel Programming Welcome to Parallel C#(10) - 이보게, 잠깐 뒤를 돌아보지 않겠나.

   

C++/CLI

C++/CLI C++/CLI는 미운 오리새끼 or 백조
C++/CLI .NET에서의 C++/CLI의 의미
C++/CLI [Step 01] 'C++/CLI가 뭐야?'에 답하기 && 가장 많은 프로그래밍 언어로 만드는 프로그램 만들기
C++/CLI [Step 02-1] 클래스(class), 핸들(^), 그리고 구조체(struct)
C++/CLI [Step.02-2] 클래스(class), 핸들(^), 그리고 구조체(struct)
C++/CLI [step.03] 배열
C++/CLI [Step. 04] nullptr, interior_ptr, pin_ptr
C++/CLI [Step. 05] 관리 코드의 array를 비관리 코드에 포인터로 전달
C++/CLI [Step. 06-1] 관리코드의 문자열과 비관리코드의 문자열 변환
C++/CLI [Step. 06-2] 관리코드의 문자열과 비관리코드의 문자열 변환
C++/CLI [Step. 07] 비관리 클래스에서 관리 클래스를 멤버로, 관리 클래스에서 비관리 클래스를 멤버로
C++/CLI [Step. 08] 프로퍼티 ( property )
C++/CLI [Step. 09] 델리게이트 (delegate)
C++/CLI [Step. 10] 이벤트 ( event )
C++/CLI [Step. 11] 열거형( enum )
C++/CLI [Step. 12] for each
C++/CLI [Step. 13] parameter array
C++/CLI [Step. 15] static 생성자, initonly, literal
C++/CLI [Step. 14] 인터페이스 ( interface )
C++/CLI [Step. 16] array 클래스에 non-CLI 오브젝트 사용
C++/CLI [Step. 17] 델리게이트에 비관리 함수를 할당하기 그리고 다음 예고
C++/CLI [Step. 18] 순수 가상 함수
C++/CLI [Step. 19] char* -> 관리코드, 관리코드 -> char*
C++/CLI [Step. 20] 닷넷에서 HalfNetwork를 사용하자 - 1
C++/CLI [Step. 21] 닷넷에서 HalfNetwork를 사용하자 - 2
C++/CLI [Step. 22] 닷넷에서 HalfNetwork를 사용하자 ? 3
C++/CLI [Step. 23] 닷넷에서 HalfNetwork를 사용하자 ? 4
C++/CLI [Step. 24] 닷넷에서 HalfNetwork를 사용하자 ? 5
C++/CLI [Step. 25] 닷넷에서 HalfNetwork를 사용하자 ? 6(마지막)

   

C++0x

C++0x [VC++] 1. 큰 변화가 기대되는 Visual C++( VC++ )
C++0x [VC++] 2. C++0x의 auto
C++0x [VC++] 3. static_assert
C++0x [VC++] 4. 우측 값 참조( RValue Reference ) - 첫 번째
C++0x [VC++] 5. 우측 값 참조( RValue Reference ) ? 두 번째
C++0x [VC++] 6. 우측 값 참조( RValue Reference ) - 세 번째
C++0x [VC++] 7. 우측 값 참조( RValue Reference ) - 네 번째
C++0x [VC++] 8. 우측 값 참조( RValue Reference ) ? 다섯 번째
C++0x [VC++] 9. Lambda ( 람다 ) - 첫 번째
C++0x [VC++] 11. Lambda - 두 번째
C++0x [VC++] 12. Lambda - 세 번째
C++0x [VC++] 13. Lambda - 네 번째
C++0x [VC++] 14. decltype
C++0x 대용량 파일 조작을 위한 C++0x의 변화
C++0x nullptr
C++0x VC++ 10에 구현된 C++0x의 코어 언어 기능들
C++0x C++0x 관련 책 "Visual C++ 10과 C++0x"
C++0x "Visual C++ 10과 C++0x" pdf 파일
C++0x [Plus C++0x] 람다(Lambda) 이야기 (1)
C++0x [Plus C++0x] 람다(Lambda) 이야기 (2)
C++0x [Plus C++0x] 람다(Lambda) 이야기 (3)
C++0x [Plus C++0x] 람다(Lambda) 이야기 (마지막회)
C++0x [STL] 1. What's new in VC++ 2010?
C++0x [STL] 2. unique_ptr (1/2)
C++0x [STL] 3. unique_ptr (2/2)
C++0x [STL] 4. make_shared
C++0x [STL] 5. 에 추가된 새로운 함수들 (1/5)
C++0x [STL] 6. 에 추가된 새로운 함수들 all_of, any_of, none_of (2/5)
C++0x VS2010에서 nullptr의 알려진 버그
C++0x RValue Reference에 의한 STL의 성능향상 테스트
C++0x [STL] 7. 에 추가된 새로운 함수들 copy_if, copy_n, find_if_not (3/5)
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [0]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [1]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [2]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [3]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [4]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [5]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [6/7] 완결!
VC++ 10 Concurrency Runtime PPL task를 이용한 피보나치 수 계산
VC++ 10 Concurrency Runtime 인사 및 Multi Core, Multi Thread...그리고 VC++ 10
VC++ 10 Concurrency Runtime Concurrency Runtime
VC++ 10 Concurrency Runtime Parallel Patterns Library (PPL)
VC++ 10 Concurrency Runtime 양보할 줄 아는 Concurrency Runtime의 event
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - Task
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - 병렬 알고리즘
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - parallel_for 알고리즘
VC++ 10 Concurrency Runtime Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - parallel_for_each 알고리즘
VC++ 10 Concurrency Runtime Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 2
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - parallel_invoke
VC++ 10 Concurrency Runtime Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 마지막회
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - combinable
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - task group에서의 병렬 작업 취소 - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - task group에서의 병렬 작업 취소 - 2
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_vector - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_vector - 2
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_queue - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_queue - 2
VC++ 10 Concurrency Runtime Concurrency Runtime(ConcRT)의 디버그 모드에서 메모리 leak 문제
VC++ 10 Concurrency Runtime Asynchronous Agents Library 소개
VC++ 10 Concurrency Runtime Asynchronous Agents Library - agent. 1 ( 상태 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? agent. 2 ( 기능 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library - message 전달 함수. 1 ( 전송 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message 전달 함수. 2 ( 수신 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 1. ( 인터페이스 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 2. ( unbounded_buffer )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 3. ( overwrite_buffer & single_assignment )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 4. ( call )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 5. ( transformer )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 6. ( choice )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 7. ( join & multitype_join )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 8. ( timer )
VC++ 10 Concurrency Runtime Concurrency Runtime ? 동기화 객체 1. ( critical_section & reader_writer_lock )
VC++ 10 Concurrency Runtime Concurrency Runtime ? 동기화 객체 2. ( event )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 9. ( custom )
VC++ 10 Concurrency Runtime Concurrency Runtime - 만델브로트 프랙탈 ( Mandelbrot Fractal ) 예제
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 1. ( Scheduler )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 2. ( SchedulerPolicy )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 3. ( ScheduleGroup )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 4. ( ScheduleTask )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 5. ( Context blocking )
Visual C++ 10 About Visual C++ 10
Visual C++ 10 디버깅 모드에서 역어셈블리 코드 보기
Visual C++ 10 Visual C++ 10의 변화
Visual C++ 10 [Upgrade to VC++ 10] _WIN32_WINNT 버전 문제
Visual C++ 10 VS2010 C++ 프로젝트의 디렉토리 설정

   

클라우드 컴퓨팅

Cloud 구름 속의 미래 : Windows® Azure™ Platform [1]
Cloud SQL Azure - CTP1
Cloud SQL Azure 알아보기 (1) - 데이터베이스 개체 생성
Cloud SQL Azure 알아보기(2) ? 데이터베이스 스키마 마이그레이션, 데이터 전송
Cloud 구름 속의 미래 : Windows® Azure™ Platform [2]
Cloud SQL Azure 사용 시 주의점(1) - 방화벽 설정
Cloud SQL Azure 알아보기(3) ?SQL Server 2008 R2 Nov CTP
Cloud SQL Azure 알아보기(4) ? SQL Azure Cloud App
Cloud SQL Azure 알아보기 (5)- SQL Azure 이점과 T-SQL 지원
Cloud [MS@클라우드컨퍼런스] MS 클라우드 기술과 플랫폼
Cloud 클라우드 기반 분산 컴퓨팅을 위한 AppFabric (1) : 아하! App 분산!
Cloud Hello Windows Azure / Windows Azure Platform의 이해
Cloud Hello Windows Azure / Gallery of 'Powered by Windows Azure Platform'
Cloud Hello Windows Azure / Windows Azure 개발 환경의 구축
Cloud Hello Windows Azure / Understanding Windows Azure Development Process
Cloud Hello Windows Azure / Windows Azure Tools for Visual Studio 1.2 출시
Cloud Hello Windows Azure / Windows Azure Platform 최신 소식 업데이트 (종합) [수정]
Cloud Hello Windows Azure / Twitter 스타일 방명록 만들기 #1
Cloud Windows Azure Update: Microsoft Project Code-Named "Houston" CTP 1
Cloud SQL Azure와 Excel 2010의 PowerPivot
Cloud Hello Windows Azure / Twitter 스타일 방명록 만들기 #2
Cloud Windows Azure Update: CloudStorageAccount 클래스 사용 시 주의 사항
Cloud SQL Azure Update: Dynamic Management View
Cloud Hello Windows Azure / Twitter 스타일 방명록 만들기 #3
Cloud Windows Azure Update: myAzureStorage
Cloud SQL Azure 와 SQL Reporting Service
Cloud Windows Azure Update: Windows Azure CDN의 활용
Cloud [작업 중] Windows Azure Update: Adaptive Smooth Streaming with Windows Azure Storage

   

게임 개발

Direct3d Mobile [d3dm 기초] 1. wm6.x 개발환경 세팅
Direct3d Mobile .NET 기반에서 공개소스 게임엔진 포팅하기
DirectX 11 [JumpToDX11-1] 사라진 Direct3D 오브젝트를 찾아서...
DirectX 11 [JumpToDX11-2]DeviceContext...넌 누구냣!!
DirectX 11 [JumpToDX11-3] Feature Level
DirectX 11 [JumpToDX11-4] ID3D11View
DirectX 11 [DX11_#1]D3D Buffer( 1 / 2 )
DirectX 11 [DX11_#2]D3D Buffer( 2 / 2 )
DirectX 11 [DX11_#3]기본적인 설정
DirectX 11 [JumpToDX11-5] 새로운 시대를 여는 DirectX11...
DirectX 11 [JumpToDX11-6] 커맨드(Command)...
DirectX 11 [DX11_#4]텍스트, 버튼 출력
DirectX 11 [JumpToDX11-7] 간편해진 리소스 처리.
DirectX 11 [JumpToDX11-8] Deferred Contexts
DirectX 11 [JumpToDX11-9] Multi-threaded Rendering 을 위한 API.
DirectX 11 [JumpToDX11-10] GPGPU 를 위한 DirectCompute.
DirectX 11 [JumpToDX11-11] DirectCompute 를 위한 한걸음!
DirectX 11 [JumpToDX11-12] DirectCompute 의 절차.
DirectX 11 [JumpToDX11-13] Tessellation 등장.
DirectX 11 [DX11_#5]DirectX11의 활용 사례(1/3)
DirectX 11 [JumpToDX11-14] DirectX9 세대의 테셀레이션( ID3DXPatchMesh 편 )
DirectX 11 [JumpToDX11-15] DirectX9 세대의 테셀레이션( IDirect3DDevice9::DrawXXXPatch편 )
DirectX 11 [알콜코더의 미리 배워보는 DirectX 11 - 입문편] 0. 누구를 위한 연재인가
DirectX 11 [알콜코더의 미리 배워보는 DirectX11-입문편] 1.튜터리얼 01 : 다이렉트 3D 기초 #1
DirectX 11 [알콜코더의 미리 배워보는 DirectX11-입문편] 1.튜터리얼 01 : 다이렉트 3D 기초 #2
DirectX 11 [JumpToDX11-16] DirectX9 세대의 테셀레이션( D3DXTessellateNPatches편 )
DirectX 11 [알콜코더의 미리 배워보는 DX11 ? 입문편] DX11에서 무엇이 추가되었나?
DirectX 11 [JumpToDX11-17] DirectX9 세대의 테셀레이션( ATI 라이브러리편 )
DirectX 11 [발표자료] 예제로 느껴보는 다이렉트X 11의 매력
DirectX 11 [JumpToDX11-18] DirectX11의 테셀레이션 ( 테셀레이션을 위한 하드웨어의 등장편 )
DirectX 11 [알콜코더의 미리 배워보는DX11 입문편] DirectX 11의 특징들
DirectX 11 [알콜코더의 미리배워보는 DX11-입문편] 1. 튜터리얼01 : 디바이스와 스왑체인의 생성

   

F#

F# Welcome to F#(1) - 첫만남.
F# Welcome to F#(2) - 두번째 만남.
F# Welcome to F#(3) - 사소한 탐색전.
F# Welcome to F#(4) - 과거와 배경을 좀 더 알고싶어.
F# Welcome to F#(5) - 아주 조금씩 심화되는 탐색전.
F# Welcome to F#(6) - 비교본능.
F# Welcome to F#(7) - 클리프 행어.
F# Welcome to F#(8) - 은총알과 엄친아.
F# Welcome to F#(9) - 메이져 데뷰.
F# Welcome to F#(10) - 인도음식 카레.....?
F# Welcome to F#(11) - 차별을 권장하는 언어인거임?!?!
F# Welcome to F#(12) - 공동작업 좋치아니항가

   

MFC

MFC [MFC] 리스타트 매니저(Restart Manager) - (1/3) : 기능 소개
MFC [MFC] 리스타트 매니저(Restart Manager) - (2/3) : 사용하기
MFC [MFC] 리스타트 매니저(Restart Manager) - (3/3) : 활용하기
MFC [MFC] 태스크 대화상자(Task Dialog) - (1/3) : 기능 소개
MFC [MFC] 태스크 대화상자(Task Dialog) - (2/3) : 사용하기
MFC [MFC] 태스크 대화상자(Task Dialog) - (3/3) : 활용하기
MFC [MFC] 태스크 대화상자(Task Dialog) - 예제 코드 올립니다.
MFC [MFC/윈도우 7 멀티터치] #2 : 제스처(gesture)를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #3 : 제스처(gesture)를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #4 : WM_TOUCH 메세지를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #5 : WM_TOUCH 메세지를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #6 : 예제 코드 올립니다

   

RIA

RIA Expression Blend3 preview - 1.인터페이스
RIA Expression Blend3 preview - 2. Photoshop import
RIA Silverlight 3 & Blend 3 RC 공개!!!
RIA Silverlight 4 Beta 공개
RIA .Net Ria Service + IIS6 + Silverlight 4 Troubleshooting!!
RIA 실버라이트 비하인드 코드에서 바인딩하기.
RIA .Net Ria Service 와 Entities 그리고 Stored Procedure 하다가 생긴일..
RIA 실버라이트 프로그래머가 할 수 있는 최소한의 블랜드 디자이너를 위한 배려

   

SharePoint 2010

SharePoint 2010 Visual Studio 2010 에게 바란다 - SharePoint 14 Development
SharePoint 2010 SharePoint 2010 Overview
SharePoint 2010 SharePoint 2010 개발 환경 구성
SharePoint 2010 SharePoint 2010 개발 환경- Hello World 웹 파트 생성 및 배포하기
SharePoint 2010 SharePoint 2010 Web Part 생성
SharePoint 2010 SharePoint 2010 Visual Web Part
SharePoint 2010 SharePoint 2010 Feature
SharePoint 2010 SharePoint 2010 Event Receiver
SharePoint 2010 SharePoint 2010 데이터 기술
SharePoint 2010 SharePoint 2010 Server Object Model
SharePoint 2010 Visual Studio 2010 출시에 따른 SharePoint Developer Tools
SharePoint 2010 SharePoint 2010 LINQ to SharePoint
SharePoint 2010 Client Object Model - .NET
SharePoint 2010 Client Object Model ? Silverlight (1)
SharePoint 2010 Client Object Model ? Silverlight (2)
SharePoint 2010 Client Object Model - Javascript(1)
SharePoint 2010 Client Object Model - Javascript(2)
SharePoint 2010 Client Object Model ? 정리
SharePoint 2010 SharePoint 2010 개발환경 구축 가이드
SharePoint 2010 REST -.NET
SharePoint 2010 REST ? Silverlight
SharePoint 2010 REST - jQuery
SharePoint 2010 SharePoint 2010 프로젝트 디버깅
SharePoint 2010 SharePoint 2010 Developer Dashboard

   

Team Foundation Server

Team Foundation Server Visual Studio Team System 2010 (CTP10) - 작업 항목 링크
Team Foundation Server TFS 2010 설치 하기
Team Foundation Server TFS 2010 Build Service 설치
Team Foundation Server TFS 2010 설치 과정 중에 TF255040 문제
Team Foundation Server Visual Studio 2010을 활용한 ALM (1-5) - ALM 이란 무엇인가
Team Foundation Server Team Foundation 트러블 슈팅 가이드
Team Foundation Server Visual Studio Team Foundation Server 2010 를 설치해보자
Team Foundation Server Visual Studio Team Foundation Server 2010 설치 전 할일
Team Foundation Server VS TFS 2010 설치편 - 설치전 IIS, .NET 설치
Team Foundation Server VS TFS 2010 설치편 - 설치 시작
Team Foundation Server VS TFS 2010 구성편 - 설치 후 TFS 구성으로 점심 얻어먹기 편
Team Foundation Server VS TFS 2010 사용편 - SourceSafe? 버려~
Team Foundation Server [HowTo] Team Foundation Server 의 로컬 매핑 캐시 제거하기
Team Foundation Server [HowTo] SharePoint 2010 Beta 깨끗하게 제거하기
Team Foundation Server [HowTo] SCVMM 의 Install Virtual Guest Service 작업 중 2941 오류
Team Foundation Server [HowTo] TFS2010 의 Tfs_Analysis 웨어하우스 데이터베이스가 망가졌을 경우
Team Foundation Server [PPT] 테스트와 가상화의 만남 - 테스트 가상화(Lab Management)
Team Foundation Server Team Foundation Server 2010으로 업그레이드, 마이그레이션, 동기화
Team Foundation Server Visual Source Safe 사용자를 위한 TFS2010 시리즈

   

Visual Studio 2010

Visual Studio 2010 Visual Studio Team System 2010 CTP 만료 해결하기
Visual Studio 2010 Visual Studio 2010 의 특징
Visual Studio 2010 Visual Studio 2010 내부 빌드 최신 동영상: C# 4.0 Language + IDE + WPF Shell + Editor
Visual Studio 2010 Visual Studio 2010 & .NET 4.0 참고 자료들
Visual Studio 2010 Visual Studio 2010 Beta 1 설치부터 살펴보기
Visual Studio 2010 멀티 모니터 사용
Visual Studio 2010 Visual Studio 2010 Beta 2 출시
Visual Studio 2010 Visual Studio 2010 Beta 2 설치 미리 보기
Visual Studio 2010 VS 2010 Beta 2 설치 과정에서 Silverlight SDK 문제
Visual Studio 2010 VS2010 베타2의 WPF & Silverlight 디자이너 성능 향상 팁
Visual Studio 2010 VS 2010 기능 소개 01 인텔리 센스 기능의 변화
Visual Studio 2010 Visual Studio 2010과 Blend Preview for .NET 4 통합 문제
Visual Studio 2010 VS 2010 기능 소개 02 - IDE의 기능 추가
Visual Studio 2010 VS 2010 기능 소개 03 - IDE의 변화
Visual Studio 2010 Visual Studio 2010 출시 일정
Visual Studio 2010 VS 2010 기능소개 04 - Visual C#&VB 개발자 IDE Tips & Tricks 첫번째
Visual Studio 2010 VS 2010 기능소개 05 - Visual C#&VB 개발자 IDE Tips & Tricks 두번째
Visual Studio 2010 Visual Studio 2010 RC 공개 임박!
Visual Studio 2010 Visual Studio 2010 RC 공개
Visual Studio 2010 C#에서 IntelliSense가 동작하지 않을 때 문제 해결 방법
Visual Studio 2010 똑똑한 검색을 지원하는 VSTS 2010의 "Navigate To" 검색
Visual Studio 2010 실버라이트4 RC와 블렌드 4 베타 공개
Visual Studio 2010 윈도우폰 7 개발환경 공개
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (2회) - VS IDE
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (3회) - Box Selection
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (4회) - Call Hierarchy
Visual Studio 2010 Visual Studio 2010 출시와 완소 정보 총 정리
Visual Studio 2010 Visual Studio 2010 e-book 무료로 다운로드 하세요
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (5회) - Navigate To
Visual Studio 2010 Visual Studio 2010 RTM 추가 완소 정보
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (6회) - Generate from Usage
Visual Studio 2010 VS 2010 기능소개 05 - Visual C#&VB 개발자 IDE Tips & Tricks 두번째
Visual Studio 2010 Visual Studio 2010, 2008, 2005 에서 .NET Framework 1.1 개발하기
Visual Studio 2010 Visual Studio 2010, 2008, 2005 에서 .NET Framework 1.1 개발하기
Visual Studio 2010 Just for fun! / Visual Studio Express Edition
Visual Studio 2010 왜 Visual Studio 2010 이여야 하는가?
Visual Studio 2010 Visual Studio 2010 최신 PDF 자료를 MSDN 에서 다운로드 받으세요
Visual Studio 2010 Just for fun! / DreamSpark는 대학생 여러분을 위한 솔루션입니다.
Visual Studio 2010 VS2008 을 VS2010 에서 동시에 개발하기
Visual Studio 2010 VS2008 과 VS2010 동시에 개발하기 : 테스트 프로젝트가 포함 될 경우
Visual Studio 2010 Introducing Visual Studio LightSwitch! - Enjoy your development
Visual Studio 2010 Visual Studio Hotfix List
Visual Studio 2010 곧 다가올 기술, Microsoft Research [1/2]
Visual Studio 2010 곧 다가올 기술, Microsoft Research [2/2]
Visual Studio 2010 Visual Studio 31 (1) - 시작, 그리고 Intellisense
Visual Studio 2010 Visual Studio 31 (2) - Startpage
Visual Studio 2010 Visual Studio 31 (3) - Temp Project
Visual Studio 2010 Visual Studio 31 (4.1) - Visual Studio 2010 Productivity Power Tools, Part 1
VIsual Studio Extensibility [Blueprints] S+S Blueprints
VIsual Studio Extensibility Visual Studio 2010 SDK 와 Readme
VIsual Studio Extensibility Visual Studio 2010 Extension Manager
VIsual Studio Extensibility [VSIX] 1. What is different from before version?
VIsual Studio Extensibility [VSIX] 2-1. How to start VSIX programming
VIsual Studio Extensibility [VSIX] 2-2. How to start VSIX programming
VIsual Studio Extensibility MousePresentationTracker - MEF 세미나 예제
VIsual Studio Extensibility [VSX] 1. Visual Studio Extensibility,, 그 시작
VIsual Studio Extensibility Visual Studio 2010 확장 모델인 VSIX 버그
VIsual Studio Extensibility VSGesture v2.0 for VS2010 is now available for download

   

우리 블로그 소식

VSTS 2010 팀 블로그 Visual Studio Team System 2010 공식 팀 블로그 맴버소개
VSTS 2010 팀 블로그 Visual Studio Team System 2010 팀 블로그 소개
VSTS 2010 팀 블로그 VSTS 2010 팀 블로그/스터디 맴버를 모집합니다.
VSTS 2010 팀 블로그 VSTS 2010 팀 맴버 지원을 마감합니다
VSTS 2010 팀 블로그 Visual Studio Team System 2010 Beta 1 공개
VSTS 2010 팀 블로그 [MSDN 주간 세미나] 발표자료 / .NET Framework와 Visual Studio : 현재와 미래 1, 2
VSTS 2010 팀 블로그 VSTS 2010 팀 3분기 맴버 모집
VSTS 2010 팀 블로그 VSTS 2010 팀 세미나 동영상 - 6월 10일
VSTS 2010 팀 블로그 VSTS 2010 팀 맴버 추가 모집
VSTS 2010 팀 블로그 VSTS 2010 팀 트위터를 오픈하였습니다.
VSTS 2010 팀 블로그 TECH DAY 2009 행사 오픈!!!
VSTS 2010 팀 블로그 VSTS 2010 공식 블로그 Viva 2010팀 멤버 추가 모집 공고
VSTS 2010 팀 블로그 [세미나] 차세대 응용 프로그램 구축 방법 및 사례 소개 세미나
VSTS 2010 팀 블로그 Visual Studio 2010 팀에서 팀원 모집합니다.
VSTS 2010 팀 블로그 한국 Visual Studio 2010 사용자를 위한 트위터 커뮤니케이션
VSTS 2010 팀 블로그 C++ 개발자와 함께하는 Visual Studio 2010
VSTS 2010 팀 블로그 [무료 세미나] ReMIX 10
VSTS 2010 팀 블로그 6월 1일, 대한민국 웹 컨퍼런스의 지존 ReMIX 10가 개최됩니다!
VSTS 2010 팀 블로그 REMIX10 의 VS2010 팀 후기
VSTS 2010 팀 블로그 6월 1일, REMIX10 세미나 세션 공개
VSTS 2010 팀 블로그 [세미나] Visual Studio Camp #1
VSTS 2010 팀 블로그 [세미나 후기] Visual Studio Camp #1
VSTS 2010 팀 블로그 [세미나 발표 자료] Visual Studio Camp #1
VSTS 2010 팀 블로그 [세미나] Visual Studio Seminar #1 / 2010년 9월 28일
VSTS 2010 팀 블로그 9월 13일에 개최하는 KGC에서 강연을 합니다.
VSTS 2010 팀 블로그 KGC10에서의 VS2010 스터디 팀의 활약 모습
VSTS 2010 팀 블로그 [VSKOREA] Visual Studio 2010 정보가 한 눈에…
VSTS 2010 팀 블로그 [세미나 후기] Visual Studio Seminar #1
VSTS 2010 팀 블로그 [세미나 발표 자료] Visual Studio Seminar #1
VSTS 2010 팀 블로그 [후기] C++ & 게임 개발자를 위한 개발 생산성 및 퍼포먼스 향상 전략 세미나

   

WCF

WCF WCF란 무엇인가?
WCF 기본 WCF 프로그래밍 - 첫 WCF 서비스 만들기
WCF 기본 WCF 프로그래밍 - 첫 WCF 서비스 만들기 2
WCF WCF의 기본 - Service Contract
WCF WCF의 기본 - Data Contract
WCF WCF 서비스의 동시성(Concurrency) - 1
WCF WCF 서비스의 동시성(Concurrency) - 2
WCF WCF - Serialization
WCF WCF Hosting - WAS를 이용한 Hosting
WCF 도메인을 여러개 등록했을때 WCF 서비스를 호스팅 할수 없어요 ㅠㅠ
WCF WCF Hosting(2) - ASP.NET 호환성(Compatibility)
WCF WCF Hosting (3) - Windows Service를 이용한 Hosting
WCF WCF Security (1) - SSL을 이용한 전송계층에서의 보안 설정
WCF WCF Security (2) - 전송 계층에서의 메세지 인증 (사용자 지정 인증)
WCF WCF Troubleshooting (1)
WCF WCF Service Configuration Editor
WCF WCF Troubleshooting (2)


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

지난 회 차에 여러 가지 문제로 .NET 스마트클라이언트가 가진 문제점을 살펴 보았습니다. 그 중, 주된 이슈는 이미 로드된 어셈블리는 업데이트/갱신이 불가능하다는 것과, 메모리의 사용률이 지속적으로 증가한다는 문제입니다. 이러한 문제는 사내 정책적인 서버를 도입하여 해결 가능하지만, 대부분의 조직과 기업은 이러한 정책 서버를 도입하지 않는 것으로 알고 있습니다.    

이미 얘기 했다시피, 평소에도 .NET 에서 이러한 문제를 가지고 고민을 했었지만, 최근 이러한 문제가 이슈가 되었을 때 더 이상 필자 또한 방관할 수 없었습니다. 왜냐하면 "안된다!" 라는 것 자체가 .NET 의 많은 매리트를 배제한다는 의미가 될 수 있기 때문입니다. 이러한 문제로 '목숨거는' 고객이라면 차라리 '지금은 곤란하다. 조금만 기다려달라' 라는 답이 훨씬 나아 보입니다. 물론 이런 문제가 가능하다는 전제 조건으로 말입니다.

   

문제 해결 방안

일단, 몇 날 몇 일을 고민하며 생각한 끝에 아래와 같은 아키텍처링을 하게 되었습니다. 물론 최선의 방법도, 최적의 방법도 아니지만, 문제가 된다면 저에게 피드백을 주시기 바랍니다. 저 또한 짧은 지식으로 이러한 고민을 하게 되었으니 저도 많이 답답하네요^^;    

   

위의 아키텍처링은 논리적인 아키텍처입니다. 이 방법을 통해 이전 아티클을 통해 골치 아픈 .NET 어플리케이션의 메모리 릭(Memory Leak) 을 해결할 수 있을 것으로 기대합니다.

   

어플리케이션과 AppDomain 의 분리

.NET 어플리케이션은 기본적으로 하나의 프로세스(Process) 를 차지하게 됩니다. 그것이 독립 프로세스든, IEHost.DLL 또는 IEExec.EXE 든 간에 말이죠. 이 독립 프로세스는 독립적인 하나의 어셈블리에서 관장하게 됩니다. 기본적인 이 부분의 컨셉은 어플리케이션의 재시작을 방지하기 위한 방법이기도 합니다.    

기존의 어플리케이션의 프로세스와 AppDomain 을 분리함으로써 최소한으로 AppDomain 이 안전하게 언로드될 수 있는 환경을 제공하는 것입니다. 그리고 위해 AppDomain Manager 는 이것을 관장하는 최상위 Manager Layer 가 됩니다.

   

MVVM 으로 구현부와의 분리

MVVM(Model View ViewModel) 패턴의 가장 큰 특징은 View 와 ViewModel 을 분리한 것입니다. 이것을 분리함으로써 View 와 ViewModel 의 종속 관계를 완전히 해결하고, ViewModel 은 격리된 AppDomain 으로 제한함으로써 언제든지 AppDomain 이 언로드될 수 있게 합니다.    

이 부분을 구현하기 가장 이상적인 환경은 바로 WPF(Windows Presentation Foundation) 이 되겠네요.

   

Views 의 교체

MVVM 으로 구현부와의 분리를 통해 당연히 Views 는 언제든지 교체가 가능합니다. 서버/로컬에서 Views 가 교체된다면 ViewModels 을 언로드하고 새로운 Isolated AppDomain 을 생성하여 View 와 ViewModel 간에 연결하는 방법입니다.    

특히 이 통신 구간은 View 와 ViewModel 간의 Interface Contract 를 통해 크리티컬한 자원의 관리를 최소화하는 것에 있습니다. 이로써 이미 로드된 사용자 화면과 어셈블리라도 서버/로컬의 갱신이 있다면 언제든지 갈아치울 수 있는 구간입니다. 이 부분이 앞서 얘기한 .NET 스마트클라이언트의 문제를 해결할 수 있는 핵심 구간입니다.

   

업데이트 기능을 재작성

이 아키텍처링의 가장 큰 문제지만, .NET 스마트클라이언트의 NTD(No Touch Deployment) 기능을 그대로 사용할 수 없습니다. .NET 의 NTD 는 이미 실행되는 AppDomain 에 어셈블리를 로드하기 때문에 .NET 의 NTD 를 그대로 사용한다면 이 아키텍처링을 적용할 수 없습니다.

NTD 기능 뿐만 아니라, ClickOnce 의 자동 버전 감지 기능도 사용할 수 없습니다. ClickOnce 는 주기적으로 서버의 Application Manifest 를 확인하는 과정으로 새로운 버전을 감지하고 업데이트하는데, 이 기능을 그대로 사용한다면 위의 아키텍처링은 사실상 무의미하고, 결국 메모리 사용 증가는 해결할 수 없기도 합니다.

   

제한 사항

하지만 필자가 제안한 .NET 스마트클라이언트의 문제를 해결하기 위한 방법은 제한적인 방법으로 수행이 가능합니다. 물론 모든 경우라도 제안이 가능한 방법이라면 좋겠지만, .NET 의 기본 아키텍처가 해결하지 못한 이상, 필자 또한 제한적인 방법으로 .NET 스마트클라이언트의 문제점을 해결할 수 있습니다.

그 제한적인 방법의 한계는 아래와 같습니다.

  • 개발 표준을 완벽하게 MVVM 기반으로 개발되어야 한다.
  • MVVM 패턴으로 완벽하게 분리가 되어야 한다.
    • WPF 를 사용할 경우 MVVM 패턴으로만 작성되어야 한다.
    • 윈도우 폼(Windows Forms) 또는 ActiveX 컨트롤 일 경우, MVVM 로 작성할 수 없다.
    • 이 경우, View와 ViewModels 를 분리하도록 별도 프레임워크 개발이 필요하다.
  • Marshaling 을 통한 통신
    • Marshaling 은 AppDomain 간의 원격 통신을 해야 한다.
    • 원격 통신으로 인한 성능 저하
  • WPF 개발
    • Binding Expression 을 확장한 Binding Marshaling Expression(단지, 예임) 으로 바인딩을 해야 한다.
    • 원격 바인딩으로 성능 저하 예상

   

결론

필자는 .NET 어플리케이션이 업데이트될 경우 왜 반드시 최적의 방법이 어플리케이션 쉘을 재시작하느냐에 시작한 고민으로부터 시작됩니다. .NET 아키텍처를 이해못하는 것은 아니지만, 고객은 언제나 더 향상된 방법을 제안합니다. 그리고 필자는 그런 고민을 극복하고자 제안한 방법입니다.

물론 위의 아키텍처링을 효율적인 면과 성능적인 면을 더 자세히 테스트해 보아야 하겠지만, 분명한 것은 끊임없이 고객의 요청은 진화하지 퇴화하지는 않을 거라고 생각합니다.

예전에 필자는 위와 같은 문제를 문의할 때, ".NET 에서는 안된다" 라고 답했습니다. 맞아요. 안됩니다. 하지만 문득, '안되면 되게 해야지!' 라는 생각이 들더군요. 짧은 소견이지만 잘못된 부분이 있으면 언제든지 피드백 주시기 바랍니다.

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

개요

.NET 에서 윈도우 어플리케이션을 개발해 본 독자라면 한번 쯤은 .NET 스마트클라이언트라는 용어를 많이 들어보았을 것입니다. 스마트클라이언트는 배포(Deployment), 플랫폼 독립 모델을 제공함으로써 다양한 클라이언트를 지원하는 것이 특징입니다.

예전에 필자가 UX 라는 주제로 쓴 포스트 중 "당신이 생각하는 UX 란?" 에서도 언급하였듯이, .NET 스마트클라이언트는 X-Internet 이라는 트랜드로 기술적인 부분을 초점으로 마케팅한 용어로 발전하였습니다. 이와 반대로 RIA(Rich Internet Application) 는 UX(User eXperience) 초점에서 마케팅한 용어라고 보셔도 좋습니다.

   

사전 지식

하지만 .NET 스마트클라이언트는 사실상 매번 나오는 이슈가 있습니다. 아니, 이것은 .NET 스마트클라이언트의 문제라기 보다는 .NET 자체의 아키텍처와 관련된 문제이기도 합니다.

결혼부터 말하자면, .NET 어플리케이션은 로드된 어셈블리(Loaded Assemblies) 는 언로드(Unload) 가 되지 않습니다. 간단하게 아래와 같이 .NET 어플리케이션의 모델을 보면 알 수 있습니다. .NET 어플리케이션은 하나의 AppDomain(Application Domain) 을 갖는 것을 알 수 있습니다.

   

AppDomain 은 어플리케이션 간의 CAS(Code Access Security) 라는 임계 영역에 존재하게 됩니다. 말 그대로 CAS(Code Access Security) 이 CAS는 어플리케이션간의 엑세스를 제한함으로써 신뢰할 수 없는 코드나 어플리케이션은 사용자의 컴퓨터에서 실행할 수 없도록 한 보안 모델입니다.    

즉, 이메일이나 인터넷, 사용자 그룹 및 권한 등 신원이 확인되지 않은 어플리케이션을 실행했을 때, 악의적인 목적으로 사용자의 로컬 자원을 엑세스할 수 없도록 제한하는 모델이라고 보시면 됩니다.    

이 코드 보안 모델은 .NET 의 어떤 어플리케이션이든 모두 이 보안 정책 안에 있다고 보시면 됩니다. ASP.NET 도 마찬가지로 아래와 같이 AppDomain 의 임계 영역 안에서 어플리케이션이 동작하게 됩니다. AppDomain 이 하나의 웹 어플리케이션을 동작하게 하고, HttpRuntime 에 의해 HttpContext 가 관리됩니다. 그리고 각각의 요청에 의해 HttpContext 는 별도의 스레드(Thread) 로 사용자의 요청을 응답하게 되는 구조라고 보시면 됩니다.

 예를 들어, 아래와 같은 코드 보안을 위한 선언적인 방법을 이용하여 악의적으로 사용될 수 있는 코드 쓰기, 수정 등을 할 수 없도록 합니다. 어셈블리, 클래스, 구조체, 생성자에서 사용할 수 있습니다. 물론 사용자가 이 보안 수준을 변경할 수 도 있지요.

문제 1

여태까지 이것을 말하기 위해 설명을 한 것입니다. 바로 .NET 어플리케이션은 어셈블리를 로드할 수 는 있지만, 언로드할 수 는 없습니다.

그러니까 더 자세하게 얘기하면, 아무리 가비지 컬렉션(Garbage Collection) 을 호출하고 CLR Runtime(Common Language Runtime) 이 이것을 대신 수행해 준다고 해도, 로드된 어셈블리 자체는 이 대상에서 예외라는 것입니다. 결론은 .NET 어플리케이션을 오래 쓰면 쓸 수록 메모리 사용이 증가할 가능성이 있습니다.

플러그인 모델(Plugin Model) 기반의 어플리케이션도 확장 기능이 많아지면 많아질 수록 메모리 점유율이 높아지고, 특히 엔터프라이즈 기업용 어플리케이션은 반드시 피해갈 수 없는 문제이기도 합니다.    

개인적으로 플러그인 모델과 엔터프라이즈 어플리케이션의 중간 영역이라고 생각되는 Visual Studio 를 한 1주일 정도 닫지 않고 써보셨나요? 쓰지 못할 정도는 아니지만, 괜히 버벅되고 느려지는 현상이 나타나게 된답니다.^^; 이런 현상은 Visual Studio 뿐만이 아니라 .NET 으로 작성된 모든 어플리케이션은 모두 영향을 받게 됩니다.

   

그 이유는, .NET 은 로드된 AppDomain 의 어셈블리를 언로드할 수 있는 방법을 제공해 주지 않습니다. AppDomain 이 참조하는 관계는 기본적으로 로컬 자원의 어셈블리를 참조하겠지만, 코드 베이스(Code Base-코드의 출처) 가 인트라넷이나 인터넷이라면 그 코드 베이스로부터 어셈블리를 다운로드 하게 됩니다.    

문제 2

결론부터 말하면, .NET 어플리케이션은 참조 또는 다운로드한 어셈블리는 다운로드 캐시(Download Cache) 에 보관하게 됩니다. 어셈블리를 참조 또는 다운로드하는 판정 조건은 어셈블리의 버전, 토큰 등 복잡한 과정을 거치기 때문에, 제대로 된 정책을 갖고 있지 않는다면, 이미 다운로드된 어셈블리는 다운로드 캐시로부터 어셈블리를 재사용합니다.    

그렇기 때문에, 다운로드된 어셈블리는 File Lock(파일 잠김)이 발생하므로, 동일한 파일 이름의 어셈블리를 다운로드 받는 것은 불가능 합니다. 하지만 해결책이 없는 것은 아닙니다. Assembly.Load 시리즈의 메서드에는 byte[] 로 읽을 수 있는 오버로드된 메서드가 존재하기 때문입니다.    

즉, 아래와 같이 File Lock 을 방지할 수 있습니다. 하지만 어셈블리는 로드할 수 있으나, 기존의 로드된 어셈블리를 갈아치우지는 못합니다.

 

결국, 하나의 어플리케이션을 오래 사용하면 할수록 메모리의 점유율을 증가할 수 있게 될 가능성이 큽니다. 특히 엔터프라이즈 기업용 어플리케이션은 단위 업무별로 적절한 파일 크기, 업무간의 연간 관계 등을 고려하여 어셈블리를 모듈화하는데, 사실상 메모리 사용률 증가의 문제는 여전히 해결할 수 없는 문제입니다. 그 이유는, 앞서 말했듯이 어셈블리를 언로드할 수 있는 방법은 AppDomain 을 언로드하는 것이고, AppDomain 을 언로드하면 메인 어플리케이션을 재시작해야 된다는 문제입니다.

   

문제 3

이 섹션은 문제 2와 연관된 정책적인 문제입니다. 다운로드된 어셈블리는 다시 다운로드 받을 수 없기 때문에 선행적으로 몇 가지 정책적인 강제가 필요할 수 밖에 없습니다.

  • 어플리케이션 쉘(Shell)
    • 어플리케이션 쉘이 업데이트되면 어플리케이션을 재시작 해야 한다.
  • 어플리케이션 실행 중 단위 어셈블리
    • 단위 어셈블리가 한 번 다운로드되면 서버/로컬의 어셈블리가 갱신되도 다운로드 받지 못한다.
    • 단위 어셈블리가 다운로드 되고 서버/로컬 어셈블리가 갱신되어도 알림 받을 수 없다.
    • 이럴 경우, 어플리케이션 쉘을 서버에서 갱신하여 업데이트 알림을 받을 수 있고, 어플리케이션을 재시작 해야한다.

즉, 어떠한 경우라도 갱신된 어플리케이션을 적용하기 위해서는 메인 어플리케이션 쉘을 재시작해야 한다는 결론을 얻을 수 있습니다.

   

문제 4

더욱 문제인 것은 .NET Framework 4.0 기반의 일부 스마트클라이언트는 이 문제와 상관없이 불가능합니다. 그 이유는 이미 닷넷엑스퍼트의 안재우 수석님의 블로그 중 "[.NET 4.0] IE Embedded WinForm(Smart Client) 지원 중단" 를 참고하세요.

이유의 요지는, IEHost.DLL 과 IEExec.EXE 파일이 .NET Framework 2.0 으로 강력한 이름의 서명이 되었다는 것입니다. 이것은 즉, IEHost.DLL 과 IEExec.EXE 를 통하는 .NET 스마트클라이언트의 경우 GAC(Global Assembly Cache) 를 통해 활성화가 되는데, .NET Framework 4.0 의 스마트클라이언트 어플리케이션은 어셈블리 리디렉트(Assembly Redirect)를 통하지 않고서는 이것을 활성화할 수 있는 방법이 없습니다. 어셈블리 리디렉트를 통한다고 하더라도 Dependency Assemblies 는 .NET Framework 2.0 을 바라보기 때문에 .NET Framework 4.0 의 기능을 사용한다면 절대 불가능하기도 합니다.

하지만 .NET 어셈블리의 바이트 코드 조작을 통해서 가능하긴 합니다.

  • IEHost.DLL, IEExec.exe 의 바이트 코드를 수정하여 강력한 서명을 지운다
  • IEHost.DLL, IEExec.exe 의 바이트 코드를 수정하여 .NET 4.0 으로 저장한다
  • GAC(Global Assembly Cache) 에서 IEHost.DLL 과 IEExec.EXE 를 제거한다.

어셈블리의 바이트 코드 조작은 Mono 프레임워크를 통해서 아주 쉽게 할 수 있습니다. 하지만 IEHost.DLL 과 IEExec.EXE 를 사용하는 모든 사용자 클라이언트를 해킹하는 무자비한 방법입니다. 하지만 된다는 것만으로도 만족한다면 이 방법이 최선의 방법이 될 것 같네요.

   

.NET 스마트클라이언트의 고찰

.NET 스마트클라이언트는 .NET 엔터프라이즈 어플리케이션에 많은 기여를 하였습니다. 그리고 .NET 스마트클라이언트를 사용하는 기업 또는 인트라넷 환경은 매우 많기도 합니다.    

필자 또한 얼마 전에 이러한 고민으로 Microsoft 의 의뢰를 받은 적이 있습니다. 그리고 개인적으로 아주 많이 고민했습니다.    

왜냐하면 자바의 클래스 로드(Class Loader) 는 .NET 의 스마트클라이언트와 유사한 점이 굉장히 많습니다. 하지만 다른 점이 하나 있다면, 자바의 클래스 로더는 GC(Garbage Collection) 의 대상이 된다는 것이죠. 다시 말하면, 어플리케이션의 재시작 없이 마음만 먹으면 메모리 사용률이 증가하지 않도록 아키텍처링이 가능하다는 것입니다.    

필자가 결론적으로, .NET 의 AppDomain 과 자바의 클래스 로더는 각기 특색은 있지만, 어느 것이 정답인지는 모르겠습니다. 다만, 고객이 어플리케이션의 재시작 없이 어플리케이션 업데이트/갱신이 가능해야 한다는 전제 조건이라면 자바의 클래스 로더가 장점이긴 합니다.    

하지만, 필자는 이 문제로 몇 일 동안 고민했습니다. 왜냐하면 세상에는 불가능한 것이 없다라는 것이 필자의 신념이기도 하며, 어떤 문제든 최선의 방법이라는 것이 존재한다고 믿습니다. 그리고 결국 "빙고" 를 찾았습니다. ^^

다음 회 차에서는 .NET 스마트클라이언트의 이러한 문제를 개선할 수 있는 방법을 알아보도록 하겠습니다.

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

Visual Studio 2010, 2008 버전에서는 .NET Framework 2.0, 3.5, 3.5 SP1 을 선택할 수 있는 Multi Targeting(멀티 타게팅) 기능을 제공합니다. 최신의 .NET Framework 버전을 선택하여 개발할 수 있으며, 하위 호환성 있는 개발을 위해 최신의 Visual Studio 에서 하위 .NET Framework 버전을 선택할 수 있습니다.

하지만 Visual Studio 2010, 2008 의 Multi Targeting 은 .NET Framework 1.x 를 지원하지 않습니다. 프레임워크 버전과 개발 도구간의 비호환성 문제 때문에 .NET Framework 1.x 버전은 Visual Studio 2003 으로만 개발이 가능합니다.

이번에 소개하는 방법을 통하여 Visual Studio 2010, 2008, 2005 도구를 이용하여 .NET Framework 1.x 를 개발할 수 있는 환경을 구성할 수 있습니다.

단, 기존의 Visual Studio 2003 은 MSBuild(Microsoft 통합 빌드 솔루션) 를 지원하지 않기 때문에, .NET Framework 4.0 SDK 에 포함된 MSBuild Targets 를 사용하였습니다.

(Visual Studio 2008, 2005 버전에서도 아래와 같은 방식으로 MSBuild Targets 을 수정하시면 .NET Framework 1.1 개발 및 빌드 환경을 구축할 수 있습니다)

   

Visual Studio 2003 으로 개발된 프로그램

아래와 같이 간단한 ConsoleApplication1 프로젝트를 만들었습니다.

이 응용 프로그램을 실행하면 다음과 같은 결과가 나옵니다.

Mscorlib.dll 의 버전이 1.0.5000.0 인 것을 확인할 수 있습니다.

   

Visual Studio 2010 으로 프로젝트 컨버팅 하기

만들어진 Visual Studio 2003 프로젝트를 Visual Studio 2010 버전으로 컨버전합니다.

컨버전이 완료되었으면 프로젝트 파일(.csproj) 을 열어 아래와 같이 수정합니다.

먼저 기존의 .NET Framework 어셈블리를 .NET Framework 1.1 의 어셈블리로 강제로 변경해 줍니다. Visual Studio IDE 에서 .NET Framework 1.1 을 추가하면 2.0 이상의 어셈블리가 추가되므로 반드시 .csproj 파일에서 변경해 주어야 합니다.

그리고 Import 노드에 Microsoft.Csharp.v1.1.targets 의 라인을 추가해 줍니다.

   

Microsoft.CSharp.v1.1.targets 파일 만들기

$(MSBuildToolsPath) 의 폴더인 C:\Windows\Microsoft.NET\Framework\v4.0.30128 경로에서 Microsoft.CSharp.targets 파일의 복사본 이름을 Microsoft.CSharp.v1.1.targets 파일로 만들어 줍니다.

위의 방법으로 Microsoft.Common.target 파일을 Microsoft.Common.v1.1.targets 파일 이름으로 복사본을 만듭니다.

 

Microsoft.CSharp.v1.1.targets 파일 수정하기

아래의 노드를 찾아서 <NoWarn> 노드의 1701; 1702 값을 제거합니다. 이 값은 .NET Framework 1.1 의 경고 값으로 사용할 수 없는 값입니다.

그리고 아래의 CSC 노드를 찾아서 ErrorReport 속성을 제거합니다. .NET Framework 1.1 SDK 의 CSC.exe 는 ErrorReport 기능이 존재하지 않기 때문입니다.

추가로 CSC 노드의 ToolPath 의 경로를 아래와 같이 수정합니다. 이 경로에 포함되는 csc.exe 를 사용하여 .NET Framework 1.1 로 빌드하는 중요한 구문입니다.

아래의 Import 노드를 찾아서 복사본으로 만들었던 Microsoft.Common.v1.1.targets 파일명으로 변경합니다.

   

Microsoft.Common.v1.1.targets 파일 수정하기

아래의 노드를 찾아 붉은 영역의 노드를 추가합니다. 만약 TargetFrameworkVersion 노드를 .csproj 파일에 명시적으로 수정하게 되면, Visual Studio 2010 은 v2.0 이상 버전으로 변경을 해야 프로젝트 파일을 로드할 수 있기 때문에 Microsoft.Common.v1.1.targets 파일에서 변경해야 합니다.

아래의 ReportingServiceTargets 노드를 찾아 주석으로 처리하거나 삭제합니다.

아래의 _DebugSymbolsIntermediatePath 노드의 ItemGroup 을 주석으로 처리합니다. $(IntermediateOutputPath) 이미 기존의 Microsoft.CSharp.targets 에서 값이 선언되었으므로 같은 값이 추가되거나 할 경우 Collection Type 으로 간주하기 때문에 이 구문은 필요가 없습니다.

마찬가지로 아래의 구문도 $(IntermediateOutputPath) 는 Collection Type 으로 처리가 되는 것을 방지하기 위해 아래의 구문도 주석으로 처리하거나 제거합니다.

아래의 구문도 위와 같은 이유로 제거하거나 주석으로 처리합니다.

아래의 Code Analysis 기능은 Visual Studio 2003 에서 통합되어 제공되지 않기 때문에 제거하거나 삭제합니다.

   

Visual Studio 2010 에서 .NET Framework 1.1 빌드 하기

모든 구성이 완료 되었으면, Visual Studio 2010 에서 ConsoleApplication 을 실행해 봅니다. 빌드가 .NET Framework 1.1 로 빌드된 것을 확인할 수 있습니다.

   

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

.NET Framework 4.0 마이그레이션 이슈

.NET Framework 2010.04.16 12:00 Posted by POWERUMC

아마 .NET Framework 4.0 을 출시로 향상된 프레임워크의 API 를 사용하기 위해 .NET Framework 4.0 으로 개발하거나 마이그레이션의 계획을 할 예정이라면 반드시 아래의 문서를 보시기 바랍니다.

 

.NET Framework 4.0 으로 마이그레이션 이슈

.NET Framework 4.0 은 구조적으로 전혀 새로워지고 향상된 프레임워크입니다. 그로 인하여 .NET Framework 4.0 은 기존의 구조 또는 API 들이 호환되지 않는 경우가 있습니다. 어플리케이션 레벨과 코어 레벨에서 변경된 사항들로 인한 이슈와 변경 방법을 참고 하십시오.

.NET Framework 4 Migration Issues

   

.NET Franework 4.0 호환성

특히 .NET Framework 4.0 부터는 기존의 .NET Framework 2.0 부터 .NET Framework 3.5 SP1 까지 사용된 CAS(Code Access Security) 와 관련한 변경 사항으로 .NET Framework 의 전반적인 보안 관련 정책이 변경이 되었습니다.

Code Access Security Policy Compatibility and Migration

그 외에도 .NET Framework 4.0 환경에서 기존의 어플리케이션이나 콤포넌트를 정상적으로 동작시키기 위하여 아래의 문서를 참고하시기 바랍니다.

Version Compatibility in the .NET Framework
.NET Framework 4 Application Compatibility Walkthrough

   

ObsoleteAttribute 특성을 피할 것

또한, 상당히 많은 양의 클래스나 구조체들이 ObsoleteAttribute 특성이 적용되었습니다.

장기적으로 지속 가능한 어플리케이션을 위하여 ObsoleteAttribute 특성이 적용된 API 는 절대 사용하지 않는 것을 권장하며, 아래의 문서를 참고하십시오.

Obsolete in the .NET Framework Version 3.5
.NET Framework V2.0 Obsolete Type/Member List (By Namespace)

Obsolete Types in the .NET Framework 4

   

그래도 문제가 발생한다면…?

혹시 그래로 문제가 발생하시나요? 그럼 Microsoft Connect 사이트에서 당신의 문제를 보고하시기 바랍니다. 또는 netfxcf@microsoft.com 으로 버그 번호와 함께 이메일을 보내시기 바랍니다.

(MSDN 문서에서 이곳으로 오류를 보고하라고 하네요 ^^)

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

먼저 이전 포스트의 "MEF 는 Generic Type 을 지원하지 않는다!" 에서 언급했고, .NET CLR 2.0 부터 Generic Type 을 지원함에도 불구하고, .NET Framework 4.0 에 포함되는 MEF 가 Generic Type 을 지원하지 않는다는 것은 솔직히 납득하기가 어렵습니다. MEF 개발 PM 이 말하는 강력한 계약 기반(Strongly Contract Based) 의 모델이라는 점은 머리로는 이해는 되지만, 사실 안될 것도 없습니다. -_-;

MEF 가 갖는 대표적인 키워드인 Composable 은 현재 Generic Type 을 지원하지 않지만, 상당히 매력이 있습니다. 이미 현대적인 프레임워크는 Modular 에 집중하고 있고, MEF 는 더 나아가 Modular + Composite 이라는 상당한 매력을 가진 프레임워크입니다.

일단 서두는 이쯤에서 접어두고, MEF 가 Generic Type 을 지원하기 위한 몇 가지 공개되어 있는 방법을 알아보고, 다시 이야기를 나누어 봅시다. 
   

How to support Generic Type of MEF ?    

첫 번째 방법 - Factory Provider

가장 간단한 방법이 바로 Factory Pattern 을 이용한 방식입니다. 객체의 생성은 Factory 를 통해 생성하도록 하고, Factory 는 객체의 Type 을 받음으로써 객체의 생성을 Factory 에게 모두 의존하는 방법입니다. 우선 아래의 링크를 참고하세요.

 ExportProvider 를 재정의하여 객체의 Type 을 등록하여 원하는 Type 의 객체를 생성하도록 합니다.

1: public interface IService { }

2: public interface IUserService : IService { }

3:  

4: [Export]

5: public class UserController {

6: [ImportingConstructor]

7: public UserController(IUserService userService) { }

8: }

9:  

10: // in your application

11: private void Compose() {

12: var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());

13: var factoryProvider = new FactoryExportProvider<IService>(GetService);

14: var container = new CompositionContainer(catalog, factoryProvider);

15: container.AddPart(this);

16: container.Compose();

17: }

18:  

19: public IService GetService(Type type) { return ... }

   

하지만, 이 방법은 상당히 문제가 많은 방법입니다. 가장 즐겨쓰고, 흔히 볼 수 있는 Pattern 이기 때문에 추가되는 Factory 마다 객체 등을 Factory Provider 에 등록을 해 주어야 합니다. 그 뿐만이 아니죠. Factory Pattern 의 특성상 객체를 생성하는 Factory 는 일일이 각 객체의 타입을 체크하여 반환해 주어야 합니다.

그리고 위의 코드에서는 Type 인자가 1개이지만, 그 이상이라면??? 가령, Generic Type Class<T1,T2,T3,T4,T5> 가 된다면 대략 난감하겠죠. 일단 작은 코드에서는 쓸만할 수 있지만, 꾸준히 성장하는 코드라면 이러한 Factory 방식은 코드의 변경이 너무 잦아집니다.

   

두 번째 방법 - Type Mapping

MEF 는 Codeplex 에 공개가 되어있고, MEF Contrib 으로 불리우는 MEF 의 확장 라이브러리 입니다. MEF Contrib 의 가장 큰 특징 중에 하나인 ComposablePartCatalog 를 재정의 하는 Generic Catalog 를 지원해 줍니다. 이 링크에서 Type Mapping 을 통한 문서를 볼 수 있습니다.

public class GenericCatalogContext
{
protected AggregateCatalog _aggegateCatalog;
protected GenericCatalog _genericCatalog;
protected ImportDefinition _repositoryImportDefinition;

public GenericCatalogContext()
{
var typeCatalog = new TypeCatalog(typeof(OrderProcessor), typeof(RepositoryTypeLocator));
_aggegateCatalog =
new AggregateCatalog();
_aggegateCatalog.Catalogs.Add(typeCatalog);
_genericCatalog =
new GenericCatalog(_aggegateCatalog);
string orderProcessorContract = AttributedModelServices.GetContractName(typeof(OrderProcessor));
var orderProcessPartDefinition = typeCatalog.Parts.Single(p => p.ExportDefinitions.Any(d => d.ContractName == orderProcessorContract));
_repositoryImportDefinition = orderProcessPartDefinition.ImportDefinitions.First();
Context();
}

public virtual void Context()
{

}
}

[InheritedExport]
public abstract class GenericContractTypeMapping
{
public GenericContractTypeMapping(Type genericContractTypeDefinition, Type genericImplementationTypeDefinition)
{
}

public Type GenericContractTypeDefinition { get; }
public Type GenericImplementationTypeDefinition { get; }
}

public class RepositoryTypeLocator : GenericContractTypeMapping
{
public RepositoryTypeLocator()
:
base(typeof(IRepository<>), typeof(Repository<>))
{
}
}

public class Repository<T> : IRepository<T>
{
}

이러이러한 과정을 통해서 아래와 같이 Type Mapping 을 통해 Generic Type 을 사용할 수 있습니다.

[TestFixture]
public class When_querying_catalog_for_an_order_repository_and_no_closed_repository_is_present : GenericCatalogContext
{
[Test]
public void order_repository_part_definition_is_created()
{
Assert.IsNotNull(_result.Item1);
}

[Test]
public void order_repository_export_is_created()
{
Assert.IsNotNull(_result.Item2);
}

public override void Context()
{
_result = _genericCatalog.GetExports(_repositoryImportDefinition).Single();
}

private Tuple<ComposablePartDefinition, ExportDefinition> _result;
}

Contract Type 와 Mapping Type 을 매핑하여 Locator 로 등록하여 주고, 각각 Mapping Class 를 통해 실제 계약의 Generic Type 매핑이 이루어 집니다.

다시 말해서, Generic Class 별로 Locator Class, Mapping Class, 그리고 Mapping Context Class 를 만들어주어야 합니다. 배보다 배꼽이 더 커지는 격입니다. 일단, 아이디어는 좋지만 안쓰고 말랍니다.

   

세 번째 방법 - MEF + Unity 조합

아마도 가장 이상적인 방법이긴 합니다. Unity Application Block 은 Unity Container Extension 을 지원하기 때문에 객체의 Register, Resolve 등의 이벤트를 가로채서 Unity 의 기능을 확장할 수 있습니다. 이 이벤트를 MEF 에서 받도록 하여 MEF 의 ExportProvider 의 GetExportsCore 를 통해 Unity 의 객체에서 Resolve 하도록 하는 방법입니다.

UnityContainerExtension 을 재정의하여, 아래와 같이 이벤트를 받고, 이것을 MEF ExportProvider 로 전달하는 방법입니다.

UnityContainerExntension 에서는 아래와 같이...

protected override void Initialize()
{
this.Context.Registering += new EventHandler<RegisterEventArgs>(Context_Registering);
this.Context.RegisteringInstance += new EventHandler<RegisterInstanceEventArgs>(Context_RegisteringInstance);
}

MEF 의 ExportProvider 에서는 아래와 같이…

protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
{
if (definition.ContractName != null)
{
Type contractType;
if(Mapping.TryGetValue(definition.ContractName, out contractType))
{
if (definition.Cardinality == ImportCardinality.ExactlyOne || definition.Cardinality == ImportCardinality.ExactlyOne)
{
var export = new Export(definition.ContractName, () => serviceLocator.GetInstance(contractType));
return new List<Export> { export };
}

}
}
return Enumerable.Empty<Export>();
}

일단 가장 완벽해 보입니다만, 이 속에는 그 이상 많은 문제들이 생기게 됩니다. MEF 도 내부적으로 Injection(주입) 기법을 사용하고, Unity 에서도 Injection 을 사용하는데 바로 이 Injection 방법이 달라지게 되는 것입니다. 즉, MEF 기반의 코드와 Unity 기반의 코드의 Injection 선언 방법이 틀려지고, 서로 호환할 수 없다는 것입니다.

결국 DI 프레임워크는 특정 DI Container 에 의존할 수 밖에 없어지고, 더불어 Compisite 과 Injection 은 두 가지의 사용 방법이 혼재될 수 밖에 없다는 것이죠.

   

Conclusion

MEF 에서 Generic Type 을 사용하고 싶어서 안달이 난 1은 여러 가지 방법을 찾아보았지만, 사용성, 재사용성, 확장성, 유연성 등 모든 면에서 원하는 해답을 찾지 못했습니다. 그리고 현재까지 MEF 에서 Generic Type 을 지원하기 위한 대략적인 3가지 방법을 정리해보도록 하죠.

  

장점

단점

MEF Factory Export Provider

  • 구현이 쉽다
  • Factory 의 관리가 힘들다
  • Factory 의 확장이 힘들다
  • 모든 Factory 를 Catalog 로 관리해야 한다.

MEF Contrib Type Mapping

  • 합리적이다
  • Type Mapping 코드가 복잡하다
  • Mapping/Locator/Context 클래스를 구현해야 한다
  • 상속 기반이다

MEF + Unity Integrated

  • 합리적이고 , 구현이 쉽다
  • Injection 기법이 서로 달라진다
  • Injection 코드가 서로 달라진다
  • Injection 이 호환되지 않는다
  • 각각의 객체간의 Composite 이 불가능하다

이제 슬슬 머리가 아파옵니다. 향후 .NET Framework 4.0 에서 가장 큰 빛을 보게 될 MEF 이지만, Generic Type 을 지원하지 않는다는 것은 가장 큰 오점이 아닐까 생각합니다. 우선 이쯤에서 마무리하고 어떻게 해야 할지 생각해 보도록 하지요.


MEF 에서 Generic Type 문제는 코드 플랙스에 MEFGeneric 으로 공개하였습니다.
[.NET/.NET Framework] - MEFGeneric 코드 플랙스에 공개합니다.

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

MEF Preview 6 공개

Managed Extensibility Framework 2009.07.20 12:36 Posted by POWERUMC

MEF(Managed Extensibility Framework) 이 2009년 7월 13일에 릴리즈되어 14일에 공개가 되었습니다. 특히 MEF 는 .NET Framework 4.0 에 포함이 되어있으며, CodePlex 에서 굉장히 빠른 속도로 발전하고 있는 프레임워크 중에 하나 입니다.    

CodePlex MEF 사이트에 등록된 릴리즈 노트 입니다.

그 중에서 몇 가지만 살펴보도록 하겠습니다.

실버라이트 3 지원

특히 이번에 눈여겨 볼 만한 것이 Silverlight 3 를 지원하는 것입니다. 이미 Preview 5 이전의 MEF 에서는 실버라이트를 지원하기 위해 코드에서 전처리 명령 구문을 사용하여 프레임워크 실버라이트가 지원되긴 했습니다. 그것을 기반으로 이번 Preview 6 버전에서는 실버라이트 3 용 솔루션이 제공됩니다. 이 솔루션을 통해 실버라이트용 MEF 프레임워크를 빌드 하시면 됩니다.

   

ExportAttribute 의 seal 키워드 제거

이번 MEF Preview 6 에서 ExportAttribute 의 seal 키워드가 제거됩니다. 이 전 Preview 5 에서는 ExportAttribute 을 절대 상속할 수 없는 구조였기 때문에 독자적인 Export 를 제공할 수 없었기, Metadata 를 ExportAttribute 와 같은 기능울 개별적으로 지정해야 하는 등의 Export 기능의 확장에 불합리한 구조로 되어있었습니다. 하지만 이번 Preview 6 버전에서는 ExportAttribute 클래스의 seal 키워드가 제거 됨으로써 ExportAttribute 의 확장이 용이하게 되었습니다.

 

ImportMany 로 통일된 컬렉션 처리

이제 모든 컬렉션은 ImportMany 로 통일되었습니다. Preview 5 에서는 ExportCollection<T> 또는 IEnumerable<T> 와 같이 사용하기도 까다롭고 이것으로 반환된 객체를 처리하기에도 편리한 구조는 아니였습니다. Preview 5 에서도 ImportMany 의 기능이 존재하였지만, 이번 Preview 6 는 이것으로 모든 컬렉션 처리를 하도록 통일되었다는게 혼란스러웠던 부분을 간소화할 수 있을 것 같습니다.

   

기타 그 밖에도 구조적으로 보다 안정되었고, 메서드나 클래스의 네이밍이 변경되었고, 예외 시에 Composition 동작의 분석을 추적하기 쉽도록 덤프(Dump) 를 뜰 수 있는 로깅 기능 등 다양한 기능이 추가되었습니다. 정식 버전의 MEF 는 어떤 모습으로 나올지 정말 기대가 됩니다. 더 자세한 내용은 아래의 링크를 통해 더 쉽게 설명이 되어있으니 참고 하십시오.


참고문헌
MEF Preview 6 Available
http://blogs.msdn.com/nblumhardt/archive/2009/07/09/mef-preview-6-available.aspx

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Setting up the infrastructure for developing VSIX

개발을 시작하기 전 기본적인 인프라 구성을 먼저 하여 봅니다. 일단 Visual Studio 2010 을 다운로드 받아 설치해 보도록 하겠습니다.


친절하게도 이전에 슈러님께서 포스팅한 글이 있으므로 참고하면서 설치 하도록 하고, 이제 Visual Studio 2010 Beta 1 SDK를 설치 해 보도록 하겠습니다.

Installing Visual Studio 2010 SDK Beta 1


참고 링크를 따라 이동 하면 그림 1-1과 같은 Microsoft Download Center의 웹페이지가 나타날 것입니다. 거기서 Download 버튼 클릭 하면 Visual Studio 2010 SDK Beta 1을 다운로드 하실 수 있습니다. Release 날짜가 2009년 6월 12일로 다소 따끈 따끈한(?) 아이입니다.
Visual Studio 2010 SDK에는 Tool Windows, Menu Commands, Isolated Shell Projects, Editor Extension을 생성 할 수 있도록 프로젝트 템플릿을 제공하고 있으며, Editor Extension에는 Text Adorment, Colorizer, Magin 템플릿을 포함하고 있고, 빌드와 디버그 Extension은 개발자에게 VSX를 개발
                                                                           <그림 1-1>      하는데 큰 도움을 줍니다.

                                                                                                                         <그림 1-2>
그림 1-2는 초기 Visual Studio 2010 SDK Beta 1 Setup 화면입니다 Next버튼을 클릭하여 다음으로 넘어 갑니다. 그림 1-3과 같이 라이센스 동의 화면이 나타나는데 역시나 인스톨할 때 화면들이기 때문에 다음버튼만 죽어라 누르면 됩니다. 

                                                                                                                         <그림 1-3>
그리고 설치 경로를 물어보고 다음 버튼을 선택하면 설치를 시작합니다. 설치 시간은 대략 몇분이면 마무리 되므로 큰 인내심은 필요치 않습니다. 그리고 정확히 DSL 부분까지 파고 들어갈지 의문이기 때문에 만약 DSL도 다루게 된다면 그때가서 설치 부분을 포스팅 할지를 생각해 보도록 하겠습니다.

VSX를 개발하기 위한 물밑 작업은 완료 되었습니다. 참 쉽~죠잉~!
File > New > Project 를 클릭하면 그림 1-4와 같이 Extensibility 메뉴에 해당 Project Templete이 생성되어 있는 것을 확인 하실 수 있습니다.
                                                                                                                                    <그림 1-4>

다음 포스팅에서는 Editor Templete에 기능에 대하여 알아 보도록 하겠습니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Evolution of ASP.NET

.NET Framework 4.0 beta 1와 함께 ASP.NET도 새로운 변화를 시도하였습니다. ASP.NET 2.0의 변화 보다는 덜 파격적이지만, 그 동안 .NET Framework 업데이트가 진행되는 동안 ASP.NET의 변화가 미미했던 것을 감안 한다면, 이번 .NET Framework 4.0 업데이트와 함께 동승한 ASP.NET의 업데이트는 놀라울 만큼 바뀌었습니다.(스캇 구스리 아저씨가 힘좀 쓰셨나 봅니다.ㅋㅋ)
그 중 Core Service 업데이트는 Performance 측면과 기존 ASP.NET Issue를 처리하는데 두드러진 발전을 이루어 냈습니다. 그럼, Core Service의 주요 업데이트 사항을 간략하게 소개하고 넘어가도록 하겠습니다.

1. Extensible Output Caching - 확장 가능한 출력 캐쉬 정도로 직역하겠습니다. 기존에도 있던 페이지 캐싱을 개발자 나름대로 커스트마이징 할 수 있도록 확장 시켜준 기능입니다.
2. Auto-Start Web Application - HTTP Request에 대하여 콜드 스타트 또는 개별 응용프로그램의 재사용에 소모되는 비용을 절감하기 위한 기능인 듯 합니다. 왜 ASP.NET 맨처음 띄우면 밥먹고 담배피고 와야 하잖아요.(오바입니다만...) 그런 비용을 절감 하기 위해 나온 기술인듯합니다. 허나, Windows Server 2008 R2에 탑재 가능한 IIS 7.5에서만 지원되는 기능이라니 OTL이 아닐 수 없습니다. 한 3년동안은 이 기술 볼 수 없을 듯합니다.(회사에서 2008을 깔아야 뭘 하죠? 아직 2000 쓰는 곳도 많이 있는데....)
3. Permanently Redirecting a Page - 영원히 헤어지는 이전 페이지와의 관계, 참 무슨 영화 제목 같 군요. 이전에 이슈가 되었던 Request가 여러번 일어나던 오래된 페이지에서 Redirect를 시킬 경우 HTTP 302 요청 이슈에 대한 대처 방안입니다. 이건 뭐 다들 아실만한 내용이겠네요.
4. The Incredible Shrinking Session State - 깜놀하게하는 세션 상태정보의 감소로 의역(?)하죠. ASP.NET 에서 단일 서버라면 모르지만, 웹팜(Web farm - 모르신다면 당장 찾아 보세요. 롸잇 놔우)에서는 세션 상태 정보를 저장하기 위해서 세션 State Server를 구성하거나 MS SQL을 이용하여 세션 정보를 저장할 수  있도록 저장소를 두는 방법이 있었습니다. 근데, 문제는 두 State Server 다 거의 Raw데이타에 가까운 정보들이 날(Raw)로 저장되어 엥간한 동시접속자 수를 자랑하는 사이트에서는 엄청난 데이터량이 문제였습니다. 데이터 양도 문제지만 Network Traffic도 데이터 양 비례하게 올라가는 문제를 양산하게 됐죠. 이를 해결하기 위해 Gzip Compression을 사용했다는 군요.
그럼, 본격적으로 Extensible Output Caching에 대해 알아 보도록 하겠습니다.



What is Extensible Output Caching?  

말 그대로 확장 가능한 출력 캐쉬 기능입니다. 쉽게 설명 하자면, 엄연히 Caching 기능은 ASP.NET 1.0부터 있었습니다. 페이지나 컨트롤, HTTP Response의 출력물(뭐 쉽게 말하자면 컴파일러에서 html 코드로 완전히 변환된 상태의 코드)을 서버의 메모리 내에 상주시킴으로써 재생산에 드는 비용을 감소 시키는 데에 일조 하던 기능입니다. 하지만, 이것도 만만찮게 다른 문제를 일으키고는 했는데요 저장소는 무조건 메모리로 박치기 하다보니, 다른 웹 어플리케이션과 메모리 선점 전쟁을 하게 되는 문제가 생기고, 서버 자체적으로는 쓰잘때기 없는 것(일명 쓸애기) 까지 메모리 영역을 점령함으로써 서버 리소스를 잡아먹게 되는 문제가 생긴 것이지요.

그래서, 기존에 있던 기능은 살리고~ 살리고~ 살리고 살리고 살리고 커스텀한 Provider만 Implement(구현)하여 대체 저장소를 선택할 수 있도록 한 기능입니다. 물론 MS SQL 서버에도 저장이 되도록 기능을 갖추고 있습니다.

그럼 실전으로 들어가 정확히 뭐하는 기능인지 코드로 살펴 보도록 하겠습니다.

Walkthrough : Step 1. Create a Web Project

웹 프로젝트를 그림 1-1 과 같이 선택하여 생성합니다.

<그림 1-1>

<그림 1-2>

자~! 여기까지는 많이 하셨을 테니 거두 절미 하고, 솔루션에 CustomOutputCacheProvider Class를 추가하기 위해 클래스 라이브러리 프로젝트를 생성하도록 하겠습니다.
                                                                                                                   <그림 1-3>
                                                                                                                                    <그림 1-4>

프로젝트를 생성하셨다면 TestCustomOutputCacheProvider 클래스를 추가 시킵니다. 그리고, CustomOutputCacheProvider 프로젝트에 System.Web과 System.Confihuration을 참조 추가 시키겠습니다. 이제 어느정도 구색이 갖추어졌습니다.

Walkthrough : Step 2. Implement OutputCacheProvider

이번 단계에서는 개발자 특정 저장소를 지정할 수 있도록 커스텀한 CacheProvider를 생성도록 하겠습니다.

<그림 1-5>

그림 1-5와 같이 System.Web.Caching.OutputCacheProvider 추상 클래스를 Implement할 경우 자동으로 오버라이드 메서드가 생성되는 것을 볼수 있습니다.
                                                                                                                           <그림 1-6>

기능 구현에 많은 시간을 할애 할 수 없는 관계로 간단하게 세션에다가 캐싱을 하도록 구현 하겠습니다. 저장소를 선택하고 구현 하는 부분은 각자 구미가 땡기는 대로 구현하여 보세요.

Walkthrough : Step 3. Setting WebSite

먼저 그림 1-7과 같이 Web.Config파일에 caching 섹션을 추가하고 Provider 섹션을 구성하도록 하겠습니다.
<그림 1-7>
 
여기서 부연 설명 드리겠습니다. <outputCache> 섹션의 Attribute인 defaultProvider는 Web Application에 기본적으로 제공할 OutputCacheProvider입니다. 나중에 나올 Global.asax에서 설정해 주지 않는 이상 자동적으로 AspNetInternalProvider를 제공합니다. 그리고 하위 섹션인 <providers> 섹션 이하 섹션에는 개발자가 커스텀 하게 만들어 놓은 Provider를 넣는 부분입니다. 복수 개의 CacheProvider를 제공할 수 있어 좀 더 성능 향상에 기여 할 수 있습니다.(점점 말투가 딱딱해 지고 있습니다. 퇴근 시간이 훌쩍 넘어서 인지 정신적 압박이...)

그리고나서, Global.asax를 추가 시켜보도록 하겠습니다. 그림 1-8과 같이 새로운 아이템 추가 버튼을 클릭하면 대화 상자에서 Global.asax파일을 선택하여 확인 버튼을 선택합니다.
                                                                                                     <그림 1-8>

추가된 Global.asax 파일을 열고 그림 1-9의 코드를 추가 시켜 줍니다.
                                                                                                                       <그림 1-9>

 
GetOutputCacheProviderName 메서드는 Web.config 파일에 <caching>섹션이 추가 되어 있어야만 호출 되는 메서드 입니다. 그리고 코드를 보면 특정 페이지 에만 "SessionCache"라는 Provider명을 리턴하게 되어 있는데 이 부분은 Web.config의 <provider> 하위 노드의 Name Attribute와 매핑되는 키값입니다. 즉, 특정 페이지에 접근 시 개발자가 원하는 CacheProvider에 접근하여 커스텀한 저장소에 저장하게 하는 기능의 Entry Point 라고 보셔도 무방합니다.

그리고 UserControl을 생성하여 간단한 컨트롤을 작성하여 보겠습니다. 그리고 UserControl 선언적 구문 페이지 상단에는 <%@ OutputCache Duration="60" VaryByParam="None" providerName="SessionCache" %>
을 기입하여 줍니다. Duration의 의미는 소멸되기 까지 대기 시간으로 60초이고 VaryByParam은 Post든 QuerString이든 파라미터가 넘어오는 건마다 페이지를 저장시킬지의 여부를 선택합니다. 그리고 providerName은 해당 UserControl에서 제공할 Provider를 선택하는 Attribute입니다다.
그리고, 한가지 재미 있는점은 여러개의 Provider를 구현 하고 Web.config에 등록 시킨 후 각각 UserControl에 다른 Provider를 등록 시키면 Global.asax에서 한가지 ProviderName을 리턴 하더라도 각각의 Provider 저장소에 저장이 된다는 것입니다.

이제 마지막으로 웹 프로젝트에 CustomOutputCacheProvider 프로젝트를 참조한다. 그림 1-10r과 같이 전체적인 Test 솔루션 구성이 끝이 났습니다.

                                                                                                   <그림 1-10>

Walkthrough : Step 4. Running Web Application and Result

이제 마지막 단계 Test Application을 구동하고 값을 확인하는 작업만 남겨두고 있습니다. F5를 눌러 빌드를 한 후 정상적으로 빌드 성공이 되면 Web browser가 화면에 나타날 것 입니다. 제가 값을 확인 하기 위해 몇개 중단점을 찍어 값을 확인 하는 것으로 이번 아티클을 마무리 할까 합니다.

맨 처음 브라우저가 화면에 나타나고 그림 1-11처럼 Global.asax의 GetOutputCacheProviderName메서드에 중단 점이 걸리는 것을 확인 할 수 있습니다.
                                                                                                                              <그림 1-11>
그 다음 F5를 다시한번 눌러 다음 중단점으로 이동하면  우리가 만들어 놓은 TestCustomOutputCacheProvider 클래스의 Set메서드를 호출하는 것을 확인 할 수 있습니다.

                                                                                                                              <그림 1-12>
Text Visualizer로 값을 확인한 결과 그림 1-13과 같이 렌더된 Html 태그값이 저장소에 저장되는 것을 확인 할 수 있습니다.

                                                                                                         <그림 1-13>
마지막으로 인위적으로 포스트 백을 일으켜 저장소에 저장된 값을 가지고 오는 과정을 확인 하도록 하겠습니다. 화면에 버튼 이벤트를 일으키면 서버 쪽 Global.asax의 GetOutputCacheProviderName메서드를 재 호출 하여 페이지에 할당된 ProviderName을 리턴받은 후 TestCustomOutputCacheProvider 클래스의 Get메서드를 호출하는 것을 확인 할 수 있습니다. Get메서드에서는 이미 저장소에 캐싱된 Html 값이 반환 되어 사용자에게 전달되는 것을 확인 할 수 있습니다.

<그림 1-14>

정말 오늘 길게도 썼습니다. 씁씁후후~ 어떤 메카니즘을 가지고 동작하는지 대충 이해하셨을 듯 합니다. 그럼 다음에도 ASP.NET 4.0에 다른 기능에 대하여 알아보도록 하겠습니다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

VSIX 시작하기

본격적으로 VSIX 프로그래밍에 대하여 알아 보도록 하겠습니다. 일단 VSIX의 개념 부터 짚고 넘어가는 것이 순리일듯 합니다. 일단 이미 이전에 엄준일씨에게서 포스팅 된 Visual Studio 2010 Extension Manager를 참고하는 편이 나을 것입니다.
VSIX는 뭐의 줄임말일까요? Visual Studio 2010에서 새롭게 선보인 VSIX는 언뜻 Visual Studio Extensibility와 관련 있는 듯 하나 VSIX는 인스톨러와 관련이 있으므로 Visual Studio Installer Extension의 의미일 듯 합니다.(이건 필자의 생각은 아닙니다. 머리나쁜 필자가 저런 의미심장한(?) 단어를 생각해 낼 일이 없기 때문에)

앞서서 1. What is different from before version에서 포스팅한 내용에 추가적으로 이전 버전과는 다른 Deployment Packaging을 지원하기 시작하였습니다. 이전에는 Packaging을 하기 위해서는 Setup 프로젝트를 생성하고 Regpkg.exe를 통해 Package 등록에 필요한 일련의 과정들을 설정하여 Deployment하는 복잡한 과정을 거쳐야 하였습니다.


하지만, 이제 상황은 달라졌습니다. VSIX가 나오므로 해서 패키징과 배포가 아주 아주 용이해졌을 뿐이나라 Extension Manager를 통하여 설치된 VSX 목록을 관리 할 수 있는 편리함도 함께 추가 되었습니다.

VSIX는 일종의 범용적인 Package로써 .zip의 OPC(Open Packaging Convention)가 적용된 형태라고 볼 수 있습니다. 그럼 심심한데 압축을 풀어 볼까요. 실제 압축이 풀리는지 막 궁금하지 않습니까? 필자만 궁금한가? 필자만 변태인가? 흐흐

<그림 1-1>

테스트로 프로젝트를 생성하고 빌드를 한 상태로 Debug 폴더로 들어가 보겠습니다. 그림 1-1과 같은 화면이 눈앞에 보일 것입니다. 뭐 별다른건 없어 보이는군요. Manifest파일과 .vsix 파일 말고는 우리가 자주 보던 어셈블리와 디버그 파일입니다. 그러나 .vsix의 압축을 풀어 보겠습니다.

<그림 1-2>
그림 1-2와 같이 .vsix 파일명과 같은 폴더가 생성된 것을 볼 수 있습니다. (아~ 그래픽 툴에 상당히(?) 익숙하고 디자인 감각이 있는 필자는 <~요기 라고 표현함으로써 직관성과 예술성을 감미하였습니다.)
그럼 해당 폴더로 들어가 보겠습니다.

 
<그림 1-3>

역시나 기대 했던것 처럼 .vsixmanifest파일과 해당 어셈블리가 위치되어 있습니다. 근데, [Content_Type].xml은 뭥미? 그래서 까봤습니다.(궁금한건 못참는 이 죽일놈에 개발자 습성 때문에)
<그림 1-4>

역시나 .vsix 내부의 파일 타입이 OPC API 요건 대로 아주 잘 정리되어 있는것 을 볼 수 있습니다.
그럼, .vsix 컨텐츠 목록을 정리해보겠습니다.
 [Content_Type].xml  컨텐츠의 알려진 파일에 대한 정보 수집 장소
 extension.vsixmanifest  Extension의 정보를 담고 있는 manifest정도로 이해
 하면 될듯?. 일종의 config파일과 비슷(?) 오바일까요?
 Assembly file  우리가 개발한 어셈블리 파일
 나머지 듣보잡  기타 아이콘, 이미지 기타 등등 파일들
<표 1-1>
 이상으로 .vsix를 까발리는 행위를 마치고 순수 VSIX를 개발하기 위한 일련의 과정들을 설명하기로 하겠습니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
VSX ( Visual Studio Extensibility ) 란?
Visual Studio Extensibility (이하 VSX )는 예전 부터 우리가 쓰고 있는 Visual Studio와 통합하여 개발 생산성을 높이고자 기존에 제공 되던 IDE환경에서 보다 커스트마이징한 환경을 구축하는 것을 말합니다.
이미 Third-party 솔루션을 제공하는 회사는 널리고 널렸으며, 회사 자체 Framework을 구축하고 있는 곳은, 자체적으로 개발 생산성 향상을 위하여 VSX를 제공하고 있는 것을 흔히 볼수 있을 것입니다.


 
이미 Visual Studio 2005 와 Visual Studio 2008에서 그 기능을 강력하게 지원하고 있으며, 기본적으로 Visual Studio SDK를 설치 하지 않아도 제공되는 Add-In Templete은 쉽게(?) 원하는 Application을 작성할 수 있도록 도와 주고 있습니다.

주로 VSX 종류는 3가지로 나눌 수 있습니다.

1. Visual Studio Add-In
우리가 쉽게 할 수 있는 Add-In은 그 자체만으로 강력한 기능을 제공합니다. Visual Studio의 오브젝트 모델에 접근 할 수 있고, 사용자 인터페이스를 추가하고, 여러 옵션 툴바, 메뉴 등의 IDE 요소에 접근하여 컨트롤할 수 있게 제공하고 있습니다. 주로 Add-In으로 Package보다는 간단하지만 그 기능만은 간단하지 않은 기능들을 선보이고 있으므로 충분히 이것으로도 원하는 산출물을 내놓을 수 있다고 생각하고 있는 바이지만, 이 아티클을 쓰는 이유와는 좀 다르므로 일단 패스하고 추후, 필자의 블로그를 통해 간단하게 나마 소개해보고자 합니다.(필자 엄청난 귀차니즘으로 그날이 언제쯤 올런지.... 쯧쯧)

2. Visual Studio Macro
아~ 매크로 이름만들어도 귀에 팍팍 꽂히는 소리가 들리는군요? MS Office Excel의 기능을 화려하게 만들어 주는 이놈이 Visual Studio에도 있었습니다.(실제 쓰는 사람은 몇 보지 못한 1人) Package보다 쉽고 Add-In 보다 쉽다라고 자부하는 매크로는 주로 아주 반복적인 작업에 많이 쓰입니다. 하지만 우리가 원하는 커스트마이징과 퐌타스틱한 Application에는 조금 아쉬운듯한 새로운 기능 추가 조건에는 맞지 않습니다. 하지만, 오토메이션 업무에는 뛰어난 발군의 기능(?)을 제공하고 있으니 무시할 수는 없는 노릇입니다.

3. Visual Studio Package
드디어 나왔군요 Package. 총 3가지 중 Visual Studio의 활용성과 기능 추가성에 대해서는 Package만한 놈이없을 정도로 그 기능적인 확장성을 인정 받습니다. Package는 Unmanaged 즉 Native언어로 구현 가능하며, 또한 C#, VB와 같이 CLR에서 동작할 수 있는 Managed Language로도 구현이 가능하다니, 필자와 같은 C# 개발자에게는 좋은 IDE를 제공하여 주는 셈입니다. 

이렇게 VSX를 이루고 있는 아이들을 살펴 보았습니다. 그럼, 과연 Visual Studio 2010과 이전 버전 (이하 Visual Studio 2008 또는 2005)과는 무엇이 틀린지 살펴 보도록 하겠습니다.(필자 생전 태어나서 처음 아티클 이란 것을 써서 그런지 몰라도 벌써 퇴근의 압박과 고질적인 손가락 관절염이 도지고 있네요.)

What is different?

가중 눈에 띄게 달라진점을 꼽으라면 역시나 .NET Framework 4.0 부터 지원하는 MEF일 것입니다. (필자 MEF 땜에 VSIX 테스트 프로젝트 생성하고 안돌아가 똥줄 좀 탔습니다.)
MEF에 대해서 머리에 물음표가 그려지는 개발자 여러분께서는 엄준일씨(땡초)가 고생 고생해서 써놓은 아티클을 참고 하길 바랍니다. (친절한 필자는 링크도 남겨주는 군화. http://www.vsts2010.net/13)
MEF 즉 단위 컴포넌트 별로 쪼개어 개발될 뿐만 아니라, 의존성 또한 약화 되어버리니 이건 Visual Studio에서 그토록 기다리고 기다리던 기능이 아닐까 합니다. (아무래도 이전 버전에서 패키지를 관리하기에는 좀 어려움이 있기는 했습니다.)
그리고, MEF는 필자 개인적인 생각이고 UI측면에서는 WPF로 바뀐 Editor라고 할까나... .NET Framework 3.0 부터 지원하던 WPF가 드디어 IDE에 녹여지게 되었고, 추가적인 Editor 기능 역시 WPF로 구현이 가능하게 되어 개발하면서 움직이는 화면을 보는 재미가 쏠쏠하지 않을까 합니다.(필자 UX 개발자가 아니어서 저런 기능까지 구현할까라는 의구심이 들기도 합니다. 만들어 놓으면 그림판만 WPF지 윈폼 컨트롤 못지 않는 딱딱함을 지원할것이 100%입니다.) 그리고 Package프로젝트 생성 시 이전 버전과는 달리 Editor Templete이 분리 되어 있는 것을 볼 수 있을 것입니다. 아마도 MS는 이번 2010의 Editor에 사활을 거는듯 합니다. 왜냐하면 Editor의 Architecture를 눈에 띄게 변경시켜 놓았으니 말입니다. 이번 Framework 변경사항과도 일맥 상통하게 확장성이 화두인듯 합니다.


What is Next?
  
 
다음 번부터는 Tutorial 형식으로 하나하나 따라해 볼 수 있도록 구성해볼 생각입니다. 설치에서 부터 간단한 어플리케이션 작성 그리고 개인적으로는 Azure 도 붙여 보고자 합니다. 아주 재미 있을 것 같지 않습니까? 필자 엉덩이가 들썩 들썩하는 소리가 들려 오는 군요.
참고로 국내에서는 VSX 개발에 대한 리소스가 충분치 않는 것이 사실입니다. 필자의 검색능력이 부족하여서 인지도 모르겠으나, 많은 관심을 가지고 본다면, 정말 재미있는 영역이 될것입니다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Strongly Typed Metadata
 
지난 포스트의 [MEF] 7. Exports and Metadata를 통해 Export 의 Contract 에 Metadata 를 제공하는 방법을 알아보았습니다.
 
MetadataAttribute 을 선언하여 Export 의 Metadata 를 제공하는 방법입니다.
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Email")]
[ExportMetadata("Logging", true)]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
Export 에 Metadata 를 제공해 주어서 무척 고맙지만, 위의 방법처럼 MetadataAttribute 를 선언하는 방법을 사용하기에 그다지 내키지 않는 구석이 있습니다.
 
아무래도 아래와 같은 이유 때문이겠죠?
 
l 메타데이터의 타입 불안정
l 빌드 시 오류를 해결이 어려움
l 사용상 모든 키값을 외우기 어렵고, 오타 발생 위험
 
위의 이유 때문에 Metadata 를 사용하기 위해 강력한 타입을 원하게 될 것입니다. 그리고 강력한 타입의 Metadata 를 사용하길 권장 드립니다.
 
 
Declaring Strongly Typed Metadata
 
Attribute 을 상속받아 Export 의 Metadata 를 Strongly Typed 으로 확장시키는 방법입니다.
 
아래의 Stringly Typed Metadata 를 위해 Attribute 클래스를 만드는 소스 코드 입니다.
 
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MessageSenderTypeAttribute : Attribute
{
        public MessageSenderTransport Transport { get; set; }
        public bool IsSecure { get; set; }
 
        public MessageSenderTypeAttribute() { }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport)
               : this(transport, false)
        {
        }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport, bool isSecure)
        {
               this.Transport = transport;
               this.IsSecure = IsSecure;
        }
}
 
public enum MessageSenderTransport
{
        Email,
        Phone,
        Sms
}
 
Strongly Typed Metadata 를 위한 Attribute 클래스를 완성하였으면, 이것을 그대로 ExportAttribute 과 함께 추가적으로 선언하면 됩니다.
 
그리고 ExportAttribute 을 선언한 코드에 위의 Attribute 특성을 부여합니다. 아래는 그 소스 코드의 일부입니다.
 
[Export(typeof(IMessageSender))]
[MessageSenderType(MessageSenderTransport.Email)]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
[Export(typeof(IMessageSender))]
[MessageSenderType(MessageSenderTransport.Phone, true)]
public class PhoneMessageSneder : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import PhoneMessageSneder");
        }
}
 
[Export(typeof(IMessageSender))]
[MessageSenderType(Transport=MessageSenderTransport.Sms, IsSecure=true)]
public class SmsMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import SmsMessageSender");
        }
}
 
그렇다면 아래와 같이 Metadata 를 Strongly Typed 으로 질의(Query) 할 수 있게 됩니다.
 
아래의 소스 코드는 지난 포스트의 소스 전체 소스 코드를 참고하십시오.
 
foreach (var export in program.Sender)
{
        if ((MessageSenderTransport)export.Metadata["Transport"] == MessageSenderTransport.Sms)
        {
               export.GetExportedObject().Say();
 
               if ((bool)export.Metadata["IsSecure"] == true)
               {
                       Console.WriteLine("Security message");
               }
        }
}
 
위의 소스 코드 실행 결과는 원하던 결과대로 다음과 같습니다.
 

[그림1] Strongly Typed 질의 결과
 
 
하지만 아직도 문제는 남아 있는 것 같아 보이네요. Strongly Typed 으로 Export Metadata 를 선언하였지만 여전히 질의(Query) 과정은 똑같은 문제점을 가지고 있습니다.
 
l 메타데이터의 질의(Query) 과정의 타입 불안정
l 빌드 시 오류를 해결이 어려움
l 사용상 모든 키값을 외우기 어렵고, 오타 발생 위험
 
 
More Strongly Typed Metadata Query
 
Metadata 를 질의(Query) 하기 위해 MEF 는 보다 강력하게 Import 하는 방법을 제공해 줍니다. 확장된 MetadataAttribute 에 인터페이스(Interface) 를 구현하도록 하여 Import 시에 인터페이스(Interface) 를 통한 Metadata 를 질의(Query) 하는 방법입니다.
 
우선 Attribute 에 사용되는 프로퍼티를 인터페이스로 선언합니다.
 
public interface IMessageSenderTypeAttribute
{
        bool IsSecure { get; }
        MessageSenderTransport Transport { get; }
}
 
단, 여기에서 반드시 Getter 만 선언하셔야 합니다. Setter 를 선언하시면 MEF 는 Setter 프로퍼티로 인해 유효하지 않는 Attribute 으로 인식하여 예외를 발생하게 됩니다.
 
아래의 소스 코드는 Metadata Attribute 클래스에 위의 인터페이스를 구현한 코드 입니다.
 
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MessageSenderTypeAttribute : Attribute, IMessageSenderTypeAttribute
{
        public MessageSenderTransport Transport { get; set; }
        public bool IsSecure { get; set; }
 
        public MessageSenderTypeAttribute() { }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport)
               : this(transport, false)
        {
        }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport, bool isSecure)
        {
               this.Transport = transport;
               this.IsSecure = IsSecure;
        }
}
 
 
이제 Export 의 MetadataView 로 질의할 수 있도록 MetadataView 의 인터페이스를 알려주도록 해야 합니다.
 
[Import(typeof(IMessageSender))]
ExportCollection<IMessageSender, IMessageSenderTypeAttribute> Sender { get; set; }
 
아래의 소스 코드는 MetadataView 를 통해 Strongly Typed 으로 Export Metadata 를 질의(Query) 하는 소스 코드 입니다.
 
[Import(typeof(IMessageSender))]
ExportCollection<IMessageSender, IMessageSenderTypeAttribute> Sender { get; set; }
                                             
static void Main(string[] args)
{
        Program program = new Program();
        program.Run();
 
        foreach (var export in program.Sender)
        {
               if (export.MetadataView.Transport == MessageSenderTransport.Email)
               {
                       export.GetExportedObject().Say();
               }
        }
}
 


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

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

[MEF] 10. Querying the CompositionContainer  (0) 2009.05.18
[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
[MEF] 6. Lazy Exports  (0) 2009.04.13
[MEF] 5. Catalog 사용  (0) 2009.04.09
Exports and Metadata
 
Export 에 Metadata 를 등록하고 제어하는 방법입니다. 지난 포스트에서 알 수 있듯이 Export 는 구성 요소간에 Contact 를 제공하여 이들을 구성(Composition) 할 수 있는 플러그인 모델(Plugin Model) 을 제공해 줍니다.
 
하지만 Contract 가 제공되어 구성 요소를 확장하고 구성하는 것은 매우 용이하다는 것을 알게 되었으나 Contract 로 인해 파생된 다양한 구성 요소를 어떻게 제어하느냐의 고민을 하게 됩니다. 즉, 다양한 구성 요소 가운데 내가 필요로 하는 구성 요소를 골라낼 수 있도록 Metadata 를 제공하여 구성 요소를 질의(Query)할 수 있도록 하는 것입니다.
 

예를 들어 아래와 같은 Export 구성 요소 중 어떻게 EmailMessageSender 로 Say() 를 호출할 것인가가 문제인 것이죠.

[Export(typeof(IMessageSender))]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
[Export(typeof(IMessageSender))]
public class PhoneMessageSneder : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import PhoneMessageSneder");
        }
}
 
[Export(typeof(IMessageSender))]
public class SmsMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import SmsMessageSender");
        }
}
 
이런 경우, 원초적인 방법으로 리플랙션(Reflection) 을 이용하여 Type 검사를 통해 EmailMessageSender 를 골라내서 사용하면 되지만, 직접적으로 Type 을 검사하기 위해서는 Tightly Coupling 이 발생하여 결국 유연한 플러그인 모델을 구현하기 위해 아무런 도움이 되지 않습니다.
 
이러한 방법을 해소하기 위해 또 다른 우회 방법은 리플랙션을 통해 Modules Name 으로 비교하는 방법이지만, 이것 또한 플러그인 모델에서 구성 요소가 교체 되었을 경우를 생각하면 전혀 대응할 수 없는 방법입니다.
 
MEF 에서는 이런 문제를 해소하기 위해 Contract 에 Metadata 를 제공하며 정적/동적인 방법을 제공해 줍니다.
 
 
Attaching Metadata to an Export
 
Export 에 Metadata 를 제공해 주기 위해서 MEF 에서는 ExportMetadataAttribute 특성을 제공해 줍니다.
 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Field,
                    AllowMultiple = true, Inherited = false)]
public ExportMetadataAttribute(string name, object value)
 
왜 ExportMetadata 클래스는 sealed 로 선언이 되었나요?
 
일반적으로 sealed 로 클래스를 봉인할 경우 리플랙션의 성능이 향상됩니다.
 
 
ExportMetadata 는 키와 값(Key/Value) 을 지정하여 Contract 에 Metadata 를 제공해 줄 수 있습니다. 그리고 하나의 Export 에 여러 개의 Metadata 를 제공할 수 있도록 AllowMultiple 을 지원합니다.
 
Metadata 는 여러 가지의 정보를 포함할 수 있습니다. Export 가 제공하는 기능의 특성을 기술할 수 있으며, 예를 들어, 권한, 로깅, 구성 요소 분류 방법 등이 될 수 있을 것입니다.
 
아래의 소스 코드는 Metadata 를 지정하는 예를 보여줍니다.
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Email")]
[ExportMetadata("Logging", true)]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Phone")]
public class PhoneMessageSneder : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import PhoneMessageSneder");
        }
}
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Sms")]
public class SmsMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import SmsMessageSender");
        }
}
 
 
 
Constraining Imports statically
 
MEF Preview 5 에서는 ImportRequiredMetadataAttribute 클래스가 제거되었습니다.
 
MEF Preview 4 에서는 선언적인 방법으로 ImportRequiredMetadataAttribute 를 통해 Metadata 를 질의할 수 있었으나, MEF Preview 5 에서는 ImportRequiredMetadataAttribute 클래스가 제거되었습니다.
 
아마도 추측으로는 ImportRequiredMetadataAttribute 를 선언 시에 여러 개의 구성 요소가 검색될 경우 Exception 이 발생하는데, Exception 을 최소화 하고자 제거가 된 것 같습니다.
 
혹시 Statically 한 방법으로 ImportRequiredMetadataAttribute 에 대응되는 클래스를 아시면 저에게 알려주세요.
 
 
Constraining Imports dynamically
 
이 방법은 Export 의 ExportMetadata 를 런타임 시에 질의(Query) 하는 방법입니다.
 
Import 시 ExportCollection<T> 을 사용하여 Export 를 수동적으로 질의(Query) 하는 방법입니다. 이 방법은 지난 포스트의 Lazy Load 를 이용한 방법으로 단지 Metadata 만 질의(Query) 뿐이고, 객체의 생성에 대한 판단은 필요 시에만 GetExportedobject() 메서드를 이용하여 생성할 수 있습니다.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
 
namespace MetadataSample
{
        class Program
        {
               [Import(typeof(IMessageSender))]
               ExportCollection<IMessageSender> Sender { get; set; }
 
               static void Main(string[] args)
               {
                       Program program = new Program();
                       program.Run();
 
                       foreach (var export in program.Sender)
                       {
                              if ((string)export.Metadata["SenderType"] == "Email")
                                      export.GetExportedObject().Say();
 
                              if (export.Metadata.ContainsKey("Logging") &&
                                      (bool)export.Metadata["Logging"] == true)
                                      Console.WriteLine("Logged success");
                       }
               }
 
               void Run()
               {
                       var catalog = new AggregateCatalog(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
 
                       var container = new CompositionContainer(catalog);
                       var batch = new CompositionBatch();
                       batch.AddPart(this);
                       container.Compose(batch);
               }
 
               public interface IMessageSender
               {
                       void Say();
               }
 
               [Export(typeof(IMessageSender))]
               [ExportMetadata("SenderType", "Email")]
               [ExportMetadata("Logging", true)]
               public class EmailMessageSender : IMessageSender
               {
                       public void Say()
                       {
                              Console.WriteLine("Import EmailMessageSender");
                       }
               }
 
               [Export(typeof(IMessageSender))]
               [ExportMetadata("SenderType", "Phone")]
               public class PhoneMessageSneder : IMessageSender
               {
                       public void Say()
                       {
                              Console.WriteLine("Import PhoneMessageSneder");
                       }
               }
 
               [Export(typeof(IMessageSender))]
               [ExportMetadata("SenderType", "Sms")]
               public class SmsMessageSender : IMessageSender
               {
                       public void Say()
                       {
                              Console.WriteLine("Import SmsMessageSender");
                       }
               }
        }
}
 
 
실행 결과는 예상할 수 있듯이 아래와 같이 런타임 시에 결과를 보여줍니다.


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

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

[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
[MEF] 6. Lazy Exports  (0) 2009.04.13
[MEF] 5. Catalog 사용  (0) 2009.04.09
[MEF] 4. Import 선언  (0) 2009.04.07

[MEF] 5. Catalog 사용

Managed Extensibility Framework 2009.04.09 21:31 Posted by POWERUMC
Catalog
 
Catalog 는 동적으로 Composable Part 를 찾아 Container 에 등록합니다. Composable Part 는 ExportAttribute 으로 Contract 를 선언할 수 있는데 개별적으로 일일이 Composable Part 를 등록하는 것은 너무나 큰 반복 작업이 될 수 있지만, MEF 의 Catalog 로 쉽게 자동적으로 등록을 할 수 있습니다.
 
Catalog 를 사용하지 않는 Composable Part 의 등록는 매우 고단한 작업입니다. 아래의 예제 소스 코드는 수동으로 Composable Part 를 등록하는 방법입니다.
 
 
예제에서는 단지 하나의 Export 를 등록하였지만 실제로 이러한 Composable Part 는 수십에서 수백개를 넘을 수 도 있습니다. 많은 Composable Part 를 동적이고 또는 자동적으로 등록해 주기 위해서 Catalog 를 사용하면 쉽게 해결할 수 있습니다.
 
 
이 외에도 Composable Part 를 Catalog 에 등록하는 여러 가지 방법이 있습니다. 아래는 MEF 에서 지원하는 Catalog 입니다.
 
 
Assembly Catalog
 
Assembly Catalog 는 닷넷 어셈블리를 Catalog 로 사용할 수 있습니다.
 
public AssemblyCatalog(string codeBase)
public AssemblyCatalog(Assembly assembly)
 
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
 
AssemblyCatalog 의 시그너처(Signature) 에서 보듯이 CodeBase 로 Catalog 를 사용할 수 있지만, 실버라이트에서는 CodeBase 를 지원하지 않으므로 실버라이트에서는 CodeBase 로 AssemblyCatalog 를 사용할 수 없습니다.
 
 
Directory Catalog
 
Directory Catalog 를 사용하면 특정 폴더의 어셈블리를 모두 검색하여 Composable Part 를 사용할 수 있습니다.
 
public DirectoryCatalog(string path)
public DirectoryCatalog(string path, string searchPattern)
 
Directory Catalog 는 내부적으로 System.IO 의 Directory.GetFiles() 메서드를 사용하여 지정한 폴더의 모든 어셈블리를 검색하도록 하여 Composable Part 를 가져오도록 합니다.
 
var catalog = new DirectoryCatalog("Addins");
 
기본적으로 *.DLL 확장자의 어셈블리만 검색하도록 설정되어있고, 어셈블리 파일의 검색 패턴을 지정하여 검색할 수 있습니다.
 
var catalog = new DirectoryCatalog("Addins", "*.exe");
 
아마 MEF 의 예제중에 XFileExplorer 처럼 특정 폴더에 어셈블리를 복사해 넣으면 자동으로 Composable Part 가 로드되는 예제가 있는데, 이것은 DirectoryCatalog 가 자동으로 처리해 주는 것이 아니라 FileSystemWatcher 클래스를 이용하여 직접 구현해야 합니다.
 
FileSystemWatcher 로 파일의 변경이 감지되면 DirectoryCatalog 의 Refresh() 메서드를 이용하여 폴더를 재검색 또는 새로고침을 할 수 있습니다.
 
 
Aggregation Catalog
 
Aggregation Catalog 는 복합적인 Catalog 를 사용할 수 있도록 합니다. 만약 Assembly Catalog 와 Directory Catalog 등을 동시에 사용하여 Composable Part 를 가져오도록 하려면 Aggregation Catalog 를 사용하면 됩니다.
 
public AggregateCatalog(params ComposablePartCatalog[] catalogs)
 
Aggregation Catalog 의 생성자는 params 로 인자를 받을 수 있습니다.
 
var catalog = new AggregateCatalog(   new AssemblyCatalog(Assembly.GetExecutingAssembly()),
                                                                            new DirectoryCatalog("."));
 
 
Type Catalog
 
Type Catalog 는 Composable Part 를 Type 으로 개별적으로 사용하도록 합니다. 가장 단순하고 무식(?)한 방법일 수도 있지만 세세한 제어가 필요할 때 사용 가능한 Catalog 일 것 같습니다.
 
public TypeCatalog(params Type[] types)
public TypeCatalog(IEnumerable<Type> types)
 
var catalog = new TypeCatalog(typeof(EMailMessageSender), typeof(PhoneMessageSender));
 
 
Using catalog with a Container
 
이제 Catalog 를 Composable Container 에서 Composition 하도록 넘겨주는 일만 남았습니다.
 
public CompositionContainer()
public CompositionContainer(params ExportProvider[] providers)
public CompositionContainer(ComposablePartCatalog catalog, params ExportProvider[] providers)
 
var container = new CompositionContainer(catalog);
 
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

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

[MEF] 7. Exports and Metadata  (0) 2009.04.16
[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

[MEF] 3. Export 선언

Managed Extensibility Framework 2009.03.29 21:03 Posted by POWERUMC
Exports 선언
 
MEF 는 Export 를 통해 외부로 구성요소를 노출할 수 있습니다. Export 는 System.ComponentModel.Composition.ExportAttribute 특성을 통해 선언합니다. 이 특성은 클래스 뿐만 아니라 프로퍼티와 메서드에도 선언을 할 수 있습니다.
 
 
구성요소 Export 하기
 
ExportAttribute 특성을 사용하여 아래와 같이 구성요소를 외부로 노출하게 됩니다. ExportAttribute 은 몇 가지의 시그너처(Signature) 를 제공하는데 매개변수를 생략하게 될 경우 MEF 은 클래스의 타입으로 Contract 를 매핑하게 됩니다.
 
[Export]
class MessageSender
{
 
}
 
 
프로퍼티 Export
 
프로퍼티를 Export 하는 방법입니다. 프로퍼티를 Export 할 수 있게 되어 여러 가지 면에서 유리할 수 있습니다.
 
Core CLR 이 제공하는 타입(Type) 뿐만 아니라 외부의 다양한 타입(Type) 을 사용할 수 있습니다. 프로퍼티에 Export 를 선언할 수 있음으로써 Export 를 구조적으로 분리하여 단순화 할 수 있습니다. 그러므로 같은 구성요소 내에서 Export 간의 Related 관계를 가질 수 있습니다.
 
아래의 코드와 같이 Timeout 프로퍼티는 Contract 를 맺게 됩니다.
 
public class Configuration
{
   [Export("Timeout")]
   public int Timeout
   {
       get { return int.Parse(ConfigurationManager.AppSettings["Timeout"]); }
   }
}
 
[Export]
public class UsesTimeout
{
   [Import("Timeout")]
   public int Timeout { get; set; }
}
 
 
메서드 Export
 
구성요소의 메서드를 Export 할 수 있습니다. 메서드의 Export 는 기본적으로 대리자(Delegate) 를 통해 호출하게 됩니다. 메서드를 Export 하게되면 보다 더 세세한 제어를 가능하게 하고, 심플하게 방법으로 Code Generating 이 가능합니다.
 
public class MessageSender
{
   [Export(typeof(Action<string>))]
   public void Say(string message)
   {
       Console.WriteLine(message);
   }
}
 
[Export]
public class MessageProcess
{
   [Import(typeof(Action<string>))]
   public Action<string> MessageSender { get; set; }
 
   public void Send()
   {
       MessageSender("Call send process in MessageProcess");
   }
}
 
그리고 ExportAttribute 은 타입(Type) 대신 문자열을 사용하여 Contract 를 사용할 수 있습니다.
 
public class MessageSender
{
   [Export("MessageSender")]
   public void Say(string message)
   {
       Console.WriteLine(message);
   }
}
 
[Export]
public class MessageProcess
{
   [Import("MessageSender")]
   public Action<string> MessageSender { get; set; }
 
   public void Send()
   {
       MessageSender("Call send process in MessageProcess");
   }
}
 
 
아래의 소스 코드는 이번 예제에서 사용된 전체 소스 코드입니다.
namespace ExportSample
{
   class Program
   {
       [Import]
       MessageProcess MessageProcess { get; set; }
 
       [STAThread]
       static void Main(string[] args)
       {
             Program p = new Program();
             p.Run();
 
             p.MessageProcess.Send();
       }
 
       private void Run()
       {
             var catalog = new AggregateCatalog();
             catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
 
             var container = new CompositionContainer(catalog);
             var batch = new CompositionBatch();
             batch.AddPart(this);
             container.Compose(batch);
       }
   }
 
   public class MessageSender
   {
       [Export("MessageSender")]
       public void Say(string message)
       {
           Console.WriteLine(message);
       }
   }
 
   [Export]
   public class MessageProcess
   {
       [Import("MessageSender")]
       public Action<string> MessageSender { get; set; }
 
       public void Send()
       {
             MessageSender("Call send process in MessageProcess");
       }
   }
}
 
 
Export 요약
 
이렇게 ExportAttribute 을 사용하여 Contract 를 제공하는 것은 굉장히 중요한 의미를 가지게 됩니다. 플러그인 모델(Plugin Model) 에서 Export 는 구성요소를 외부로 노출하는, 즉 Contract 의 방법을 제공해 주게 됩니다.
 
http://blog.powerumc.kr/upload/Image/NET/NET-Framework/MEF1/capture1.jpg
 
Contract 맺음으로써 개발자는 Contract Base 로 단지 Contract 만 제공받으면 됩니다. 이러한 Contract 는 제한된 상호작용을 극복하여 대부분의 커플링(Coupling)을 해소할 수 있으며, 플로그인 모델(Plugin Model) 에서 보다 쉽게 구성요소를 캡슐화 할 수 있습니다.
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

'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
[MEF] 1. Managed Extensibility Framework 이란?  (2) 2009.03.16
시작하기 전에
 
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) 를 활용하게 되면 복잡한 기능을 획기적으로 단순화 할 수 있습니다.
 
 
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

'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
[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 의 플러그인 모델은 충분히 활용 가치가 있지만 아직은 실무에서 즉시 도입하기엔 많은 고민이 따를 것 같습니다. 그리고 이런 애로 사항이 아마로 정식 버전에서는 해결되리라 생각합니다. 시간이 되면 이 부분도 한번 다루어 보도록 하겠습니다.
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

'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
[MEF] 1. Managed Extensibility Framework 이란?  (2) 2009.03.16
Task Parallel Library
 
Parallel Extension 은 PLINQ 와 더불어 확장 가능한 Task Parallel Library 를 제공합니다. Task Parallel Library 는 PLINQ 를 이용하지 않고 개별적이고 수동적인 병렬 처리 작업을 위해 사용할 수 있습니다.
 
Task Parallel Library 는 크게 세 가지 방법으로 병렬 처리를 위한 Library 를 제공합니다.
 
Loops
 
 
 

 

[그림1] Parallel.For 를 이용한 병렬 처리
 
 


[그림2] Parallel.Foreach 를 이용한 병렬 처리
 
Task Parallel Extension 으로 병렬 처리를 쉽게 처리할 수 있으며, 병렬 처리로 인자값을 넘기거나 하는 작업을 쉽게 할 수 있습니다.
 
Statements
 

 


[그림3] Parallel.Invoke 를 이용한 병렬 처리
 
 
Task
 
특히 Parallel Extension Library 에서 Task 는 수동적으로 병렬 처리를 하기 위해 다양한 기능을 지원합니다. 정교하게 스레드(Thread) 를 처리했던 것에 비하면 심플하고도 직관적으로 병렬 작업을 처리할 수 있습니다.
 
Task 는 보다 정교하게 병렬 처리 작업을 할 수 있습니다.
l 대기
l 취소
l 연장
l 상하(부모/자식) 간의 관계
l 디버그 지원
 
아래는 ThreadPool.QueueUserWorkItem 처럼 바로 작업을 시작하도록 합니다.
 

Task.StartNew(…);

 
아래는 Task 에 대해 대기 및 취소 작업을 진행하도록 합니다.
 

Task t1 = Task.StartNew(…);
t1.Wait();
t1.Cancel();
Task t2 = t1.ContinueWith(…);

 
아래는 작업에 대해 지속적인 병렬 처리를 가능하도록 합니다.
 

var p = Task.StartNew(() => {
    var c = Task.StartNew(…);
}

 
아래는 특정 작업의 결과를 받아 올 수 있습니다.
 

var p =
 Future.StartNew(() => C());
int result = p.Value;
 

 
 
Coordination Data Structures
 
병렬 처리 작업은 PLINQ 와 TPL(Task Parallel Library) 를 지원하기 위해 기존의 데이터 컬렉션 등이 등장하였습니다. 내부적으로 동기화를 지원하지 않았던 문제들을 지원하게 되었고, 특히 오늘날 멀티 코어(Multi Core) 프로세스를 위해 많은 동기적인 문제를 고민해야 했습니다. .NET Framework 4.0 은 이러한 공통적인 문제들을 해결하기 할 수 있습니다.
 
l Thread-safe collections
       ConcurrentStack<T>
       ConcurrentQueue<T>
       ConcurrentDictionary<TKey,TValue>
      
l Work exchange
       BlockingCollection<T>
       IProducerConsumerCollection<T>
l Phased Operation
       CountdownEvent
       Barrier
l Locks
       ManualResetEventSlim
       SemaphoreSlim
       SpinLock
       SpinWait
l Initialization
       LazyInit<T>
       WriteOnce<T>
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

지난 포스트에서 Parallel Extension 과 LINQ 를 이용한 PLINQ 에 대해서 살펴보았습니다. 지난번에 얘기했듯이 Manual Parallelism 는 Parallel Extension 의 성능을 절대 따라올 수 없다고 하였습니다. 왜냐하면, Parallel Extension 은 Manual Parallelism 의 병렬 처리 방식보다 더 복잡하고 정밀한 병렬 처리 알고리즘으로 처리하기 때문입니다.
 
Parallel Extension 이란?
 
Parallel Extension 은 전혀 새로운 것이 아닙니다. C# 3.0 의 LINQ 는 LINQ 쿼리식을 제공하기 위해 새로운 컴파일러(Compiler) 가 필요했습니다. 정확히 말하자면 C# 의 Language Service 의 버전이 업그레이드 되었고, LINQ 쿼리식을 편하게 쓸 수 있도록 Visual Studio 2008 을 사용해야 했습니다. 다시 말하자면, LINQ 쿼리식이 아닌 확장 메서드(Extension Methods) 만으로 쿼리가 가능했다는 것이 이것을 증명해 줍니다. 확장 메서드(Extension Methods) 는 결국 IL 레벨에서는 정적(Static) 인 인스턴스(Instance) 에 불과하니까요.
 
Parallel Extension 은 새로운 컴파일러(Compiler) 가 필요하지 않습니다. .NET 의 기본적인 코어(Core) 인 mscorelib.dll, System.dll, System.Core.dll 만을 사용하여 구현이 되었습니다. 그리고, 기존의 ThreadPool 을 개선하였고, LINQ 와 통합하여 선언적으로 Parallel Extension 을 사용할 수도 있게 되었죠.
 
Task Parallel Library 를 통해 데이터 처리와 어떤 작업(Task)에 대해서도 병렬 처리도 가능해 졌습니다. 이제는 데이터의 병렬 처리뿐만 아니라, 작업(Task) 단위로서도 Task Parallel Library 로 병렬 처리가 가능합니다.
 
이제는 병렬 처리가 된다는 것이 중요한 게 아니라, 병렬 처리의 내부적인 예외 핸들링이나 Visual Studio 에서 디버깅(Debugging) 이 가능합니다. 이러한 새로운 매커니즘으로 내부적으로는 새로운 예외 핸들링 모델(Exception Handling Model)이 필요했습니다.
 
또한 .NET Framework 4.0 의 Parallel Extension 은 다양한 언어를 지원합니다. C#, VB.NET, C++, F# 그리고 .NET 컴파일러(Compiler) 로 컴파일(Compile) 되는 언어라면 상관없습니다. RoR/PHP 라도 .NET 컴파일러(Compiler)에 의해 컴파일(Compile) 된다면 Parallel Extension 을 사용하는데 전혀 문제가 없습니다.
 
 
Parallel Extension 아키텍처
 
 
[그림1] Parallel Extension 아키텍처 (클릭하면 확대)
 
Parallel Extension 의 병렬 처리는 .NET 컴파일러(Compiler) 로 컴파일(Compile) 되는 어떤 언어든 차별을 두지 않고, 병렬 처리 기능을 사용할 수 있습니다. PLINQ 로 작성된 쿼리(Query)는 별도의 PLINQ Provider 의 엔진(Engine) 에서 쿼리를 분석하는 과정을 거치게 됩니다. 쿼리 분석(Query Analysis) 에 의해 선언된 LINQ 쿼리식을 분석하여 최적의 알고리즘으로 각각의 작업을 배치하게 됩니다.
 

[그림2] Parallel Extension 작업 분할
 
각각 분배되는 작업은 쓰레드 큐(Thread Queue) 에 쌓이고, 이 큐에 쌓이는 작업(Task) 는 실제 작업자 쓰레드(Worker Thread) 에 할당이 됩니다. 하지만, Parallel Extension 은 단지 쓰레드에 할당하는 것으로 작업이 마치기를 기다리지 않습니다. 만약, 작업을 분배하는 것은 Manual Parallel 과 크게 다르지 않기 때문이죠.
 

[그림3] Parallel Extension 작업 분할
 
Parallel Extension Library 는 병렬 처리의 작업을 지속적으로 최적의 상태를 감시합니다. 예를 들어, A 의 작업이 Task 1, Task 2, Task 3 인데, B 의 작업은 모두 마친 상태라고 할 때, Parallel Extension Library 는 A 의 작업을 놀고 있는 B 에게 또 다시 분배합니다. 이러한 반복적으로 병렬 처리의 작업이 최적화 될 수 있도록 하여, 병렬 처리의 성능을 극한으로 끌어올립니다.
 
LINQ 만 알면 난 병렬 처리 개발자
 
Parallel Extension 은 LINQ 와 통합하기 위한 프로바이더(Provider) 를 제공합니다. 아직 LINQ 잘 모르신다구요? 30분만 투자하시면 LINQ 를 사용하는데 큰 지장이 없습니다. 그리고 그만큼 쉽습니다. LINQ 를 이해하기 위해 제네릭(Generic), 확장 메서드(Extension Methods), 익명 타입(Anonymous Methods) 도 함께 공부하시면 좋습니다.
 
예전에 이런 광고도 있었죠.
“비트 박스를 잘하려면?” “북치기, 박치기만 잘하면 됩니다”
 
“그럼 PLINQ 개발자가 되기 위해서는?” “AsParallel() 만 잘하면 됩니다.”
 
맞습니다. Parallel Extension Library 의 AsParallel() 확장 메서드(Extension Methods) 만 알면 당신도 이제 병렬 처리 개발자입니다. 이전 포스트의 PLINQ 예제에서 처럼 AsParallel() 만 붙이면 그것을 PLINQ 라고 부릅니다^^ (병렬 처리를 위한 확장 메서드와 옵션은 더 많이 존재합니다 )
 
아래는 AsParallel() 의 예 입니다.
private static void ParallelSort(List<Person> people)
{
       var resultPerson = from person in people.AsParallel()
                                    where person.Age >= 50
                                    orderby person.Age ascending
                                    select person;
 
       foreach (var item in resultPeople) { }
}
 
하지만, 무조건적인 병렬 처리는 오히려 성능을 저하시킬 수 있습니다. 특히 PLINQ 를 사용하는 병렬 처리는 .NET Framework 내부적으로 쿼리 분석(Query Analysis) 과정을 거치게 됩니다. 각각 프로세서(Processor) 에 분배된 데이터는 또 분배되고, 최적화가 가능할 때까지 계속적으로 분배됩니다. 마치 세포 분할이 일어나는 것처럼 말이죠.
 
최소한 병렬 처리를 위해서 데이터에 대한 이해와 추측이 가능해야 합니다.
 
1.     추측 가능한 데이터의 양
2.     추측 가능한 데이터의 내용
3.     추측 가능한 데이터 처리 시간
 
이러한 최소한의 예측 작업을 하지 않는다면, 오히려 PLINQ 를 이용할 때 성능은 저하될 수 있습니다. 예를 들어, 평균 데이터의 양이 2개라고 가정한다면, PLINQ 의 쿼리 분석(Query Analysis) 작업은 오히려 성능 저하의 요인이 됩니다. PLINQ 쿼리 분석(Query Analysis) 에 의해 두 번째 프로세서(Processor) 의 사용량이 많다고 판단된다면, 병렬 작업은 의미가 없어지고 오히려 성능을 저하시킬 테니까요. ‘쿼리 분석(Query Analysis) 작업이 눈 깜빡 거리는 시간(1/40(0.025)초) 이라고 가정한다면, 만 건의 쿼리 분석(Query Analysis) 작업 시간은 250초’가 될 테니까요.
 
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
최근 대부분의 사용자들의 컴퓨터의 사양이 코어2 로 업그레이드 되고 있습니다. CPU 제품에 따라 코어에 대한 아키텍처가 다르지만, 기본적으로 이들 제품은 하나의 컴퓨터에 CPU 가 두 개인 제품들입니다. 인간과 비교하자면 뇌가 두 개인 사람인데 그다지 상상해서 떠올리고 싶지 않네요^^.
 
컴퓨터는 CPU 두 개를 보다 효율적으로 이용하기 위해 바로 Parallelism Processing(병렬 처리)를 하게 됩니다. 하나의 CPU 의 성능을 향상시키는 방법이 아닌, 두 개의 CPU 에게 작업을 할당함으로써 데이터의 처리 성능을 극대화 시키게 됩니다. 우리에게 익숙한 운영체제인 윈도우(Windows) 의 멀티 쓰레딩(Multi Threading) 을 생각하면 병렬 처리(Parallelism Processing) 는 그렇게 어려운 개념은 아닙니다.
 
[그림1] 어쨌든 뇌가 두 개 (여기에서 참조)
 
원래 오픈 소스 프로젝트로 Parallel Extension 프로젝트를 CodePlex 에서 본 기억이 있는데, 지금은 링크의 주소를 찾을 수 가 없네요. 구글을 통해 “Parallel Extension” 을 검색하시면, .NET 에서의 Parallel Programming 의 흔적을 찾아볼 수 있습니다.
 
우선 아래의 Person 클래스를 작성하여 테스트에 사용할 것입니다.
 
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
 
 
General~
 
코어(Core) 하나로 작업할 경우, 개발자는 아무것도 염려 하지 않아도 됩니다. 그 동안 우리가 배웠던 대로 코드를 작성하기만 하면 됩니다. 병렬 처리에 대한 고민을 하지 않고 개발한 코드라면 모두 이 범주에 속하겠네요. 이러한 방법은 가장 보편적으로 작성할 수 있습니다.
 
private static void GeneralSort(List<Person> people)
{
       List<Person> resultPeople = new List<Person>();
       foreach (Person person in people)
       {
             if (person.Age >= 50)
                    resultPeople.Add(person);
       }
 
       resultPeople.Sort((p1, p2) => p1.Age.CompareTo(p2.Age));
 
       foreach (var item in resultPeople) { }
}
 
List<Person> 개체를 파라메터로 넘겨주고, Person 중에 Age 가 50이 넘는 개체를 정렬하는 코드입니다.
바로 이 코드를 병렬 처리를 하고자 합니다. 이 코드를 병렬 처리를 하고자 한다면 코드의 양은 훨씬 늘어나고, 복잡한 처리를 해야 합니다.
 
 
Manual Parallelism
 
일반적으로 데이터의 처리를 병렬 처리로 전환하기 위해서는 쓰레드(Thread) 를 사용합니다. 쓰레드(Thread) 가 생성이 되면 커널 또는 물리적인 프로세서에 의해 의해 유휴 상태 또는 처리가 가능한 코어(Core) 로 작업이 할당되어 다중 작업(Multi Process) 을 가능하게 됩니다.
 
이러한 방법의 병렬 처리는 프로세서(Processor) 개수만큼 쓰레드(Thread) 를 생성하여 비동기 작업을 합니다.
 
private static void ThreadSort(List<Person> people)
{
       var resultPeople = new List<Person>();
       int partitionsCount = Environment.ProcessorCount;
       int remainingCount = partitionsCount;
       var enumerator = (IEnumerator<Person>)people.GetEnumerator();
       try
       {
             using (var done = new ManualResetEvent(false))
             {
                    for (int i = 0; i < partitionsCount; i++)
                    {
                           ThreadPool.QueueUserWorkItem(delegate
                           {
                                 var partialResults = new List<Person>();
                                 while (true)
                                 {
                                        Person baby;
                                        lock (enumerator)
                                        {
                                              if (!enumerator.MoveNext()) break;
                                              baby = enumerator.Current;
                                        }
                                        if (baby.Age >= 50)
                                        {
                                              partialResults.Add(baby);
                                        }
                                 }
                                 lock (resultPeople) resultPeople.AddRange(partialResults);
                                 if (Interlocked.Decrement(ref remainingCount) == 0) done.Set();
                           });
                    }
                    done.WaitOne();
                    resultPeople.Sort((p1, p2) => p1.Age.CompareTo(p2.Age));
             }
       }
       finally
       {
             if (enumerator is IDisposable) ((IDisposable)enumerator).Dispose();
       }
 
       foreach (var item in resultPeople) { }
}
 
중요한 부분은 추출된 데이터의 정렬(Sort) 작업입니다. 이 작업을 하기 위해서는 모든 쓰레드(Thread) 의 작업이 끝나야 합니다. 만약 모든 쓰레드(Thread) 가 종료되지 않은 시점에서 정렬 작업을 하게 되면, 과연 정렬된 데이터를 신뢰할 수 있을까요?? ( 왜 그런지는 여러분의 상상에 맡기도록 합니다. )
 
정렬 작업을 하기 전 ManualResetEvent 의 WaitOne() 메서드를 호출하여 모든 쓰레드(Thread) 의 WaitHandle 이 작업이 신호를 받을 때까지(동기화 작업) 기다려야 합니다. 예를 들어, 두 개의 쓰레드(Thread) 가 생성 되고 첫 번째 쓰레드는 이미 작업을 종료하였지만, 두 번째 쓰레드는 아직 작업이 완료되지 않았다면, 작업을 마친 모든 쓰레드(Thread) 는 가장 늦게 처리가 완료되는 쓰레드를 기다려야 정렬 작업을 진행할 수 있습니다.
 
마지막으로, 위의 코드의 병렬 처리 작업은 성능에 한계가 있습니다. 프로세서(Processor) 개수만큼 쓰레드(Thread) 를 생성하여 작업을 분배하는 방식이기 때문에, 병렬 처리 작업의 성능은 곧 프로세서(Processor) 개수가 될테니까요!
 
 
Parallel Extension
 
C# 4.0 은 병렬 처리를 하기 위해 코드의 양을 획기적으로 줄일 수 있습니다.
 
private static void ParallelSort(List<Person> people)
{
       var resultPerson = from person in people.AsParallel()
                                    where person.Age >= 50
                                    orderby person.Age ascending
                                    select person;
 
       foreach (var item in resultPeople) { }
}
 
LINQ 식을 사용하여 데이터 처리와 정렬 작업을 간단하게 할 수 있습니다. 감격이네요^^ 바로, .NET Framework 4.0 의 Parallel Extension 을 사용하여 LINQ 처럼 사용하는 것을 PLINQ 라고 합니다.
 
Q : foreach (var item in resultPeople) { } 코드를넣었나요?
 
A: 동일한 테스트를 하기 위함입니다. LINQ 식은 내부 구조의 특성상 “쿼리식”에 불과합니다.
보다 자세한 내용은 필자의 블로그를 참고하세요.
 
Parallel Extension 은 Manual Parallelism 보다 더 복잡하고 좋은 성능을 낼 수 있는 알고리즘으로 구현이 되어 있습니다. 그렇기 때문에 아무리 많은 코어를 가진 컴퓨터에서 동일한 테스트를 한다고 하여도 결코 Manual Parallelism 은 Parallel Extension 의 병렬 처리 성능을 기대할 수 없습니다.
 
이제 살며시 그 내부 구조도 궁금해 집니다. (다음에 계속…)
저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License


 

티스토리 툴바