프리즘 4.1 도움말 번역입니다




진보된 MVVM 시나리오들



이전 챕터에서는, 응용프로그램 유저 인터페이스( UI ), 프리젠테이션 로직, 비즈니스 로직을 세 개의 분리된 클래스( 뷰, 뷰 모델, 모델 )로 분리하고, 그 클래스들 간의 상호작용을( 데이터 바인딩, 커맨드, 데이터 유효화 인터페이스를 통해서 ) 구현하고, 생성과 엮기( wire-up )를 다루기 위한 전략을 구현함으로써 모델-뷰-뷰모델 패턴을 구현하는 방법을 설명했습니다. 


이러한 기본 요소들을 구현하는 MVVM 패턴은 응용프로그램 내의 많은 시나리오들을 지원할 수 있는 가능성이 있을 것입니다. 그러나, 기본 MVVM 패턴을 확장할 것을 요구하거나 더욱 진보된 기법들이 적용될 것을 요구하는 복잡한 시나리오들에 직면할 수도 있습니다. 이것은 응용프로그램이 더 커지거나 복잡해지면 사실이 될 가능성이 있습니다. 그러나 이러한 시나리오는 많은 작은 응용프로그램들에서도 만날 수 있는 문제입니다. 프리즘 라이브러리는 이런 많은 기법들을 구현하는 컴포넌트들을 제공하며, 자신만의 응용프로그램에서 그것들을 더욱 쉽게 사용할 수 있도록 해 줍니다.


이 챕터는 약간 복잡한 시나리오들을 설명하며, 그것들을 MVVM 패턴이 그것들을 지원하는 방법에 대해서 설명합니다. 다음 섹션은 커맨드가 서로 연결되거나 차일드 뷰들과 연관되는 방식과 그것들이 커스텀 요구사항들을 지원하기 위해서 확장될 수 있는 방법에 대해서 설명합니다. 그리고 나서 그 다음 섹션들은 비동기 데이터 요청과 연속되는 UI 상호작용을 다루는 방법과 뷰와 뷰 모델 사이의 상호작용 요청을 다루는 방법에 대해서 설명합니다.


"진보된 생성 및 엮기" 섹션은 유니티나 MEF 와 같은 종속성 주입 컨테이너를 사용할 때 생성과 엮기를 다루는 가이드를 제공합니다. 마지막 섹션은 응용프로그램의 뷰 모델과 모델을 단위 테스트하고 비헤이비어를 테스트하는 가이드를 제공함으로써 MVVM 응용프로그램을 테스트하는 방법을 설명합니다.



커맨드들



커맨드는 커맨드의 구현 로직을 UI 표현과 분리하는 방법을 제공합니다. 데이터 바인딩이나 비헤이비어는 뷰의 엘리먼트들을 뷰 모델에 의해 제공되는 커맨드들과 선언적으로 연관시켜주는 방법을 제공합니다. 챕터 5 "MVVM 패턴 구현하기" 의 "커맨드들" 섹션은 커맨드가 뷰 모델에서 커맨드 오브젝트나 커맨드 메서드로 구현될 수 있는 방법과 그것들이 뷰의 컨트롤에서 혹은 비헤이비어를 사용해서 혹은 특정 컨트롤에 의해 제공되는 내장 Command 프라퍼티를 사용해서 호출될 수 있는 방법에 대해서 설명합니다.


노트:

WPF 라우티드( routed ) 커맨드들: MVVM 패턴에서 커맨드 오브젝트나 커멘드 메서드로서 구현되는 커맨드들은 라우티드 커맨드라는 이름을 가진 WPF 의 내장 커맨드 구현과는 다소 다르다는 것을 기억해야 합니다( 실버라이트는 라우티드 커맨드 구현을 가지지 않습니다 ). WPF 라우티드 커맨드는 UI 트리(  특히 논리 트리 )의 엘리먼트들을 통해 커맨드들을 라우팅함으로써 커맨드 메시지를 받습니다. 그러므로, 커맨드 메시지들은 해당 요소로부터 혹은 명시적으로 지정된 타깃 엘리먼트들까지 위 아래로 라우팅됩니다; 기본적으로, 그것들은 뷰와 연관된 뷰 모델과 같은 UI 트리 외부의 컴포넌트까지 라우팅되지는 않습니다. 그러나 WPF 라우티드 커맨드들은 뷰의 코드-비하인드에 정의된 커맨드 핸들러를 사용해서뷰 모델 클래스로 커맨드 호출을 전달할 수 있습니다.



복합 명령들


많은 경우에, 뷰 모델에 의해 정의된 커맨드는 연관된 뷰의 컨트롤에 바인딩 될 것이며, 그래서 사용자가 직접적으로 뷰 내부에서 커맨드를 실행할 수 있습니다. 그러나 어떤 경우에는, 응용프로그램 UI 의 부모 뷰에 있는 컨트롤로부터 하나 이상의 뷰 모델의 커맨드들을 실행할 수 있기를 원할 수도 있습니다.


예를 들어, 만약 응용프로그램이 사용자로 하여금 동시에 다수의 아이템들을 편집할 수 있도록 허용했다고 한다면, 당신은 응용프로그램의 툴바나 리본에 있는 버튼에 의해 표현되는 단일 커맨드를 사용해 사용자가 모든 아이템들을 저장할 수 있도록 허용하기를 원할 것입니다. 이 경우, Save All 커맨드는 각 아이템을 위한 뷰 모델 인스턴스에 의해 구현된 Save 커맨드들을 각각 실행하게 될 것입니다. 그 예가 아래 그림에 나와 있습니다.


SaveAll 복합 커맨드를 구현하기




프리즘은 CompositeCommand 클래스를 통해 이 시나리오를 지원합니다.


CompositeCommand 클래스는 다중 자식 커맨드들로 구성되는 커맨드를 표현합니다. 복합 커맨드가 실행되면, 그것의 자식 커맨드들이 순서대로 실행됩니다. 이는 UI 에서 단일 커맨드로서 커맨드 그룹을 표현할 필요가 있거나 다중 커맨드들을 하나의 논리 커맨드로 구현하기를 원할 때 유용합니다.


예를 들어, CompositeCommand 클래스는 buy/sell view 의 Submit Alll 버튼으로 표현되는 SubmitAllOrders 를 구현하기 위해서 Stock Trader Reference Implementation( Stock Trader RI ) 에서 사용되는 클래스입니다. 사용자가 Submit All 버튼을 클릭하면, 개별 buy/sell 트랜잭션에 의해 정의된 개별 SubmitCommand 가 실행됩니다.


CompositeCommand 클래스는 자식 커맨드들의 리스트를 유지합니다( 그것들은 DelegateCommand 인스턴스들입니다 ). CompositeCommand 의 Execute 메서드는 단지 자식 커맨드들의 Execute 메서드를 순서대로 호출할 뿐입니다. CanExecute 메서드는 이와 유사하게 개별 자식 커맨드들의 CanExecute 메서드에 대한 호출을 하지만, 하나의 자식 커맨드라도 실행될 수 없는 상태라면 CanExecute false 를 반환하게 될 것입니다. 다시 말해, 기본적으로, CompositeCommand 는 모든 자식 커맨드들이 실행될 수 있을 때만 실행됩니다.



자식 커맨드들을 등록하고 등록해제하기


자식 커맨드들은 RegisterCommand UnregisterCommand 메서드를 통해서 등록되고 등록해제됩니다. 예를 들면 Stock Trader RI 에서, 각 buy/sell 주문에 대한 Submit 과 Cancel 커맨드는 SubmitAllOrders 와 CancelAllOrders 복합 커맨드에 등록됩니다. 아래 코드는 그 예를 보여 줍니다( OrderController 클래스를 참조하십시오 ).



노트:

앞의 commandProxy 오브젝트는 Submit 와 Cancel 복합 커맨드에 접근하는 인스턴스를 제공하는데, 그것은 정적으로 정의되어 있습니다. 더 많은 정보를 원한다면, StockTraderRICommands.cs 파일의 클래스를 참조하십시오.



활성화된 자식 뷰들에서 커맨드 실행하기


보통, 응용프로그램은 응용프로그램 UI 내의 자식 뷰 컬렉션을 출력할 필요가 있습니다. 여기에서 각 자식 뷰는 하나 이상의 커맨드를 구현하는 연관된 뷰 모델을 가지고 있을 것입니다. 복합 커맨드들은 응용프로그램 UI 내의 자식 뷰들에 의해 구현된 커맨드들을 표현하기 위해 사용될 수 있으며, 이는 그것들이 부모 뷰에서 실행되는 방법을 조직화하는데 도움을 줍니다. 이 시나리오들을 지원하기 위해서, 프리즘 CompositeCommand DelegateCommand 클래스는 프리즘 리전( region )과 함께 동작하도록 설계되어 왔습니다.


프리즘 리전( 챕터 7 "유저 인터페이스 만들기" 의 "리전들" 에 설명되어 있습니다 )은 자식 뷰들이 응용프로그램 UI 의 논리적 플레이스홀더( placeholder ) 와 연관되는 방법을 제공합니다. 그것들은 보통 자식 뷰들의 특정 레이아웃을 UI 에서의 위치 및 논리적 플레이스홀더와 디커플링하기 위해서 사용됩니다. 다음 그림은 각 자식 뷰들이 EditRegion 이라 명명된 리전에 추가되고 UI 디자이너가 그 리전에 뷰를 배치하기 위해서 Tab 컨트롤을 사용하는 것을 보여 줍니다.


Tab 컨트롤을 사용해 EditRegion 정의하기




부모 뷰 레벨에서 복합 커맨드는 보통 자식 뷰 수준에서 커맨드가 실행되는 방법을 조직화하기 위해 사용될 것입니다. 어떤 경우에는, 이전에 설명된 Save All 커맨드 예제에서 처럼, 당신은 보여지는 모든 뷰를 위해 커맨드가 실행되기를 원할 것입니다. 다른 경우에는, 당신은 커맨드들이 활성화된 뷰 상에서만 실행되기를 원할 것입니다. 이 경우, 복합 커맨드는 활성화되었다고 여겨지는 뷰 상에서만 자식 커맨드들을 실행할 것입니다. 예를 들어, 당신은 응용프로그램의 툴바나 리본의 Zoom 커맨드를 구현하는데, 그것이 현재 활성화된 아이템에서만 적용되도록 하기를 원할 것입니다. 다음 다이어그램은 그것을 보여 줍니다.


Tab 컨트롤을 사용하여 EditRegion 을 정의하기




이 시나리오를 지원하기 위해서, 프리즘은 IActiveAware 인터페이스를 제공합니다. IActiveAware 인터페이스는 구현자가 활성화되어 있을 때 true 를 반환하는 IsActive 프라퍼티를 정의하며, 그 활성화 상태가 변경될 때마다 발생하는 IsActiveChanged 이벤트를 정의합니다.


자식 뷰나 뷰 모델에서 IActiveAware 인터페이스를 구현할 수 있습니다. 그것은 주로 리전 안의 자식 뷰의 활성화 상태를 트래킹하는데 사용됩니다. 뷰가 활성화되어 있는지 여부는 특정 리전 컨트롤 내의 뷰를 조직하는 리전 어댑터에 의해 결정됩니다. 예를 들어 이전에 보여 준 Tab 컨트롤에 대해, 현재 선택된 탭에서 서 뷰가 활성화되어 있다고 설정하는 리전 어댑터가 존재합니다.


DelegateCommand 클래스도 IActiveAware 인터페이스를 구현합니다. CompositeCommand 는 ( CanExecute 와는 별개로 ) 생성자에서 monitorCommandActivity 파라미터를 true 로 설정함으로써 자식 DelegateCommands 의 활성화 상태를 평가하기 위해서 구성될 수 있습니다. 이 파라미터가 true 로 설정되면, CompositeCommand 클래스는 CanExecute 메서드를 위해 반환값을 결정할 때와 Execute 메서드 내에서 자식 커맨드들이 실행될 때 각 자식 DelegateCommand 의 활성화 상태를 고려할 것입니다.


monitorCommandActivity 파라미터가 true 이면, CompositeCommand 클래스는 다음과 같은 동작들을 보여 줍니다:

  • CanExecute. 모든 활성화된 커맨드들이 실행될 수 있을 때만 true 를 반환합니다. 비활성화된 자식 커맨드들은 전혀 고려되지 않습니다.
  • Execute. 모든 활성화된 커맨드들을 실행합니다. 비활성화된 자식 커맨드들은 전혀 고려되지 않습니다.

이 기능을 사용해서 앞서 설명한 예제를 구현할 수 있습니다. IActiveAware 인터페이스를 자식 뷰나 모델에 구현함으로써, 당신은 자식 뷰가 영역에서 활성화되거나 비활성화되는 때를 통지받을 수 있습니다. 자식 뷰의 활성화 상태가 변경되면, 자식 커맨드의 활성화 상태를 갱신할 수 있습니다. 그리고 나서 사용자가 Zoom 복합 커맨드를 실행하면, 활성화된 자식 뷰의 Zoom 커맨드가 실행될 것입니다.


컬렉션들 내부의 커맨드들

뷰에서 아이템 컬렉션을 출력할 때 당신이 자주 직면하게 되는 다른 일반적인 시나리오는 컬렉션의 각 아이템을 위한 UI 를 ( 아이템 레벨이 아니라 ) 부모 뷰 레벨에서 커맨드와 연관시킬 필요가 있을 때입니다.


예를 들어, 다음 그림에서 보여 주는 응용프로그램에서, 뷰는 ListBox 컨트롤에서 아이템 컬렉션을 출력합니다. 그리고 사용자가 컬렉션에서 개별 아이템들을 제거할 수 있도록 하는 Delete 버튼을 정의하는 아이템을 출력하기 위해서 데이터 템플릿을 사용합니다.


컬렉션 내의 커맨드들을 바인딩하기




뷰 모델이 Delete 커맨드를 구현하기 때문에, 각 아이템을 위한 UI 의 Delete 버튼을 뷰 모델에 의해 구현된 Delete 커맨드와 엮는 것이 도전 과제입니다. ListBox 의 각 아이템들에 대한 데이터 칸텍스트가 Delete 커맨드를 구현하는 부모 뷰 모델이 아니라 컬렉션 내의 아이템들을 참조하고 있는 것이 어려운 점입니다.


이 문제를 해결하는 한 가지 접근법은, 데이터 템플릿이 아니라 부모 컨트롤에 상대적인 바인딩을 보장하도록 하기 위해, ElementName 바인딩 프라퍼티를 사용하여 부모 뷰의 커맨드에 데이터 템플릿의 버튼을 바인딩하는 것입니다. 다음 XAML 은 이 기법을 설명하고 있습니다.



데이터 템플릿의 버튼 컨트롤의 내용은 컬렉션 아이템의 Name 프라퍼티에 바인딩된다. 하지만 버튼을 위한 커맨드는 root 엘리먼트의 데이터 칸텍스트를 통해 Delete 커맨드에 바인딩됩니다. 이는 버튼이 아이템 레벨이 아니라 부모 뷰 레벨에서 커맨드에 바인딩될 수 있도록 허용해 줍니다. 당신은 CommandParameter 프라퍼티를 사용해서 아이템에 적용될 커맨드가 무엇인지를 지정하거나, ( CollectionView 를 통해 ) 현재 선택된 아이템에서 수행될 커맨드를 구현할 수 있습니다.



커맨드 비헤이비어


실버라이트 3 이전 버전에서, 실버라이트는 직접적으로 커맨드들을 지원하는 컨트롤들을 제공하지 않았습니다. ICommand 인터페이스가 이용 가능했지만, 컨트롤들이 ICommand 구현에 직접적으로 연결될 수 있도록  허용해주는 Command 프라퍼티를 구현한 컨트롤이 존재하지 않았습니다. 이 한계를 극복하기 위해서, 그리고 실버라이트 3 에서 MVVM 커맨딩 패턴들을 지원하기 위해서, 프리즘 라이브러리( 버전 2.0 )은 모든 실버라이트 컨트롤들이 연결된 비헤이비어를 사용해 커맨드 오브젝트에 바인딩될 수 있도록 허용하는 메커니즘을 제공했습니다. 이 메커니즘은 WPF 에서도 작동하는데, 이는 뷰 모델 구현들이 실버라이트와 WPF 에서 재사용될 수 있도록 해 주었습니다.


다음 예제는 버튼의 클릭 이벤트에 뷰에서 정의한 커맨드 오브젝트가 바인딩될 수 있도록 하기 위해서 프리즘 커맨드 비헤이비어가 사용되는 방식을 보여 줍니다.



실버라이트 4 는 모든 Hyperlink-상속 컨트롤들과 ButtonBase-상속 컨트롤들을 위해 Command 프라퍼티를 위한 지원을 추가했으며, 이는 그것들이 WPF 에서와 같은 방식으로 커맨드 오브젝트들에 바인딩될 수 있도록 해 줬습니다. 이러한 컨트롤들을 위해서 Command 프라퍼티를 사용하는 것은 챕터 5 "MVVM 패턴 구현하기" 의 "커맨드들" 섹션에서 설명되고 있습니다. 그러나 프리즘 커맨드 비헤이비어는 하위 호환성과 커스텀 비헤이비어의 개발을 지원하기 위해서 남아 있으며, 이는 뒤에서 설명됩니다.


이 비헤이비어 접근법은 뷰의 컨트롤에 쉽게 적용될 수 있는 인터랙티브 비헤이비어를 구현하고 캡슐화하기 위해서 범용적으로 이용 가능한 기법입니다. 이전에 보여 준 커맨드를 지원하기 위해서 비헤이비어를 사용하는 것은 비헤이비어가 지원할 수 있는 많은 시나리오들 중의 하나일 뿐입니다. Microsoft Expression Blend 는 이제 챕터 5 "MVVM 패턴 구현하기" 의 "뷰에서 커맨드 메서드 실행하기" 섹션에서 설명했던 InvokeCommandAction CallMethodAction 을 포함하는 다양한 비헤이비어들을 제공합니다. 그리고 커스텀 비헤이비어를 개발하기 위한 소프트웨어 개발 키트( SDK )를 제공합니다. Expression Blend 는 비헤이비어를 위해 드래그-앤-드랍 생성 및 프라퍼티 편집 지원을 제공하는데, 이는 비헤이비어 추가 작업을 쉽게 만들어 줍니다. 커스텀 Expression Blend 비헤이비어를 개발하는 것에 대한 더 많은 정보를 원한다면, MSDN 의 "Creating Custom Behavior" 를 참조하십시오.


실버라이트 4 에서 커맨드-이용 가능한 컨트롤들에 대한 지원을 소개하고, Expression Blend Behaviors SDK 를 소개하기는 했지만, 프리즘 커맨드 비헤이비어에 대한 대단한 요구들이 있는 것은 아닙니다. 당신은 아마도 그것들의 치밀한 구문과 구현들, 그리고 쉽게 확장될 수 있는 능력과 유용성을 발견하게 될 것입니다.



프리즘 커맨드 비헤이비어들을 확장하기


프리즘 커맨드 비헤이비어는 어태치드( attached ) 비헤이비어 패턴에 기반하고 있습니다. 이 패턴은 컨트롤에 의해 발생되는 이벤트들을 뷰 모델에 의해 제공되는 커맨드 오브젝트에 연결합니다. 프리즘 커멘드 비헤이비어는 두 개의 부분으로 구성됩니다; 어태치드 프라퍼티와 비헤이비어 오브젝트. 어태치드 프라퍼티는 타깃 컨트롤과 비헤이비어 오브젝트 간의 관계를 수립합니다. 비헤이비어 오브젝트는 타깃 컨트롤을 관찰하며, 컨트롤이나 뷰 모델 내의 이벤트들이나 상태 변경들에 기반한 동작을 수행합니다.


프리즘 커맨드는 ButtonBase-상속 컨트롤들의 Click 이벤트에 기반합니다. 이는 ButtonBaseClickCommandBehavior 클래스와 타깃 컨트롤의 클릭 이벤트에 그것을 연결하는 어태치드 프라퍼티를 제공함으로써 가능해 집니다. 아래 그림은 ButtonBase, ButtonBaseClickCommandBehavior, 뷰 모델에 의해 제공되는 ICommand 오브젝트의 관계를 보여 줍니다.


ButtonClick 이벤트를 ICommand 에 전달하기





당신의 응용프로그램은 ButtonBase 로부터의 Click 이벤트가 아니라 컨트롤이나 이벤트로부터의 커맨드들을 실행할 필요가 있습니다. 혹은 비헤이비어가 타깃 컨트롤과 상호작용하는 방식이나 뷰 모델이 그것에 바인딩되는 방식을 커스터마이즈할 필요가 있습니다. 이런 경우에, 당신은 자신만의 어태치드 프라퍼티나 비헤이비어 구현을 정의할 필요가 있습니다.


프리즘 라이브러리는 CommandBehaviorBase<T> 클래스를 제공해서 ICommand 오브젝트들과 상호작용하는 비헤이비어들을 생성하기 쉽게 만들어 줍니다. 이 클래스는 커맨드를 실행하고 커맨드의 CanExecuteChanged 이벤트의 변경을 관찰하고, 실버라이트와 WPF 에서 커맨드 지원을 확장하는데 사용될 수 있습니다.


커스텀 비헤이비어를 생성하려면, CommandBehaviorBase<T> 를 상속하는 클래스를 생성하고 당신이 관찰하고 싶은 컨트롤을 타깃으로 설정하십시오. 이 클래스의 타입 파라미터는 비헤이비어가 연결된 컨트롤의 타입을 식별합니다. 클래스의 생성자에서, 당신은 컨트롤로부터 관찰하고자 하는 이벤트들을 구독할 수 있습니다. 다음 코드 예제는 ButtonBaseClickCommandBehavior 클래스의 구현을 보여 줍니다.



CommandBehaviorBase<T> 클래스를 사용하면, 자신만의 커스텀 비헤이비어 클래스를 정의할 수 있습니다; 이는 타깃 컨트롤이나 뷰 모델에 의해 제공되는 커맨드들과 비헤이비어가 상호작용하는 방식을 커스터마이즈 할 수 있도록 해 줍니다. 예를 들어, 당신은 다양한 컨트롤 이벤트에 기반해 바인딩된 커맨드를 실행하거나 바인딩된 커맨드의 CanExecute 상태에 기반해 컨트롤의 가시적 상태를 변경하는 비헤이비어를 정의할 수 있습니다.


타깃 컨트롤에 커맨드 비헤이비어를 선언적으로 어태치하는 것을 지원하기 위해서, 어태치드 프라퍼티가 사용됩니다. 어태치드 프라퍼티는 비헤이비어가 XAML 에서 컨트롤에 어태치되는 것을 허용하며, 타깃 컨트롤과 비헤이비어 구현의 연관 및 생성을 관리합니다. 어태치드 프라퍼티는 정적 클래스에서 정의됩니다. 프리즘 커맨드 비헤이비어는 어떤 규약에 기반하는데, 그것은 커맨드를 실행하기 위해서 사용되는 이벤트를 가리키는 정적 클래스의 이름입니다. 어태치드 프라퍼티의 이름은 데이터 바인딩되고 있는 오브젝트의 타입을 가리킵니다. 그러므로 앞서 설명한 프리즘 커맨드 비헤이비어는 Click 이라 명명된 정적 클래스를 사용합니다. 이 클래스는 Command 라고 명명된 어태치드 프라퍼티를 정의합니다. 이는 앞에서 보여 준 Click.Command 라는 구문을 사용하는 것을 허용합니다.


커맨드 비헤이비어 자체가 어태치드 프라퍼티를 통해 타깃 컨트롤과 실제로 연관되기도 합니다. 그러나 이 어태치드 프라퍼티는 정적 클래스에 대해 private 이며, 개발자는 볼 수 없습니다.



Command 어태치드 프라퍼티의 구현은 ButtonBaseCommandBehavior 클래스의 인스턴스를 생성합니다. 이는 OnSetCommandCallback 콜백 메서드를 통해서 이루어 집니다. 그것이 아래 코드 예제에 나와 있습니다.



어태치드 프라퍼티에 대한 더 많은 정보를 원한다면 MSDN 의 "Attached Properites Overview" 를 참조하십시오.



비동기 상호작용들을 다루기



보통 뷰 모델들을 비동기적으로 통신하는 서비스들이나 컴포넌트들과 상호작용할 필요가 있습니다.이는 당신이 실버라이트 응용프로그램을 다루거나, 웹서비스타 네트워크를 통한 다른 리소스들과 상호작용하거나, 응용프로그램이 계산이나 I/O 를 수행하기 위해서 백그라운드 작업을 사용할 때 특히 그렇습니다. 이러한 연산을 비동기적으로 수행하는 것은 응용프로그램이 좋은 사용자 경험을 배달하는데 필수적인 응답성을 유지하고 있음을 보장해 줍니다.


사용자가 비동기적인 요청이나 백그라운드 작업을 초기화할 때, 그 응답이 언제 도착할지와 스레드가 언제 반환될지를 예측하는 것은 어렵습니다. UI 는 UI 스레드에서만 갱신될 수 있기 때문에, 당신은 UI 스레드 상에서 요청을 디스패치함으로써 UI 를 갱신해야 할 것입니다.



웹 서비스들에서 데이터를 획득하고 상호작용하기


웹 서비스나 원격 접근 기법들과 상호작용할 때, 당신은 보통 IAsyncResult 패턴을 사용할 것입니다. 이 패턴에서는 GetQuestionnaire 같은 메서드를 호출하는 대신, BeginGetQuestionnaire EndGetQuestionnaire 와 같은 메서드 쌍을 사용합니다. 비동기 호출을 초기화하기 위해서 BeginGetQuestionnaire 를 호출합니다. 결과를 획득하거나 타깃 메서드를 실행하는 도중에 예외가 발생하는지를 확인하기 위해서, 호출이 완료되었을 때 EndGetQuestionnaire 를 호출합니다.


EndGetQuestionnaire 의 호출 시점을 결정하려면, BeginGetQuestionnaire 를 호출하는 동안에 사용할 콜백을 지정하거나 완료되었는지 상황을 조사할 수 있습니다. 콜백 접근법을 사용하면, 당신의 콜백 메서드가 타깃 메서드 실행이 완료되었을 때 호출될 것입니다. 그러면 거기에서 EndGetQuestionnaire 를 호출합니다. 그 예가 아래에 나와 있습니다.



End 메서드( 이 경우 EndGetQuestionnaire )에 대한 호출에서 주의할 점이 있습니다. 요청을 실행하는 동안에 발생한 예외가 실행될 수도 있다는 것입니다. 당신의 응용프로그램은 반드시 이를 처리해야만 하며, UI 를 통해 스레드 안전한 방법으로 그들에게 보고해 줘야할 필요가 있을 것입니다. 만약 이것들을 다루지 않는다면, 스레드는 종료될 것이고, 당신은 결과를 처리할 수 없게 될 것입니다.


응답이 항상 UI 스레드에서 발생하는 것은 아니기 때문에, 당신이 UI 상태에 영향을 줄 수 있는 무엇인가를 변경하려고 하는 계획득 세웠다면, 스레드 Dispatcher 오브젝트나 SynchronizationContext 오브젝트를 사용해서 UI 스레드에 그 응답을 디스패치해야 할 것입니다. WPF 와 실버라이트에서는 보통 디스패처를 사용할 것입니다.


다음 코드 에제에서, Questionnaire 오브젝트는 비동기적으로 획득되며, 그것은 QuestionnaireView 를 위한 데이터 컨텍스트로서 설정됩니다. 실버라이트에서 당신은 디스패처의  CheckAccess 메서드를 사용해서 당신이 지금 UI 스레드 상에서 작업하고 있는지 여부를 확인할 수 있습니다. 만약 UI 스레드가 아니라면, BeginInvoke 메서드를 사용해서 UI 스레드로 그 요청이 운반되도록 할 필요가 있습니다.



Model-View-ViewModel Reference Implementation( MVVM RI ) 는 IAsyncResult-상속 서비스 인터페이스를 이전 예제와 유사한 방식으로 사용하는 방식의 예를 보여 줍니다. 이는 고객을 위해 더 단순한 콜백 메커니즘을 제공하기 위해서 서비스를 래핑하고, 호출자의 스레드에 콜백을 디스패치하기 하는 것을 다룹니다. 예를 들어, 다음 코드 예제는 설문지( questionnaire )를 획득하는 것을 보여 줍니다.



반환되는 result 오브젝트는 발생할 수 있는 에러들과 함께 획득한 결과를 래핑합니다. 다음 코드 예제는 에러가 평가되는 방법을 보여 줍니다.




유저 인터페이스 패턴들



응용프로그램들에서는 이벤트의 발생을 사용자에게 통지하거나 작업을 진행하기 전에 확인을 요청할 필요성이 빈번하게 발생합니다. 이러한 상호작용들은 보통 응용프로그램에서의 변화를 사용자들에게 단순하게 전달하기 위해서 설계된 짧은 상호작용이거나 사용자들로부터 간단한 응답을 획득합니다. 이러한 상호작용들 중의 일부는 사용에게 다이얼로그 박스나 메시지 박스를 출력할 때와 같이 modal 로 나타납니다. 혹은 토스트 통지나 팝업 윈도우처럼 

non-modal 로 나타납니다.


이러한 경우에 사용자와 상호작용하는 여러 가지 방법들이 존재하지만, 그것들을 명확한 관심사 분리를 보존하는 방식으로 MVVM 기반 응용프로그램에서 구현하는 것은 어려운 일이 될 수 있습니다. 예를 들어, non-MVVM 응용프로그램에서, 응답을 위한 프롬프트를 띄우기 위해 코드-비하인드 파일에서 MessageBox 클래스를 사용할 것입니다. MVVM 응용프로그램에서, 이는 적절하지 않습니다. 왜냐하면 그것은 뷰와 뷰 모델 간의 관심사 분리를 깰 것이기 때문입니다.


MVVM 패턴의 관점에서, 뷰 모델은 사용자의 상호작용을 초기화할 책임과 모든 응답을 처리할 책임이 있고, 반면에 뷰는 사용자 경험에 적절한 무엇인가를 사용해 사용자와의 상호작용을 실제로 관리할 책임이 있습니다. 뷰 모델에 구현된 프리젠테이션 로직과 뷰에 의해 구현된 사용자 경허 사이의 관심사 분리를 보존하는 것은 테스트 용이성과 유연성을 증대시킵니다.


MVVM 에는 이러한 종류의 사용자 상호작용을 구현하기 위한 두 가지 일반적인 접근법이 존재합니다. 한 접근법은 사용자와의 상호작용을 초기화하기 위해서 뷰 모델에 의해 사용될 수 있는 서비스를 구현하는 것이며, 그래서 뷰의 구현에 대한 의존성을 서비스의 의존성을 보존하는 것입니다. 다른 접근법은 사용자와의 상호작용에 대한 의도를 표현하기 위해서 뷰 모델에 의해 발생되는 이벤트들을 사용하는 것과 더불어, 이러한 이벤트들에 바인딩되고 상호작용에 대한 가시적 관점을 관리하는 뷰의 컴포넌트를 사용하는 것입니다. 각 접근법은 다음 섹션들에 설명됩니다.



상호작용 서비스 사용하기


이 접근법에서, 뷰 모델은 메시지 박스를 통해 사용자와의 상호작용을 초기화하기 위해서 상호작용 서비스 컴포넌트에 의존합니다. 이 접근법은 관심사의 명확한 분리와 테스트 용이성을 지원하는데, 이는 상호작용의 가시적 구현을 개별 서비스 컴포넌트에 캡슐화함으로써 수행됩니다. 일반적으로, 뷰 모델은 상호작용 서비스 인터페이스에 대한 의존성을 가집니다. 그것은 의존성 주입이나 서비스 로케이터를 통해 상호작용 서비스의 구현에 대한 참조를 빈번하게 요청합니다.


뷰 모델이 상호작용 서비스에 대한 참조를 가지게 되면, 그것은 프로그램적으로 필요할 때마다 사용자와의 상호작용을 여청할 수 있습니다. 상호작용 서비스는 상호작용의 가시적 관점을 구현하는데, 이는 다음 그림에 나와 있습니다. 뷰 모델에서 인터페이스 참조를 사용하는 것은 사용자 인터페이스의 구현 요구사항에 따라 여러 가지 구현이 사용될 수 있도록 허용해 줍니다. 예를 들어, WPF 와 실버라이트를 위한 상호작용 서비스의 구현이 제공될 수도 있는데, 이는 응용프로그램의 로직의 재사용성을 많이 높여 줍니다.


사용자와의 상호작용을 위해서 상호작용 서비스를 사용하기



Modal interactions, such as where the user is presented with MessageBox or modal pop-up window to obtain a specific response before execution can proceed, can be implemented in a synchronous way, using a blocking method call, as shown in the following code example.



그러나 이접근법의 단점은 동기적인 프로그래밍 모델을 강제한다는 것인데, 이는 상호작용 서비스를 구현할 때 실버라이트에서 다른 상호작용 메커니즘들과는 공유되지 않고, 매우 많은 어려움을 겪게 만듭니다. 대안적인 비동기적 구현은 상호작용이 완료되었을 때 실행될 콜백을 뷰 모델이 제공하도록 허용하는 것입니다. 다음 코드는 이러한 접근법을 설명합니다.



비동기 접근법은 modal 및 non-modal 상호작용이 구현될 수 있도록 허용함으로써 상호작용 서비스를 구현할 때 유연성을 증대시켜 줍니다. 예를 들어, WPF 에서, MessageBox 클래스는 진짜로 사용자와의 modal 상호작용 구현하기 위해서 사용될 수 있습니다; 반면에, 실버라이트에서, 팝업 윈도우는 사용자와의 pseudo-modal 상호작용을 구현하기 위해서 사용될 수 있습니다.



상호작용 요청 오브젝트들을 사용하기


MVVM 에서 단순한 사용자 상호작용을 구현하기 위한 다른 접근법은, 뷰의 비헤이비어와 쌍을 이루는 상호작용 오브젝트를 통해서, 뷰 모델이 뷰 자체에 대한 상호작용 요청을 직접적으로 만드는 것을 허용하는 것입니다. 이 상호작용 요청 오브젝트는 이벤트들을 통해 상호작용 요청, 그것의 응답을 캡슐화하며, 뷰와 통신합니다. 뷰는 상호작용에 대한 사용자 경험 부분을 초기화하기 위해서 이러한 이벤트들을 구독합니다. 뷰는 일반적으로 뷰 모델에 의해서 제공되는 상호작용 요청 오브젝트에 바인딩되는 비헤이비어에 상호작용데 대한 사용자 경험을 캡슐화합니다. 다음은 이를 설명합니다.


사용자와의 상호작용을 위해서 상호작용 요청 오브젝트 사용하기



이 접근법은 단순한, 그리고 여전히 유연한 메커니즘을 제공하는데, 이는 뷰 모델과 뷰 사이의 명확한 관심사 분리를 보존합니다 - 이는 뷰 모델이 응용프로그램의 프리젠테이션 로직과 요청되는 사용자 상호작용들을 캡슐화하도록 해 주는 반면, 뷰가 상호작용의 가시적 관점을 완전하게 캡슐화할 수 있도록 해 줍니다. 뷰를 통해 기대한 사용자와의 상호작용을 포함하는, 뷰 모델의 구현이 쉽게 테스트될 수 있습니다. 그리고 UI 디자이너는, 상호작용을 위해 다양한 사용자 경험을 캡슐화하는 다양한 비헤이비어의 사용을 통해, 상호작용을 구현하는 방식을 선택하는데 있어 많은 유연성을 가지게 됩니다.


이 접근법은 MVVM 패턴을 사용해서 일관성을 가지는데, 뷰가 뷰 모델상에서 관찰하고 있는 상태 변경들을 반영할 수 있게 해 주며, 둘 사이의 데이터 통신을 위해서 양방향 데이터 바인딩을 사용합니다. 상호작용 요청 오브젝트 내의 상호작용에 대한 비가시적 요소들에 대한 캡슐화, 그리고 상호작용의 가시적 요소들을 관리하기 위해 관련 비헤비어를 사용하는 것은, 커맨드 오브젝트와 커맨드 비헤이비어가 사용되는 방식과 매우 유사합니다.


이 접근법은 프리즘에 의해 가깝게 채택됩니다. 프리즘 라이브러리는 IInteractionRequest 인터페이스와 InteractionRequest<T> 클래스를 통해서 이 패턴을 직접적으로 지원합니다. 뷰의 비헤이비어들은 이 인터페이스에 바인딩되며, 그것이 노출하는 이벤트를 구독합니다. InteractionRequeset<T> 클래스는 IInteractionRequest 인터페이스를 구현하고 뷰 모델이 상호작용을 초기화하고 요청을 위한 칸텍스트를 지정하고, 선택적으로 콜백 대리자를 지정하는 것을 허용하기 위해서 두 개의 Raise 메서드를 정의합니다.



뷰 모델에서 상호작용 요청들을 초기화하기


InteractionRequest<T> 클래스는 상호작용 요청 동안에 뷰와 뷰 모델의 상호작용을 조직합니다. Raise 메서드는 뷰 모델이 상호작용을 초기화하고, ( T 타입의 ) 칸텍스트 오브젝트를 지정하고, 상호작용이 완료되었을 때 호출될 콜백 메서드를 지정하는 것을 허용합니다. 칸텍스트 오브젝트는 뷰 모델이 사용자와의 상호작용 동안에 사용될 수 있는 데이터와 상태를 뷰에 넘기는 것을 허용합니다. 만약 콜백 메서드가 정의되어 있다면, 칸텍스트 오브젝트는 뷰 모델에 다시 넘겨질 것입니다; 이는 상호작용 동안에 사용자가 만든 어떤 변경이 뷰 모델로 다시 넘어 가는 것을 허용합니다.



프리즘은 미리 정의된 칸텍스트 클래스를 제공하는데, 이는 일반적인 상호작용 요청 시나리오들을 지원합니다. Notification 클래스는 모든 칸텍스트 오브젝트들을 위한 기저 클래스입니다. 이는 응용프로그램 내의 중요한 이벤트를 사용자에게 통지하기 위해서 상호작용 요청이 사용될 때 사용됩니다. 이는 두 개의 프라퍼티들을 제공합니다 - Title Content - 이것은 사용자에게 출력될 것입니다. 일반적으로 통지들은 단방향이며, 그래서 사용자가 이 값들을 상호작용하는 도중에 변경하는 것은 기대되지 않습니다.


Confirmation 클래스는 Notification 클래스를 상속하고, 세 번째 프라퍼티를 추가합니다 - Confirmed - 이것은 사용자가 확인을 누르거나 작업을 거부했음을 표시하기 위해서 사용됩니다. Confirmation 클래스는 사용자로부터 yes/no 응답을 획득하기 원할 때 MessageBox 스타일 상호작용을 구현하기 위해서 사용됩니다. 당신은 Notification 클래스를 상속하는 커스텀 칸텍스트 클래스를 정의해서 당신이 상호작용을 지원하는데 필요로 하는 데이터와 상태를 캡슐화할 수 있습니다.


InteractionRequest<T> 클래스를 사용하기 위해, 뷰 모델 클래스는 InteractionRequest<T> 클래스의 인스턴스를 생성하고, 뷰가 그것에 데이터 바인딩되는 것을 허용하기 위한 읽기 전용 프라퍼티를 정의할 것입니다. 뷰 모델이 요청을 초기화하고자 할 때, 그것은 Raise 메서드를 호출하는데, 칸텍스트 오브젝트를 넘기고, 선택적으로 콜백 대리자를 넘길 것입니다.



MVVM Reference Implementation( MVVM RI ) 는 조사( survey ) 응용프로그램에서 뷰와 뷰 모델 간의 사용자 상호작용을 구현하기 위해서 IInteractionRequest 인터페이스와 InteractionRequest<T> 클래스가 사용되는 방법에 대해 설명합니다( QuestionnaireViewMode.cs 를 참조하십시오 ).



상호작용 사용자 경험을 구현하기 위해서 비헤이비어들을 사용하기


상호작용 요청 오브젝트는 논리적인 상호작용을 표현하기 때문에, 상호작용을 위한 정확한 사용자 경험은 뷰에 정의됩니다. 상호작용을 위한 사용자 경험을 캡슐화하기 위해서 보통 비헤이비어가 사용됩니다; 이는 UI 디자이너가 적절한 비헤이비어를 선택하고 그것을 뷰 모델의 상호작용 요청 오브젝트에 바인딩할 수 있도록 해 줍니다.


뷰는 반드시 상호작용 요청 이벤트를 검출할 수 잇도록 설정되어야만 하고, 그리고 나서 그 요청을 위한 적절한 가시적 출력을 제출할 수 있도록 설정되어야만 합니다. Microsoft Expression Blend Behavior Framework 은 트릭거와 액션의 개념을 지원합니다. 트리거는 특정 이벤트가 발생할 때마다 액션을 초기화하기 위해서 사용됩니다.


 Expression Blend 에 의해서 제공되는 표준 EventTrigger 는, 뷰 모델에 의해서 노출된 상호작용 요청 오브젝트들에 바인딩됨으로써, 상호작용 요청 이벤트를 모니터링합니다. 그러나 프리즘 라이브러리는 커스텀 EventTrigger 를 정의하느데, InteractionRequestTrigger 라는 이름을 가지고 있고, 그것은 IInteractionRequest 인터페이스에 대한 적절한 Raised 이벤트에 자동적으로 연결됩니다. 이는 필요한 XAML 의 양을 줄여 주고, 잘못된 이벤트 이름이 부주의하게 들어가는 경우를 줄여 줍니다.


이벤트가 발생하고 나면, InteractionRequestTrigger 가 특정 액션을 실행합니다. 실버라이트에 대해, 프리즘 라이브러리는 PopupChildWindowAction 클래스를 제공하는데, 이는 사용자에게 팝업 윈도우를 보여 줍니다. 자식 윈도우가 출력될 때, 그것의 데이터 칸텍스트는 상호작용 요청에 대한 칸텍스트 파라미터로 설정됩니다. PopupChildWindowAction ContentTemplate 프라퍼티를 사용하면, 칸텍스트 오브젝트의 Content 프라퍼티를 위해 사용되는 UI 레이아웃을 정의하기 위한 데이터 템플릿을 지정할 수 있습니다.


팝업 윈도우의 타이틀은 칸텍스트 오브젝트의 Title 프라퍼티에 바인딩됩니다.


노트:

기본적으로, PopupChildWindowAction 클래스에 의해 출력되는 특정 타입의 팝업 윈도우는 칸텍스트 오브젝트의 타입에 의존합니다. Notification 칸텍스트 오브젝트에 대해서는 NotificationChildWindow  가 출력되는 반면에, Confirm 칸텍스트 오브젝트에 대해서는 ConfirmationChildWindow 가 출력됩니다. NotificationChildWindow 는 간단한 팝업 윈도우를 출력해 통지를 보여 주는 반면에, ConfirmationChildWindow OK Cancel 버튼을 가지고 있어서 사용자의 응답을 캡쳐합니다. 당신은 이 비헤이비어를 재정의할 수 있는데, 이는 PopupChildWindowAction 클래스의 ChildWindow 프라퍼티를 사용하여 팝업 윈도우를 지정합으로써 수행됩니다.


다음의 예제는 InteractionRequestTrigger PopupChildWindowAction 을 사용해서 MVVM RI 에서 사용자를 위한 확인용 팝업 윈도우를 출력하는 방법을 보여 줍니다.



노트:

ContentTemplate 프라퍼티를 사용해서 지정되는 데이터 템플릿은 칸텍스트 오브젝트의 Content 프라퍼티를 위한 UI 레이아웃을 정의합니다. 앞의 코드에서, Content 프라퍼티는 문자열이며, 그래서 TextBlock Content 프라퍼티 자체에 바인딩됩니다.


사용자가 팝업 윈도우와 상호작용할 때, 칸텍스트 오브젝트는 팝업 윈도우에서 정의된 바인딩이나 칸텍스트 오브젝트의 Context 프라퍼티를 출력하기 위해서 사용된 데이터 템플릿에 따라 갱신됩니다. 사용자가 팝업 윈도우를 닫은 후에는, 콜백 베서드를 통해서 칸텍스트 오브젝트가 갱신된 값과 함께 뷰 모델에 다시 넘겨 집니다. MVVM RI 에서 사용된 확인 예제에서, 기본 확인 뷰는, OK 버튼이 클릭될 때, 제공된 Confirmation 오브젝트의 Confirmed 프라퍼티를 true 로 설정할 책임이 있습니다.


다른 상호작용 메커니즘을 정의하기 위해서 다양한 트리거들과 액션들이 정의될 수 있습니다. 프리즘의 InteractionRequestTrigger PopupChildWindowAction 클래스에 대한 구현은 당신만의 트리거들이나 액션들을 개발하기 위한 기반으로 사용될 수 있습니다.



진보된 생성 및 엮기( wire-up )



MVVM 패턴을 성공적으로 구현하기 위해서는, 뷰, 모델, 뷰 모델의 책임을 완전하게 이해해야 할 필요가 있습니다. 그래야 올바른 클래스들로 응용프로그램 코드를 구현할 수 있습니다. ( 데이터 바인딩, 커맨드, 상호작용 요청 등을 통해 ) 이러한 클래스들이 상호작용할 수 있도록 만들기 위해 올바른 패턴들을 구현하는 것도 중요한 요구사항입니다. 마지막 단계는 뷰, 뷰 모델, 모델을 런타임에 인스턴스화하고 서로 연관시키는 방법을 고려하는 것입니다.


만약 당신이 응용프로그램에서 종속성 주입 컨테이너를 사용하고 있다면, 이 단계를 관리하기 위한 적절한 전략을 선택하는 것이 특히 중요합니다. MEF 와 유니티는 모두 뷰, 뷰 모델, 모델 사이의 종속성을 지정하기 위한 기능과 런타임에 컨테이너에 의해 그것들을 수행하도록 하는 기능을 제공합니다.


일반적으로, 당ㅅ니은 뷰 모델을 뷰에 대한 의존성으로 정의할 수 있습니다. 그래서 ( 컨테이너를 사용해 ) 뷰가 생성될 때, 그것은 자동적으로 요청된 뷰 모델을 인스턴스화합니다. 다음으로, 뷰 모델이 의존하는 모든 컴포넌트들과 서비스들도 컨테이너에 의해 인스턴스화될 것입니다. 뷰 모델이 성공적으로 생성된 후에는, 뷰가 그것을 자신의 데이터 칸텍스트로 설정하게 됩니다.



MEF 를 사용하여 뷰 와 뷰 모델을 생성하기


MEF 를 사용하면, 당신은 import 애트리뷰트를 사용하여 뷰 모델에 대한 뷰의 의존성을 지정할 수 있습니다. 그리고 export 애트리뷰트를 통해서 인스턴스화될 concrete 뷰 모델 타입을 지정할 수 있습니다. 당신은 프라퍼티를 통해서 뷰에 뷰 모델을 임포트하거나, 뷰 모델을 생성자의 인자로 넘길 수 있습니다.


예를 들어, MVVM RI 뷰에서 Questionnaire 는 뷰 모델을 위한 쓰기 전용 프라퍼티를 선언하며, import 애트리뷰트를 지정합니다. 뷰가 인스턴스화될 때, MEF 는 익스포트된 적절한 뷰 모델의 인스턴스를 생성하고, 프라퍼티 값을 설정합니다. 이 프러퍼티 세터는 뷰 모델을 뷰의 데이터 칸텍스트로 할당합니다. 아래에 그 예가 나와 있습니다.



뷰 모델은 아래와 같이 정의되고 익스포트됩니다.



노트:

MEF 와 유니티에서는 모두 프라퍼티 주입과 생성자 주입을 사용할 수 있습니다; 그러나 당신은 프라퍼티 주입이 더 단순하다는 것을 알게 될 것입니다. 왜냐하면 두 개의 생성자를 유지할 필요가 없기 때문입니다. Visual Studio 나 Expression Blend 와 같은 디자인-타임 툴은 컨틀롤들을 디자이너에 출력하기 위해서 컨트롤들이 파라미터 없는 기본 생성자를 가질 것을 요구합니다. 당신이 정의하는 부가적인 생성자들은 기본 생성자가 호출되는 것을 보장해야만 합니다. 그래야지 뷰가 InitializeComponent 메서드를 통해 적절하게 초기화될 수 있습니다.



유니티를 사용하여 뷰와 뷰 모델을 생성하기


종속성 주입 컨테이너로 유니티를 사용하는 것은 MEF 를 사용하는 것과 유사하며, 프라퍼티-기반 주입과 생성자-기반 주입이 모두 지원됩니다. 기본적인 차이는 보통 타입들이 런타임에 묵시적으로 검색되지 않는다는 것입니다; 그 대신에, 그것들은 컨테이너에 등록되어야만 합니다.


일반적으로 당신은 뷰 모델의 인터페이스를 정의하므로, 뷰 모델의 특정 concrete 타입이 뷰와 디커플링될 수 있습니다. 예를 들어, 뷰는 뷰 모델에 대한 자신의 의존성을 아래와 같이 생성자 인자를 통해서 정의할 수 있습니다.



노트:

파라미터 없는 기본 생성자는 뷰가 Visual Studio 나 Expression Blend 와 같은 디자인 타임 툴에서 작동할 수 있도록 허용하기 위해 필요합니다.


대안적으로, 당신은 뷰의 쓰기 전용 뷰 모델 프라퍼티를 아래와 같이 정의할수 있습니다. 유니티는 요청된 뷰를 인스턴스화할 것이고, 뷰가 인스턴스화된 이후에 프라퍼티 세터를 호출할 것입니다.



뷰 모델 타입은 유니티 컨테이너에 아래와 같이 등록됩니다.



그리고 나서 뷰는 컨테이너를 통해서 아래와 같이 초기화됩니다.




외부( external ) 클래스를 사용하여 뷰와 뷰 모델을 생성하기


보통, 당신은 뷰 및 뷰 모델의 인스턴스화를 조직하기 위해서 컨트롤러나 서비스를 정의하는 것이 유용합을 발견하게 될 것입니다. 이 접근법은 MEF 나 유니티와 같은 의존성 주입 컨테이너와 함께 사용될 수 있거나, 뷰가 명시적으로 자신이 요구하는 뷰 모델을 생성할 때 사용될 수 있습니다.


이 접근법은 응용프로그램에서 네비게이션을 구현할 때 특히 유용합니다. 이 경우에, 컨트롤러는 placeholder 컨트롤이나 UI 의 리전( region )과 연관되며, 그것은 뷰의 생성과 배치를 그 placeholder 나 리전으로 조직합니다.


예를 들어, MVVM RI 는 컨테이너를 사용하여 뷰를 생성하는 서비스 클래스를 사용하고, 그것들을 메인 페이지에서 보여 줍니다. 이 예제에서, 뷰는 뷰 이름에 의해 지정됩니다. 네비게이션은 아래와 같이 UI 서비스의 ShowView 메서드에 대한 호출을 통해 인스턴스화됩니다.



UI 서비스는 응용프로그램 UI 에 있는 placeholder 컨트롤에 연관됩니다; 그것은 요청된 뷰의 생성을 캡슐화하고, UI 에서의 외형을 조직화합니다. UIService 의 ShowView 는 아래와 같이 컨테이너를 통해서 뷰의 인스턴스를 생성하고( 그래서 그것의 뷰 모델과 다른 의존성들이 수행될 수 있다 ) 그것을 적절한 위치에 출력합니다.



노트:

프리즘은 리전을 사용하여 네비게이션을 위한 확장적인 지원을 제공하니다. 리전 네비게이션은 앞의 접근법과 매우 유사한 메커니즘을 사용하는데, 리전 관리자가 지정된 영역 내에 뷰의 인스턴스화와 배치를 조직화할 책임이 있다는 차이가 있습니다. 더 많은 정보를 원한다면, 챕터 8 "네비게이션" 의 "뷰-기반 네비게이션" 섹션을 참조하십시오.



MVVM 응용프로그램들을 테스트하기



중략...



More Information



For more information about the logical tree, see "Trees in WPF" on MSDN:
http://msdn.microsoft.com/en-us/library/ms753391.aspx


For more information about attached properties, see "Attached Properties Overview" on MSDN:
http://msdn.microsoft.com/en-us/library/cc265152(VS.95).aspx


For more information about MEF, see "Managed Extensibility Framework Overview" on MSDN:
http://msdn.microsoft.com/en-us/library/dd460648.aspx.


For more information about Unity, see "Unity Application Block" on MSDN:
http://www.msdn.com/unity.


For more information about DelegateCommand, see Chapter 5, "Implementing the MVVM Pattern."


For more information about using Microsoft Expression Blend behaviors, see "Working with built-in behaviors" on MSDN:

http://msdn.microsoft.com/en-us/library/ff724013(v=Expression.40).aspx.


For more information about creating custom behaviors with Microsoft Expression Blend, see "Creating Custom Behaviors" on MSDN:
http://msdn.microsoft.com/en-us/library/ff724708(v=Expression.40).aspx.


For more information about creating custom triggers and actions with Microsoft Expression Blend, see "Creating Custom Triggers and Actions" on MSDN:
http://msdn.microsoft.com/en-us/library/ff724707(v=Expression.40).aspx.


For more information about using the dispatcher in WPF and Silverlight, see "Threading Model" and "The Dispatcher Class" on MSDN:
http://msdn.microsoft.com/en-us/library/ms741870.aspx
http://msdn.microsoft.com/en-us/library/ms615907(v=VS.95).aspx.


For more information about unit testing in Silverlight, see "Unit Testing with Silverlight 2":
http://www.jeff.wilcox.name/2008/03/silverlight2-unit-testing/.


For more information about region navigation, see the section, "View-Based Navigation" in Chapter 8, "Navigation."


For more information about the Event-based Asynchronous pattern, see "Event-based Asynchronous Pattern Overview" on MSDN:
http://msdn.microsoft.com/en-us/library/wewwczdw.aspx


For more information about the IAsyncResult design pattern, see "Asynchronous Programming Overview" on MSDN:
http://msdn.microsoft.com/en-us/library/ms228963.aspx


'Programming > Prism4.1' 카테고리의 다른 글

MVVM 패턴 구현하기  (0) 2014.09.16
모듈러 응용프로그램 개발  (0) 2014.09.13
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
프리즘 응용프로그램 초기화하기  (0) 2014.08.24
왜 프리즘을 사용하는가?  (0) 2014.08.19
소개  (0) 2014.08.18
프리즘 4.1 설치  (0) 2014.08.14

프리즘 4.1 도움말 번역입니다.



MVVM 패턴 구현하기



모델-뷰-뷰모델( MVVM ) 패턴은 응용프로그램의 비즈니스 로직과 프리젠테이션 로직을 그것의 유저 인터페이스( UI )와 명확하게 분리하는 데 도움을 줍니다. 응용프로그램 로직과 UI 를 깔끔하게 분리하는 상태를 유지하는 것은 다양한 개발 및 설계 이슈들을 제시하는데 도움을 주며, 당신의 응용프로그래이 더 쉽게 테스트되고, 유지보수되고, 진화될 수 있도록 만들어 줄 수 있습니다. 또한 코드 재사용 가능성을 많이 증대시켜 줄 수 있으며, 개발자와 UI 디자이너가 응용프로그램에서 그들의 주요 관심사를 개발하는데 있어 더 쉽게 협력할 수 있도록 해 줍니다.


MVVM 패턴을 사용하면, 응용프로그램의 UI 와 기저에 깔린 프리젠테이션 로직 및 비즈니스 로직이 세 개의 분리된 클래스로 나뉩니다: 뷰는 UI 와 UI 로직을 캡슐화합니다; 뷰모델은 프리젠테이션 로직과 상태를 캡슐화합니다. 모델은 응용프로그램의 비즈니스 로직과 데이터를 캡슐화합니다.


프리즘은 실버라이트와 WPF 응용프로그램에서 MVVM 패턴을 구현하는 방법을 보여주는 샘플들과 참조 구현들을 포함하고 있습니다. 또한 프리즘 라이브러리는 당신의 응용프로그램에서 그 패턴을 구현하는데 도움을 줄 수 있는 기능들도 제공합니다. 이 기능들은 MVVM 패턴을 구현하기 위한 가장 공통적인 경험들을 포함하고 있으며, testability 를 지원하도록 설계되었으며, 익스프레션 블렌드와 비주얼 스튜디오에서 잘 동작하도록 설계되었습니다.


이 챕터는 MVVM 패턴에 대한 개요를 제공하며, 그것의 기본적인 특징들을 구현하는 방법에 대해 기술합니다. 챕터 6 은 프리즘 라이브러리를 사용해 더욱 진보된 MVVM 시나리오를 구현하는 방법을 기술합니다.



클래스의 책임과 특징들



MVVM 패턴은 프리젠테이션 모델( PM ) 패턴과 가까운 변종이며, 데이터 바인딩, 데이터 템플릿, 커맨드, 비헤이비어( behavior ) WPF 와 실버라이트의 핵심 기능들의 일부를 활용하기 위해서 최적화되어 있습니다.


MVVM 패턴에서, 뷰는 UI 와 UI 로직을 캡슐화하니다. 뷰 모델은 프리젠테이션 로직과 상태를 캡슐화합니다. 그리고 모델은 비즈니스 로직과 데이터를 캡슐화하니다. 뷰는 뷰 모델과 데이터 바인딩, 커맨드, 변경 통지 이벤트 등을 통해 상호작용합니다. 뷰 모델은 모델에 대한 업데이트를 질의하고 관찰하고 조직화하는데, 뷰의 출력에 필요한 형태로 데이터를 변환하고 유효화하고 그룹화합니다.


다음 그림은 세 개의 MVVM 클래스들과 그것들의 상호작용을 보여 줍니다.


MVVM 클래스들과 그것들의 상호작용




모든 개별 프리젠테이션 패턴들과 유사하게, MVVM 패턴을 사용하는 것의 핵심은 효율적으로 응용프로그램의 코드를 올바른 클래스들에 생성하는 적절한 방식을 이해하는데 달려 있으며, 이 클래스들이 다양한 시나리오에서 상호작용하는 방식을 이해하는 것에 달려 있습니다. 다음 섹션들에서는 MVVM 패턴의 각 클래스들의 책이과 특징들에 대해 기술합니다.



뷰 클래스


뷰의 책임은 스크린에서 사용자가 보게 되는 외형과 구조를 정의하는 것입니다. 이상적으로 볼 때, 뷰의 코드-비하인드는 단지 InitializeComponent 메서드를 호출하는 생성자만을 포함하는 것입니다. 어떤 경우에, 코드-비하인드는 복잡한 애니메이션과 같이 XAML 에서 표현하기 어렵거나 비효율적인 비주얼 비헤이비어를 구현하는 UI 로직 코드를 포함할 수 있습니다. 혹은 뷰의 일부인 비주얼 엘리번트들이 직접적으로 조작될 필요가 있는 코드가 있을 때 UI 로직 코드를 포함할 수 있습니다. 단위 테스트를 필요로 하는 뷰에 로직 코드를 배치해서는 안 됩니다. 일반적으로, 뷰의 코드-비하인드에 있는 로직 코드는 UI 오토메이션 테스팅 접근법을 통해 테스트될 수 있을 것입니다.


실버라이트와 WPF 에서, 뷰의 데이터 바인딩 표현식은 그것의 데이터 칸텍스트에 대해 평가됩니다. MVVM 에서, 뷰의 데이터 칸텍스트는 뷰 모델로 설정됩니다. 뷰 모델은 뷰 가 바인딩할 수 있는 프라퍼티나 커맨드를 구현합니다. 그리고 변경 통지 이벤트를 통해 어떠한 상태 변경을 뷰에 통지합니다. 일반적으로 뷰와 그것의 뷰 모델은 1 대 1 관계를 가집니다.


보통, 뷰는 Control 상속 클래스이거나 UserControl 상속 클래스입니다. 그러나, 어떤 경우에는, 뷰가 데이터 템플릿을 통해 표현될 수도 있습니다. 그것은 오브젝트가 출력될 때 오브젝트를 가시적으로 표현하기 위해서 사용되는 UI 엘리먼트들을 지정합니다. 데이터 템플릿을 사용하면, 비주얼 디자이너가 뷰 모델이 렌더링될 방식을 쉽게 결정하거나, 기반 오브젝트 자체를 변경하지 않고도 그것의 가시적인 표현이나 그것을 출력하기 위해서 사용될 컨트롤의 동작을 변경할 수 있습니다.


데이터 템플릿은 코드-비하인드를 가지지 않는 뷰라고 생각할 수 있습니다. 그것들은 UI 에서 오브젝트가 출력되기를 요청받을 때마다 특정 뷰 모델 타입을 바인딩하도록 설계되어 있습니다. 런타임에, 그 뷰는 데이터 템플릿에 정의되어 있는대로 자동적으로 인스턴스화될 것이며, 그것의 데이터 컨텍스트가 관련 뷰 모델로 설정될 것입니다.


WPF 에서, 당신은 데이터 템플릿을 응용프로그램 레벨에서 뷰 모델 타입과 연관시킬 수 있습니다. 그러면 WPF 는 오브젝트가 UI 에서 출력될 때마다 특정 타입의 뷰 모델 오브젝트들에 대해 자동적으로 데이터 템플릿을 적용하게 될 것입니다. 이는 묵시적 데이터 템플리팅이라고 알려져 있습니다. 실버라이트에서, 당신은 오브젝트를 출력할 컨트롤 안에 뷰 모델 오브젝트를 위한 데이터 템플릿을 명식적으로 지정해야만 합니다. 두 경우 모두, 데이터 템플릿은 그것을 사용하는 컨트롤 내부에 인라인으로 정의되거나, 부모 뷰의 외부의 리소스 딕셔너리에 정의되거나, 뷰의 리소스 딕셔너리에 선언적으로 병합될 수 있습니다.


요약하면, 뷰는 다음과 같은 핵심 특징들을 가집니다:

  • 뷰는 윈도우, 페이지, 유저 컨트롤, 데이터 템플릿과 같은 비주얼 엘리먼트입니다. 뷰는 뷰에 포함된 컨트롤들을 정의하고, 그것들의 비주얼 레이아웃과 스타일링을 정의합니다.
  • 뷰는 DataContext 프라퍼티를 통해 뷰 모델을 참조합니다. 뷰 내부의 컨트롤들은 그 프라퍼티들에 바인딩된 데이터이며, 뷰 모델에 의해 노출된 커맨드들입니다.
  • 뷰는 뷰와 뷰 모델 간의 데이터 바인딩 동작을 커스터마이즈할 수 있습니다. 예를 들어, 뷰는 value converter 를 사용해 데이터를 UI 에 출력할 형식으로 변환하거나, 유효화 규칙을 사용하여 사용자에게 입력 데이터에 대한 부가적인 유효성 검사를 제공할 수도 있습니다.
  • 뷰는 뷰 모델에서의 상태 변화나 UI 와 사용자의 인터랙션에 의해 발생된 애니메이션이나 트랜지션과 같은 UI 비주얼 비헤이비어를 정의하고 다룹니다.
  • 뷰의 코드-비하인드는 XAML 에서 표현하기 어려운 비주얼 비헤이비어를 구현하기 위해서 UI 로직을 정의하거나, 뷰에서 정의된 특정 UI 컨트롤들에 대한 직접적인 참조를 요청할 수도 있습니다.


뷰 모델 클래스


MVVM 패턴에서 뷰 모델은 프리젠테이션 로직과 뷰를 위한 데이터를 캡슐화합니다. 그것은 뷰에 대한 직접적인 참조나 뷰의 특정 구현이나 타입에 대한 정보를 가지고 있지 않습니다. 뷰 모델은 뷰가 바인드할 수 있는 프라퍼티와 커맨드를 구현합니다. 그리고 변경 통지 이벤트를 통해 어떠한 상태 변경을 뷰에 통지합니다. 뷰 모델이 제공하는 프라퍼티와 커맨드는 UI 에 의해 제공되는 기능을 정의하지만, 뷰는 그 기능이 렌더링되는 방식을 결정합니다.


뷰 모델은 뷰와 그것이 요구하는 모델 클래스들 간의 상호작용을 조직할 책임이 있습니다. 일반적으로 뷰 모델과 모델 클래스들 간에는 1 대 다 관계가 존재합니다. 뷰 모델은 뷰에 모델 클래스를 직접적으로 노출하는 것을 선택할 것이며, 그래서 뷰 내의 컨트롤들은 그것들에 직접적으로 데이터를 바인딩할 것입니다. 이 경우, 모델 클래스들은 데이터 바인딩과 적절한 변경 통지 이벤트들을 지원하도록 설계될 필요가 있을 것입니다. 이 시나리오에 대한 더 많은 정보를 원한다면, 이 토픽의 뒤에 나오는 데이터 바인딩 섹션을 참조하십시오.


뷰 모델은 모델 데이터를 변환하거나 조작하므로, 그것은 뷰에 의해 쉽게 소비될 수 있습니다. 뷰 모델은 뷰를 특별하게 지원하기 위해서 부가적인 프라퍼티들을 정의할 수 있습니다; 이 프라퍼티들은 보통은 모델의 일부가 아닙니다( 혹은 모델에 추가될 수 없습니다 ). 예를 들어, 뷰 모델은 두 필드의 값을 뷰에 제출하기 쉽게 만들기 위해서 결합하거나, 최대 길이를 가진 필드의 인풋을 위해 남은 문자열 개수를 계산할 수도 있습니다. 또한 뷰 모델은 데이터의 일관성을 보장하기 위해서 데이터 유효성 로직을 구현할 수도 있습니다.


그리고 뷰 모델은 뷰가 UI 의 가시적인 변경을 제공하는데 사용할 수 있는 논리적 상태들을 정의할 수도 있습니다. 뷰는 뷰 모델의 상태를 반영하는 레이아웃이나 스타일링을 정의할 것입니다. 예를 들어, 뷰 모델은 데이터가 비동기적으로 웹 서비스에 대해 제출되고 있음을 지시하는 상태를 정의할 수 있습니다. 뷰는 이 상태가 유지되는 동안에 사용자에게 가시적 피드백을 제공하기 위해서 애니메이션을 출력할 수 있습니다.


일반적으로, 뷰 모델은 UI 에서 표현될 수 있고 사용자가 호출할 수 있는 커맨드나 액션을 정의할 것입니다. 일반적인 예는 사용자가 데이터를 웹 서비스나 데이터 저장소로 제출할 수 있도록 해 주는 Submit 커맨드를 뷰 모델이 제공할 때입니다. 뷰는 그 커맨드를 버튼으로 표현하는 것을 선택할 것이며, 사용자는 데이터를 제출하기 위해서 그 버튼을 클릭할 수 있습니다. 일반적으로, 커맨드가 이용 가능한 상태가 아닐 때, 그것과 관련된 UI 표현은 비활성화됩니다. 커맨드들은 사용자 액션을 캡슐화하고, 그것을 UI 에서의 가시적인 표현과 명확하게 분리하는 방법을 제공합니다.


요약하면, 뷰 모델은 다음과 같은 핵심 특징들을 가집니다:

  • 뷰 모델은 비가시적인 크래스이며, WPF 나 실버라이트 기저 클래스를 상속하지 않습니다. 그것은 유스 케이스나 응용프로그램에서의 사용자 태스크를 지원하기 위해서 요구되는 프리젠테이션 로직을 캡슐화합니다. 뷰 모델은 뷰와 모델과는 별개로 테스트 가능합니다.
  • 뷰 모델은 일반적으로 뷰에 대한 직접적인 참조를 하지 않습니다. 그것은 뷰가 바인드할 수 잇는 프라퍼티와 커맨드들을 제공합니다. 그것은 INotifyPropertyChanged INotifyCollectionChanged 인터페이스를 통한 변경 통지 이벤트를 통해 뷰에 어떠한 상태 변경들을 통지합니다.
  • 뷰 모델은 뷰와 모델의 상호작용을 조직화합니다. 그것은 데이터를 변환하고 조작해, 데이터가 뷰에 의해 소비되기 쉬운 형태로 만들어질 수 있으며, 모델 상에서 제출되지 않는 부가적인 프라퍼티들을 구현할 수도 있습니다. 또한 IDataErrorInfo INotifyDataErrorInfo 인터페이스들을 통해 데이터 유효성을 구현할 수도 있습니다.
  • 뷰 모델은 뷰가 가시적으로 사용자에게 표현할 수 있는 논리적 상태를 정의할 수 있습니다.
노트:
뷰 혹은 뷰 모델?
특정 기능이 어디에서 구현되어야 하는지는 불명확합니다. 범용적인 경험 법칙에 따르면: 스크린상의 UI 의 특정 가시적 외형과 관련된 것들과 나중에 다시 스타일링될 수 있는( 심지어 당신이 현재는 다시 스타일링할 것을 계획하고 있지 않더라도 ) 것들은 뷰로 들어 가야 합니다; 응용프로그램의 논리적 행위에 있어 중요한 것들은 뷰 모델로 들어 가야 합니다. 부가적으로, 뷰 모델은 뷰의 특정 비주얼 엘리먼트에 대한 명시적인 정보를 가지고 있지 않기 때문에, 뷰에서 비주얼 엘리먼트들을 프로그램적인 방법으로 다루기 위한 코드는 뷰의 코드 비하인드에 존재하거나 비헤이비어에 캡슐화되어야 합니다. 이와 유사하게, 데이터 바인딩을 통해 뷰에서 출력될 데이터 아이템들을 조작하고 획득하는 코드는 뷰 모델에 존재해야 합니다.
예를 들어, 리스트 박스에서 선택된 아이템에 대한 하이라이트 컬러는 뷰에 정의되어야 하지만, 출력할 아이템들의 리스트와 선택된 아이템 자체에 대한 참조는 뷰 모델에 의해 정의되어야 합니다.



모델 클래스


MVVM 패턴에서 모델은 비즈니스 로직과 데이터를 캡슐화합니다. 비즈니스 로직은 응용프로그램 로직으로 정의되는데, 이는 응용프로그램 데이터에 대한 획득과 관리와 관련이 있습니다. 그리고 비즈니스 룰이 데이터의 일관성과 유효성을 보장하도록 하는 것을 내포합니다. 재사용성의 기회를 최대화하기 위해서, 모델은 어떠한 유스 케이스-지정 혹은 유저 태스크-지정 행위나 응용프로그램 로직을 포함해서는 안 됩니다.


일반적으로, 모델은 응용프로그램을 위한 클라이언트측 도메인 모델을 표현합니다. 그것은 응용프로그램의 데이터 모델과 비즈니스 로직 및 유효성 로직을 지원하는 것에 기반한 데이터 구조를 정의할 수 있습니다.이 모델은 데이터 접근이나 캐싱을 지원하기 위한 코드를 포함할 수도 있는데, 이는 보통 이를 위해 사용되는 분리된 데이터 저장소나 서비스를 통해서 이루어집니다. 보통, 모델과 데이터 접근 레이어는 ADO.NET Entity Framework, WCF Data Services, WCF RIA Services 와 같은 데이터 접근 혹은 서비스 전략의 일부로서 생성됩니다.


일반적으로, 모델은 뷰에 바인딩되기 쉽도록 해 주는 기능들을 구현합니다. 이는 보통 그것이 INotifyPropertyChanged INotifyCollectionChanged 인터페이스들을 통해서 프라퍼티 변경 통지나 컬렉션 변경 통지를 지원한다는 것을 의미합니다. 오브젝트 컬렉션을 표현하는 모델들 클래스들은 보통 ObservableCollection<T> 클래스를 상속하는데, 이 클래스는 INotifyCollectionChanged 인터페이스에 대한 구현을 제공합니다.


모델은 IDataErrorInfo( 혹은 INotifyDataErrorInfo ) 인터페이스들을 통해 데이터 유효화나 데이터 리포팅을 지원할 수도 있습니다. 이 인터페이스들은 WPF 나 실버라이트의 데이터 바인딩으로 하여금 값이 변경될 때를 통지받을 수 있도록 해서, UI 가 갱신될 수 있도록 해 줍니다. 또한 그것들은 UI 레이어에서 데이터 유효화나 에러 리포팅을 지원할 수 있도록 합니다.


노트:

당신의 모델 클래스들이 요청된 인터페이스를 구현하지 않는다면?

가끔 당신이 INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo, INotifyDataErrorInfo 인터페이스를 구현하지 않는 모델 오브젝트를 가지고 작업하게 될 경우가 있을 것입니다. 그런 경우, 뷰 모델은 모델 오브젝트들을 래핑할 필요가 있을 것이며, 요청된 프라퍼티들을 뷰에 노출할 필요가 있을 것입니다. 이 프라퍼티들의 값들은 모델 오브젝트들에 의해 직접적으로 제공될 것입니다. 뷰 모델은 그것이 노출하는 프라퍼티들을 위해 요청된 인터페이스들을 구현할 것이며, 그래야지 뷰가 쉽게 그것들을 데이터 바인딩할 수 있을 것입니다.


모델은 다음과 같은 핵심 특징들을 가지고 있습니다:

  • 모델 클래스들은 응용프로그램의 데이터 로직 및 비즈니스 로직을 캡슐화하는 비가시적 클래스들입니다. 그것들은 응용프로그램의 데이터들을 관리하고 요청된 비즈니스 룰과 데이터 유효성 로직을 캡슐화함으로써 그 데이터의 일관성과 유효성을 보장할 책임을 가집니다.
  • 모델 클래스들은 뷰나 뷰 모델에 대한 직접적인 참조를 가지지 않습니다. 그리고 그것들이 구현되는 방식에 대한 종속성을 가지지 않습니다. 
  • 모델 클래스들은 일반적으로 프라퍼티 변경 통지 이벤트 및 컬렉션 변경 통지 이벤트들을 제공하는데, 이는 INotifyPropertyChanged 인터페이스와 INotifyCollectionChanged 인터페이스를 통해 지원됩니다. 이는 그것들이 뷰에 쉽게 데이터 바인딩되도록 해 줍니다. 오브젝트에 대한 컬렉션을 표현하는 모델 클래스들은 ObservableCollection<T> 클래스를 상속합니다.
  • 모델 클래스들은 일반적으로 IDataErrorInfo INotifyDataErrorInfo 인터페이스를 통해 데이터 유효화나 에러 리포팅을 제공합니다.
  • 모델 클래스들은 일반적으로 데이터 접근 및 캐싱을 캡슐화하는 서비스나 저장소와 함께 사용됩니다.


클래스 상호작용들



MVVM 패턴은 응용프로그램의 사용자 인터페이스, 프리젠테이션 로직, 그리고 비즈니스 로직과 데이터를 분리된 클래스로 나눔으로써 그것들 간의 명확한 분리를 제공합니다. 그러므로, 당신이 MVVM 을 구현할 대, 응용프로그램의 코드에서 이전 섹션에서 기술했던 것처럼 정확한 클래스들을 생성하는 것이 중요합니다.

잘 설계된 뷰, 뷰 모델, 모델 클래스들은 코드 및 행위의 정확한 타입만을 생성하는 것은 아닙니다; 그것들은 데이터 바인딩, 커맨드, 데이터 유효성 인터페이스들을 통해 서로 쉽게 상호작용할 수 있도록 설계되기도 합니다.

뷰와 그것의 뷰 모델 간의 상호작용은 아마도 고려해야 할 가장 중요한 것일 것입니다만, 모델과 뷰 모델 간의 상호작용도 중요합니다. 다음 섹션들은 이러한 상호작용들을 위한 다양한 패턴들을 기술하며, 응용프로그램에서 MVVM 패턴을 구현할 때 그것들을 설계하는 방법에 대해서 기술합니다.


데이터 바인딩

데이터 바인딩은 MVVM 패턴에서 매우 중요한 역할을 수행합니다. WPF 와 실버라이트는 모두 강력한 데이터 바인딩 기능을 제공합니다. 당신의 뷰 모델과 당신의 뷰 모델 클래스들은 ( 이상적으로는 ) 데이터 바인딩을 지원해서 그것들이 이러한 기능을의 이점을 취할 수 있도록 설계되어야 합니다. 일반적으로, 이는 그것들은 올바른 인터페이스들을 구현해야만 한다는 것을 의미합니다.

실버라이트와 WPF 의 데이터 바인딩은 다중 데이터 바인딩 모드를 지원합니다. 단방향( one-way ) 데이터 바인딩을 사용하며, UI 컨틀롤들이 뷰 모델에 바인딩될 수 있으며, 그래서 그것들은 디스플레이가 렌더링될 때 기저 데이터의 값을 반영합니다. 양방향( two-way ) 데이터 바인딩은 UI 에서 사용자가 데이터를 수정할 때 데이터를 자동적으로 갱신합니다.

뷰 모델에서 데이터가 변경될 때 UI 가 최신상태를 유지하도록 하기 위해서는, 적절한 변경 통지 인터페이스를 구현해야 합니다. 만약 그것이 데이터 바인딩될 수 있는 프라퍼티들을 정의한다면, 그것은 INotifyPropertyChanged 인터페이스를 구현해야 합니다. 만약 뷰 모델이 컬렉션을 표현한다면, 그것은 INotifyCollectionChanged 인터페이스를 구현하거나, 이 인터페이스에 대한 구현을 제공하는 ObservableCollection<T> 클래스를 상속해야 합니다. 이 인터페이스들은 모두 기저 데이터가 변경될 때 발생하는 이벤트를 정의합니다. 데이터가 바인딩된 컨트롤들은 이벤트가 발생될 때 자동적으로 갱신될 것입니다.

많은 경우에, 뷰 모델은 오브젝트들을 반환하는 프라퍼티들을 정의할 것입니다( 그리고 순서대로 부가적인 오브젝트들을 반환하는 프라퍼티들일 수도 있습니다 ). WPF 와 실버라이트에서 데이터 바인딩은 Path 프라퍼티를 통해서 내부 프라퍼티들에 대한 바인딩을 지원합니다. 그러므로, 뷰의 뷰 모델을 위해 다른 뷰 모델이나 모델에 대한 참조를 반하는 것이 매우 일반적인 일입니다. 뷰에 전근할 수 있는 뷰 모델과 모델은  INotifyPropertyChanged INotifyCollectionChanged 인터페이스를 적절히 구현해야 합니다.


다음 섹션들은 MVVM 에서 데이터 바인딩을 지원하기 위해서 요구되는 인터페이스들을 구현하는 방법에 대해 기술합니다.



INotifyPropertyChanged 구현하기


뷰 모델이나 모델 클래스에서 INofityPropertyChanged 인터페이스를 구현하는 것은, 기저 프라퍼티 값이 변경될 때 그것들이 뷰에 있는 데이터 바인딩된 컨트롤들에 변경 통지들을 제공할 수 있도록 해 줍니다.  이 인터페이스를 구현하는 것은 직관적입니다. 아래에 코드 샘플이 있습니다( MVVM QuickStart 의 Questionnaire 클래스를 참조하십시오 ).



INotifyPropertyChanged 인터페이스를 많은 뷰 모델 클래스들에서 구현하는 것은 반복적이고 에러 가능성이 높습니다. 왜냐하면 이벤트 파라미터에 프라퍼티 이름을 지정할 필요가 있기 때문입니다. 프리즘 라이브러리는 뷰 모델 클래스에서 상속할 수 있는 편리한 기저 클래스를 제공하는데, 이는 INotifyPropertyChanged 인터페이스를 형-안정적인 방식으로 제공합니다. 그 예가 아래에 나와 있습니다.



이를 상속한 뷰 모델은 RaisePropertyChanged 를 프라퍼티 이름을 지정해 호출하거나 프라퍼티를 참조하는 람다 표현식을 사용해서 호출함으로써 프라퍼티 변경 이벤트를 발생시킬 수 있습니다. 그 예가 아래에 나와 있습니다.



노트:

람다 표현식을 사용할 때, 이 방식은 약간의 성능 비용을 포함합니다. 왜냐하면 람다 표현식은 각 호출마다 평가되어야만 하기 때문입니다. 이러한 접근법의 이점은 당신이 프라퍼티의 이름을 변경했더라도 컴파일 타임 타입 안정성 및 리팩토링 지원을 제공한다는 것입니다. 성능 부하가 작고 당신의 응용프로그램에 대해 거의 영향을 주지 않는다고 하지만, 많은 변경 통지들이 발생한다면 이 비용들이 누적됩니다. 이 경우, 람다 메서드를 사용하지 않는 방식을 사용하는 것을 고려해야 합니다.


보통, 당신의 모델이나 뷰 모델은 모델이나 뷰 모델의 다른 프라퍼티들로부터 계산되는 값들을 가진 프라퍼티들을 포함할 것입니다. 프라퍼티들에 대한 변경을 다룰 때, 계산된 프라퍼티들에 대한 통지 이벤트들도 발생될 수 있도록 하십시오.



INotifyCollectionChanged 구현하기


당신의 뷰 모델이나 모델이 아이템들의 집합을 표현하거나 아이템들의 집합을 반환하는 하나 이상의 프라퍼티들을 가지고 있을 수 있습니다. 이러한 경우에, 당신은 ListBox DataGrid 컨트롤과 같은 ItemsControl 에 그 컬렉션들을 출력하기를 원할 것입니다. 컬렉션을 표현하는 뷰 모델이나 컬렉션을 반환하는 프라퍼티들은 ItemSource 프라퍼티를 통해 데이터 바인딩될 수 있습니다.



변경 통지 요청들을 적절하게 지원하기 위해서, 뷰 모델이나 모델은 그것들이 컬렉션을 표현하고 있을 때 INofityCollectionChanged 인터페이스를 구현해야 합니다( 부가적으로 INotifyPropertyChanged 인터페이스를 구현할 수도 있습니다 ). 만약 뷰 모델이나 모델이 컬렉션에 대한 참조를 반환하는 프라퍼티를 정의한다면, 반환되는 컬렉션 클래스는 INotifyCollectionChanged 인터페이스를 구현해야 합니다.


그러나 INofityCollectionChanged 인터페이스를 구현하는 것은 어려운 일이 될 수 있습니다. 왜냐하면 그것은 컬렉션 내에서 아이템들이 추가되거나, 삭제되거나, 변경될 때에 대한 통지를 제공해야 하기 때문입니다. 그 인터페이스를 직접적으로 구현하기보다는, 보통 그것을 이미 구현해 놓은 컬렉션 클래스로부터 상속을 받거나 그 클래스를 사용하는 것이 더 쉽습니다. ObservableCollection<T> 클래스는 이 인터페이스에 대한 구현을 제공하고, 기저 클래스로 사용되거나 아이템의 컬렉션을 표현하는 프라퍼티를 구현하는데도 사용됩니다.


만약 데이터 바인딩을 위해서 뷰에 컬렉션을 제공할 필요는 있지만, 사용자의 선택을 트래킹하거나 컬렉션 내에서의 필터링, 정렬, 그룹화를 지원할 필요가 없다면, 당신은 그냥 단순하게 당신의 뷰 모델상의 프라퍼티가 ObservableCollection<T> 인스턴스를 반환하도록 정의하면 됩니다.



만약 컬렉션 클래스에 대한 참조를 획득한다면( 예를 들어, INotifyCollectionChanged 를 구현하지 않는 다른 컴포넌트나 서비스로부터 참조를 획득한다면 ), 당신은 보통 그 컬렉션을 ObservableCollection<T> 인스턴스의 컬렉션으로 래핑할 수 있으며, 이는 IEnuerable<T>List<T> 파라미터를 취하는 생성자 중의 하나를 사용합니다.



ICollectionView 구현하기


이전 코드 예제들은 뷰에 데이터 바인딩된 컨트롤들을 통해 출력될 수 있는 아이템에 대한 컬렉션들을 반환하는 단순한 뷰 모델 프라퍼티들을 구현하는 방법을 보여 줍니다. ObservableCollection<T> 클래스는 INofityCollectionChanged 인터페이스를 구현하기 때문에, 뷰의 컨트롤들은 자동적으로 갱신되어 아이템들이 추가되거나 삭제될 때마다 컬렉션의 현재 아이템 리스트를 반영할 것입니다.


그런데, 당신은 종종 뷰에 아이템 컬렉션이 출력되는 방식을 좀 더 잘 제어하고 싶다거나 출력된 아이템 컬렉션과 사용자의 상호작용을 트래킹하고 싶다거나, 그러한 동작을 뷰 모델 자체에서 수행하고 싶을 때가 있을 것입니다. 예를 들어, 당신은 아이템 컬렉션이 뷰 모델에서 제공하고 있는 프리젠테이션 로직에 따라 필터링되고 정렬되기를 원하거나, 뷰에서 현재 선택되어 있는 아이템에 대한 트랙을 유지하여 뷰 모델에서 구현된 커맨드들이 현재 선택된 아이템에 대한 동작을 수행할 수 있도록 할 필요가 있을 것입니다.


WPF 와 실버라이트는 ICollectionView 인터페이스를 구현하는 다양한 클래스들을 제공함으로써 이러한 시나리오들을 지원합니다. 이 인터페이스는 컬렉션이 필터링되고, 정렬되고, 그룹화되는 것을 허용하는 프라퍼티들과 메서드들을 지원합니다. 그리고 현재 선택된 아이템들이 트래킹 되고 변경되는 것을 허용합니다. 실버라이트와 WPF 는 모두 이 인터페이스의 구현을 제공합니다 - 실버라이트는 PagedCollectionView 클래스를 제공하고, WPF 는 ListCollectionView 를 제공합니다.


컬렉션 뷰 클래스는 기저 아이템 컬렉션을 래핑하여 그것들이 자동적으로 그것들에 대한 트래킹, 정렬, 필터링, 페이징을 제공할 수 있도록 합니다. 이 클래스들의 인스턴스는 CollectionViewSource 클래스를 사용하여 XAML 안에서 선언적으로 생성되거나 프로그램적으로 생성될 수 있습니다.


노트:

WPF 에서는 컬렉션에 컨트롤이 바인딩될 때마다 자동적으로 기본 컬렉션 뷰가 생성될 것입니다. 실버라이트에서는 바인딩된 컬렉션이 ICollectionViewFactory 인터페이스를 지원할 때만 자동적으로 컬렉션 뷰가 생성될 것입니다.


컬렉션 뷰 클래스는 기저 컬렉션에 대한 중요한 상태 정보에 대한 트랙을 유지하기 위해서 뷰 모델에 의해 사용될 수 있습니다. 반면에 뷰의 UI 와 모델의 기저 데이터 사이에서의 명확한 관심사 분리가 유지됩니다. 실제적으로, CollectionView 들은 특별히 컬렉션들을 지원하기 위해서 설계된 뷰 모델입니다.


그러므로, 당신이 뷰 모델에서 컬렉션의 아이템들에 대한 필터링, 정렬, 그룹화, 선택 트래킹을 구현할 필요가 있다면, 당신의 뷰 모델은 뷰에 노출되는 각 컬렉션에 대해 컬렉션 뷰 클래스의 인스턴스를 생성해야만 합니다. 그리고 나서 뷰 모델 내의 컬렉션 뷰 클래스에 의해 제공되는 메서드를를 사용해 CurrentChanged 이벤트와 같은 선택 변경 이벤트나 컨트롤 필터링, 정렬, 그룹화에 대한 이벤트들을 구독해야 합니다.


뷰 모델은 ICollectionView 참조를 반환하는 읽기 전용 프라퍼티를 구현해서 뷰의 컨트롤들이 컬렉션 뷰 오브젝트를 데이터 바인딩하고 그것들과 상호작용할 수 있도록 해야 합니다. ItemsControl 을 상속한 모든 WPF 및 실버라이트 컨트롤들은 자동적으로 ICollectionView 클래스들과 상호작용합니다.


다음 코드 예제는 실버라이트에서 PagedCollectionView 를 사용하여 현재 선택된 고객의 트랙을 유지하는 것을 보여 줍니다.



그리고 나서 당신은 뷰에서 ListBox 와 같은 ItemsControl 에 뷰 모델의 Customers 프라퍼티를 바인딩할 수 있는데, 이는 컨트롤의 ItemsSource 프라퍼티를 통해서 이루어 집니다. 그 예는 아래와 같습니다.



사용자가 UI 에서 고객을 선택할 때, 뷰 모델은 그 정보를 전달받을 것이며, 그것은 현재 선택된 고객과 관련된 명령들을 수행할 수 있습니다. 또한 뷰 모델은 컬렉션 뷰의 메서드를 호출함으로써 UI 에서의 현재 선택을 프로그램적으로 변경할 수도 있습니다. 그 예가 아래에 나와 있습니다.



컬렉션 뷰에서 선택이 변경될 때, 아이템의 선택 상태를 가시적으로 표현하기 위해서 UI 가 자동적으로 갱신됩니다. 이 구현은 WPF 에서도 유사한데, 앞의 예제에서의 PagedCollectionView 가 보통 ListCollectionView BindingListCollectionView 로 대체된다는 차이가 있기는 합니다. 그 예가 아래에 나와 있습니다.




커맨드들


뷰에서 출력되고 편집될 데이터에 대한 접근을 제공하는 것과 별개로, 뷰 모델은 사용자에 의해서 수행될 하나 이상의 동작이나 연산을 정의할 것입니다. WPF 와 실버라이트에서, UI 를 통해 사용자가 수행할 수 있는 동작이나 연산은 일반적으로 커맨드로서 정의됩니다. 커맨드들은 UI 의 컨트롤에 쉽게 바인딩될 수 있는 동작이나 연산들을 표현하는 편리한 방식을 제공합니다. 그것들은 그 동작이나 연산들을 구현하는 실제 코드를 캡슐화하고, 그 동작이나 연산들이 뷰에서의 실제 가시적 표현들과 분리될 수 있도록 도와 줍니다.


커맨드들은 그것들이 뷰와 상호작용할 때 다른 방식으로 가시적으로 표현되거나 사용자에 의해 실행될 수 있습니다. 대부분의 경우, 그것들은 마우스 클릭의 결과로서 실행되지만, 그것들은 터치 제스쳐나 다른 입력 이벤트나 단축키 누름의 결과로 실행될 수도 있습니다. 뷰의 컨트롤들은 뷰 모델의 커맨드들에 데이터 바인딩되므로, 사용자는 컨트롤이 정의하는 입력 이벤트나 제스쳐를 사용해서 그것들을 실행할 수 있습니다. 뷰의 UI 컨트롤과 커맨드들 사이의 상호작용은 양방향입니다. 이 경우, 커맨드는 사용자가 UI 와 상호작용할 때 실행될 수 있습니다. 그리고 기저 커맨드가 활성화되거나 비활성화되는 것에 따라, UI 가 자동적으로 활성화되거나 비활성화됩니다.


뷰 모델은 커맨드를 커맨드 메서드나 커맨드 오브젝트로서 구현할 수 있습니다( 커맨드 오브젝트는 ICommand 인터페이스를 구현합니다 ). 두 경우 모두, 뷰의 코드 비하인드 파일 내에서의 복잡한 이벤트 처리 코드를 요구하지 않고도 뷰와 커맨드의 상호작용이 선언적으로 정의될 수 있습니다. 예를 들어, WPF 와 실버라이트의 특정 컨트롤들은 본질적으로 커맨드들을 지원하며, 뷰 모델에 의해서 제공되는 ICommand 오브젝트에 데이터 바인딩될 수 있는 Command 라는 프라퍼티를 제공합니다. 이 외의 경우에, 뷰 모델에 의해 제공되는 커맨드 메서드나 커맨드 오브젝트를 컨트롤에 연관시키기 위해서 커맨드 비헤이비어가 사용될 수 있습니다.


노트:

비헤이비어들은 강력하고 유연한 확장성 메커니즘으로 상호작용 로직이나 동작을 캡슐화합니다. 이는 뷰의 컨트롤들과 선언적으로 연관될 수 있습니다. 커맨드 비헤이비어는 커맨드 오브젝트나 커맨드 메서드를 컨트롤과 연관시키는데 사용될 수 있는데, 해당 컨트롤이 커맨드들과의 상호작용을 위해서 특별히 설계되어 있는 것은 아닙니다.


다음 섹션들에서는 뷰를 위해 커맨드 메서드나 커맨드 오브젝트로서 커맨드를 구현하는 방법을 기술하며, 뷰에서 그것들을 컨트롤들과 연관시키는 방법들에 대해 기술합니다.



커맨드 오브젝트들을 구현하기


커맨드 오브젝트는 ICommand 인터페이스를 구현하는 오브젝트입니다. 이 인터페이스는 Execute 메서드를 정의하는데, 이는 연산 자체를 캡슐화합니다. 그리고 CanExecute 메서드를 제공하는데, 이는 커맨드가 특정 시점에 실행될 수 있는지 여부를 가리킵니다. 이 두 메서드는 모두 파라미터로 커맨드를 받습니다. 커맨드 오브젝트 내에서 연산을 위한 구현 로직을 캡슐화한다는 것은 그것이 좀 더 쉽게 단위 테스트를 위해 사용되거나 유지보수될 수 있다는 것을 의미합니다.


ICommand 인터페이스를 구현하는 것은 직관적입니다. 그러나, 응용프로그램에서 쉽게 사용할 수 있도록 인터페이스를 구현하는 방법은 여러 가지가 있습니다. 예를 들어 Expression Blend SDK 의 ActionCommand 클래스를 사용하거나 프리즘이 제공하는 DelegateCommand 클래스를 사용할 수 있습니다.


프리즘의 DelegateCommand 클래스는 뷰 모델 클래스 내에서 구현된 메서드를 각각 참조하는 두 개의 대리자( delegate ) 를 캡슐화합니다. 이는 DelegateCommandBase 클래스를 상속하고, DelegateCommandBase ICommand 인터페이스의 Execute CanExecute 메서드를 구현하는데, 여기에서는 앞의 대리자들을 호출하도록 하고 있습니다. 당신은 DelegateCommand 클래스 생성자에서 뷰 모델 메서드들에 대한 대리자들을 지정하면 됩니다. 그 예가 아래에 나와 있습니다.



예를 들어, 다음 코드 예제는 Submit 커맨드를 표현하는 DelegateCommand 인스턴스가 OnSubmit 와 CanSubmit 뷰 모델 메서드에 대한 대리자를 지정함으로써 생성되는 방식에 대해서 보여 줍니다. 그리고 나서 그 커맨드는 ICommand 에 대한 참조를 반환하는 읽기 전용 프라퍼티를 통해서 뷰에 노출됩니다.



Execute 메서드가 DelegateCommand 오브젝트에서 호출되면, 그것은 당신이 생성자에서 지정했던 대리자를 통해서 당신의 뷰 모델 클래스의 메서드에 대한 호출을 포워딩할 뿐입니다. 이와 유사하게, CanExecute 메서드가 호출되면, 당신의 뷰 모델 클래스의 관련 메서드가 호출됩니다. 생성자에서 CanExecute 메서드에 대한 대리자는 선택적입니다. 만약 대리자가 지정되지 않으면, DelegateCommand CanExecute 에 대해 항상 true 를 반환할 것입니다.


DelegateCommand 클래스는 저네릭( generic ) 타입입니다. 이 타입 파라미터는 Execute CanExecute 메서드에 넘겨질 커맨드 파라미터의 타입을 지정합니다. 앞의 예제에서, 커맨드 파라미터는 object 타입입니다. 프리즘은 커맨드 파라미터가 요구되지 않을 때 사용할 DelegateCommand 클래스의 비-제너릭 버전도 제공합니다.


뷰 모델은 RaiseCanExecuteChanged 메서드를 DelegateCommand 오브젝트 상에서 호출함으로써 커맨드의 CanExecute 상태의 변경을 지정할 수 있습니다. 이는 CanExecuteChanged 이벤트가 발생하도록 만듭니다. 커맨드에 바인딩된 모든 UI 컨트롤들은 바인딩된 커맨드의 이용 가능 상태를 반영하기 위해서 활성화 상태를 갱신할 것입니다.


ICommand 인터페이스에 대한 다른 구현도 가능합니다. Expression Blend SDK 가 제공하는 ActionCommand 클래스는 프리즘의 DelegateCommand 클래스와 유사합니다. 그러나 그것은 단지 Execute 메서드 대리자만을 지원합니다. 프리즘은 CompositeCommand 클래스도 제공하는데, 이는 DelegateCommand 들이 실행을 위해 서로 그룹화되는 것을 허용합니다. CompositeCommand 클래스를 사용하는 것과 관련된 더 많은 정보를 원한다면, 챕터 6 "진보된 MVVM 시나리오들" 의 "복합 커맨드들" 항목을 찹조하십시오.



뷰에서 커맨드 오브젝트들을 실행하기


뷰의 컨트롤들이 뷰 모델에 의해 제공되는 커맨드 오브젝트들과 연관되는 여러 가지 방식이 존재합니다. 특정 WPF 및 실버라이트 4 컨트롤들은, 특히 Button 이나 RadioButton 과 같은 ButtonBase 를 상속한 클래스들이나 Hyperlink MenuItem 을 상속하는  클래스들은,  Command 프라퍼티를 통해서 커맨드 오브젝트에 쉽게 데이터 바인딩될 수 있습니다. WPF 는 KeyGesture 에 뷰 모델의 ICommand 를 바인딩하는 것도 지원합니다.



커맨드 파라미터는 CommandParameter 프라퍼티를 사용하여 선택적으로 제공될 수도 있습니다. 기대되는 파라미터의 타입은 Execute CanExecute 타깃 메서드들에서 지정됩니다. 컨트롤은 사용자가 컨트롤과 상호작용할 때 자동으로 타깃 커맨드를 실행할 것이며, 만약 커맨드 파라미터가 제공되어 있다면, 그것이 커맨드의 Execute 메서드에 파라미터로 전달될 것입니다. 앞의 예제에서, 버튼은 그것이 클릭될 때 자동으로 SubmitCommand 를 실행할 것입니다. 부가적으로 CanExecute 핸들러가 지정되어 있다면, CanExecute false 를 반환할 때 버튼은 자동으로 비활성화될 것이고, 그것이 true 를 반환할 때 버튼은 자동으로 활성화될 것입니다.


대안 접근법은 Expression Blend 상호작용 트리거들과 InvokeCommandAction 비헤이비어를 사용하는 것입니다.




이 접근법은 상호작용 트리거를 붙일 수 있는 모든 컴트롤을 위해서 사용될 수 있습니다. 그것은 당신이 ButtonBase 를 상속하지 않은 컨트롤에 커맨드를 붙이고자 할 때나, 클릭 이벤트가 아닌 다른 이벤트에서 커맨드를 실행하고자 할 때 특히 유용합니다. 또한 당신이 커맨드를 위한 파라미터를 제공할 필요가 있다면, CommandParameter 프라퍼티를 사용할 수 있습니다.


커맨드에 직접적으로 바인딩될 수 있는 컨트롤들과는 다르게, InvokeCommandAction 은 커맨드의 CanExecute 값들에 기반해 자동적으로 컨트롤을 활성화하거나 비활성화하지는 않습니다. 이 동작을 구현하기 위해서는, 컨트롤의 IsEnabled 프라퍼티를 직접적으로 뷰 모델의 적절한 프라퍼티에 데이터 바인딩해야 합니다. 그 예는 앞에 나와 있습니다.


노트:

커맨드 이용 가능한 컨트롤들 대 비헤이비어들

커맨드를 지원하는 WPF 및 실버라이트 4 컨트롤들은 당신이 선언적으로 컨트롤에 커맨드를 연결할 수 있도록 해 줍니다. 이러한 컨트롤들은 사용자가 컨트롤과 특정 방식으로 상호작용할 때 특정 커맨드를 실행합니다. 예를 들어, Button 컨트롤에 대해, 커맨드는 사용자가 버튼을 클릭할 때 실행됩니다. 커맨드와 연결된 이 이벤트는 고정되어 있으며 변경될 수 없습니다. 또한 비헤이비어들은 선언적인 형태로 커맨드에 컨트롤을 연결할 수 있도록 해 줍니다. 그러나, 비헤이비어들은 컨트롤에 의해 발생하는 이벤트들의 범주에 연관될 수 있으며, 그것들은 뷰 모델의 관련 커맨드 오브젝트나 커맨드 메서드들을 선택적으로 실행하는데 사용될 수 있습니다. 다시 말해, 비헤이비어들은 커맨드 이용 가능한 컨트롤들과 유사한 많은 시나리오들을 가능하게 할 수 있으며, 그것들은 훨씬 많은 수준의 유연성과 제어를 제공할 것입니다.

당신은 커맨드 이용 가능한 컨트롤들을 사용할 때와 비헤이비어를 사용할 때를 선택해야 할 것입니다. 또한 어떤 종류의 비헤이비어를 사용할지도 선택해야 합니다. 만약 당신이 뷰의 컨트롤들을 뷰 모델의 기능과 연관시키기 위한 단일한 메커니즘을 선호하거나 일관성있는 단일 메커니즘을 원한다면, 컨트롤들이 본질적으로 커맨드를 지원하고 있더라도 비헤이비어를 사용하는 것을 고려해야 합니다.

뷰 모델에서 커맨드를 실행하기 위해서 커맨드 이용 가능한 컨트롤을 사용할 필요가 있을 때만, 그리고 커맨드를 실행하기 위해 기본 이벤트들을 사용하는 것이 행복할 때만, 비헤이비어가 요구되지 않을 것입니다.

이와 유사하게, 만약 당신이 Expression Blend 를 사용하지 않는 개발자나 UI 설계자라면, 당신은 커맨드 이용 가능한 컨트롤들( 혹은 custom attached behaviors )을 사용하는 것을 선호하게 될 것입니다. 왜냐하면 Expression Blend 비헤이비어들을 위해서 부가적인 구문들이 요구되기 때문입니다.



뷰에서 커맨드 메서드들을 실행하기


ICommand 오브젝트로서 커맨드들을 구현하는 것에 대한 대안 접근법은, 그것들을 단순히 뷰 모델의 메서드로서 구현하고, 뷰에서 그 메서드들을 직접적으로 호출하기 위해서 비헤이비어들을 사용하는 것입니다.


이는 이전 섹션에서 보여주었던 것처럼 비헤이비어로부터 커맨드들을 실행하는 것과 유사항 방식으로 수행됩니다. 그러나, InvokeCommandAction 을 사용하는 대신에, CallMethodAction 을 사용하게 됩니다. 다음 예제 코드는 기저 뷰 모델의 ( 파라미터 없는 ) Submit 메서드를 호출하는 것을 보여 줍니다.



TargetObject {Binding} 표현식을 통해 ( 뷰 모델을 의미하는 ) 기저 데이터 칸텍스트에 바인딩됩니다. Method 파리미터는 실행될 메서드를 지정합니다.


노트:

CallMethodAction 은 파라미터가 아닙니다; 만약 타깃 메서드에 파라미터를 넘길 필요가 있다면, 뷰 모델의 프라퍼티들을 값으로서 제공해야만 하며, InvokeCommandAction 으로 커맨드를 바꾸거나 당신만의 파라미터를 넘기는 CallMethodAction 버전을 작성해야 합니다.



데이터 유효화와 에러 리포팅


당신의 뷰 모델이나 모델은 종종, 데이터 유효성 검사를 수행하거나 뷰에 데이터 유효성 에러를 표시해서, 사용자가 그것을 수정하는 동작을 수행할 수 있기를 요구받을 것입니다.


실버라이트와 WPF 는 뷰의 컨트롤에 바인딩된 개별 프라퍼티들을 변경할 때 발생하는 데이터 유효성 에러를 관리하기 위한 지원을 제공합니다. 컨트롤에 데이터 바인딩되어 있는 단일 프라퍼티에 대해, 뷰 모델이나 모델은 프라퍼티 setter 에서 데이터 유효성 에러를 표시할 수 있습니다. 이는 예외를 던지거나 들어 온 잘못된 값을 거부함으로써 수행됩니다. 만약 데이터 바인딩할 때 ValidatesOnExceptions 프라퍼티가 true 로 설정되어 있다면, WPF 와 실버라이트의 데이터 바인딩 엔진은 그 예외를 다루고 사용자에게 데이터 유효성 에러가 있음을 가시적으로 보여줄 것입니다.


그러나, 이러한 방식으로 프라퍼티에서 예외를 던지는 것은 가능하면 피해야 합니다. IDataErrorInfo INotifyDataErrorInfo 인터페이스를 뷰 모델이나 모델에서 구현하는 것이 대안적인 접근법입니다. 이 인터페이스들은 뷰 모델이나 모델이 하나 이상의 프라퍼티 값들에 대해서 데이터 유효성 검사를 수행하도록 해 주며, 뷰에 에러 메시지를 반환하도록 해서 사용자가 에러를 통지받을 수 있게 합니다.



IDataErrorInfo 구현하기


IDataErrorInfo 인터페이스는 프라퍼티 데이터 유효화 및 에러 리포팅을 위한 기본적인 지원을 제공합니다. 그것은 두 개의 읽기 전용 프라퍼티들을 정의합니다; 프라퍼티 이름으로 인덱서 인자를 사용하는 인덱서 프파리티와 Error 프라퍼티입니다. 둘 다 문자열 값을 반환합니다.


인덱서 프라퍼티는 뷰 모델이나 모델이 명명된 프라퍼티에 대한 특정한 에러 메시지를 제공하도록 합니다. 빈 문자열이나 null 반환값은 뷰에 변경된 프라퍼티 값이 유효하다는 것을 알려 줍니다. Error 프라퍼티는 뷰 모델이나 모델이 전체 오브젝트에 대한 에러 메시지를 제공하도록 해 줍니다. 그러나 이 프라퍼티는 현재 실버라이트나 WPF 데이터 바인딩 엔진에서 호출되지 않는다는 것을 기억하기 바랍니다.


IDataerrorInfo 인덱서 프라퍼티는 데이터 바인딩된 프라퍼티가 처음 출력될 때, 그리고 그것이 연속적으로 변경될 때마다 접근됩니다. 이 인덱서 프라퍼티는 변경중인 모든 프라퍼티들을 위해 호출되기 때문에, 데이터 유효성 검사가 가능한 한 빠르고 효율적으로 수행되도록 보장하는데 주의를 기울여야만 합니다.


뷰의 컨트롤들에 IDataErrorInfo 인텊이스를 통해서 유효화를 수행하고자 하는 프라퍼티들을 바인딩할 때, 데이터 바인딩의 ValidatesOnDataErrors 프라퍼티를 true 로 설정해야 합니다. 이는 데이터 바인딩 엔진이 데이터 바인딩된 프라퍼티를 위해 에러 정보를 요구하는 것을 보장할 것입니다.




INotifyDataErrorInfo 구현하기


INofityDataErrorInfo IDaraErrorInfo 보다 더 유연합니다. 그것은 프라퍼티를 위한 다중 에러, 비동기 데이터 유효성 검사, 에러 상태가 오브젝트를 위해서 변경될 때 뷰에 통지하는 기능을 지원합니다. 그러나 INotifyDataErrorInfo 는 현재 실버라이트 4 에서만 지원하고 WPF 4 에서는 지원되지 않습니다.


INotifyDataErrorInfo 인터페이스는 HasError 프라퍼티를 정의하는데, 이는 뷰 모델이 에러( 혹은 다중 에러)가 어느 프라퍼티들에서 발생했는지를 알려 주고, 뷰 모델이 개별 프라퍼티들을 위한 에러 메시지 리스트를 반환할 수 있도록 해 주는 GetErrors 메서드를 알려 줍니다.


INotifyDataErrorInfo 인터페이스는 ErrosChanged 이벤트도 정의하고 있습니다. 이는 비동기적인 데이터 유효성 검사 시나리오를 지원하는데, ErrorsChanged 이벤트를 통해 뷰나 뷰 모델이 개별 프라퍼티들에 대한 에러 상태 변경을 표시할 수 있도록 해 줍니다. 프라퍼티 값들은 데이터 바인딩 이외에도 다양한 방식으로 변경될 수 있습니다 - 예를 들어, 웹 서비스 호출이나 백그라운드 계산의 결과로서 변경될 수 있습니다. ErrorsChanged 이벤트는 데이터 유효성 에러가 식별될 때 뷰 모델이 에러가 난 뷰에 정보를 줄 수 있도록 해 줍니다.


INotifyDataErrorInfo 를 지원하기 위해서는, 각 프라퍼티를 위한 에러 리스트를 유지할 필요가 있습니다. Model-View-ViewModel Reference Implementation( MVVM RI ) 는 오브젝트의 모든 유효성 에러를 트래킹하는 ErrosConatiner 컬렉션 클래스를 사용해 이를 수행하는 방식에 대해 설명합니다. 그것은 또한 에러 리스트가 변경되었을 때 통지 이벤트들을 발생시킵니다. 다음 예제 코드는 DomainObject( 루트 모델 오브젝트 )를 보여 주고, ErrorsContainer 클래스를 사용하는 INotifyDataErrorInfo 에 대한 샘플 구현을 보여 줍니다.



실버라이트에서, 뷰 모델의 프라퍼티들에 바인딩된 모든 컨트롤들은 자동적으로 INotifyDataErrorInfo 이벤트를 구독하며, 프라퍼티가 에러를 포함할 때 컨트롤에 에러 정보를 출력합니다.



생성 및 엮기( wire-up )



MVVM 패턴은 UI 를 프리젠테이션 로직, 비즈니스 로직, 데이터와 명확하게 분리할 수 있도록 도와줍니다. 그래서 올바른 클래스로 올바른 구현을 하는 것이 MVVM 패턴을 효율적으로 사용하기 위해 중요한 첫 번째 단계입니다. 뷰와 뷰 모델간이 데이터 바인딩과 커맨드를 통해서 상호작용하도록 관리하는 것도 고려해야 할 중요한 관점입니다. 다음 단계는 뷰, 뷰 모델, 모델 클래스을 인스턴스화하고 런타임에 서로 연관시키는 방법을 고려하는 것입니다.


노트:

이 단계를 관리하기 위한 적절한 전략을 선택하는 것은 응용프로그램에서 종속성 주입 컨테이너를 사용하고자 한다면 특히 중요합니다. MEF 와 유니티는 모두 뷰, 뷰 모델, 모델 사이의 종속성들을 지정하는 기능을 제공하고 있으며, 컨테이터에 의해서 그것들을 수행합니다. 진보된 시나리오에 대한 더 많은 정보를 원한다면, 챕터 6의 "진보된 MVVM 시나리오들" 을 참조하십시오.


일반적으로, 뷰와 그것의 뷰 모델 간에는 1 대 1 관계가 성립합니다. 뷰와 뷰 모델은 뷰의 데이터 칸텍스트 프라퍼티를 통해 느슨하게 연결됩니다; 이는 뷰에서의 비주얼 엘리먼트들과 비헤이비어들이 뷰 모델의 프라퍼티, 커맨드, 메서드들에 데이터 바인딩되도록 해 줍니다. 당신은 뷰와 뷰 모델 클래스의 인스턴스화를 관리하는 방식과 그것들을 런타임에 DataContext 프라퍼티를 통해 연관시키는 방식을 결정할 필요가 있습니다.


뷰 와 뷰 모델을 생성하고 연결할 때 느슨한 결합이 유지되도록 보장하기 위해서 주의를 기울여야만 합니다. 이전 섹션에서 주의를 줬듯이, 뷰 모델은 이상적으로는 뷰에 대한 특정 구현에 의존성을 가져서는 안 됩니다. 이와 유사하게, 뷰는 이상적으로는 뷰 모델의 특정 구현에 의존성을 가져서는 안 됩니다.


노트:

그러나, 뷰는 묵시적으로 뷰 모델의 특정 프라퍼티, 커맨드, 메서드에 의존할 것이라는 점을 기억해야 합니다. 왜냐하면 그것이 데이터 바인딩을 정의하고 있기 때문입니다. 만약 뷰 모델이 요구되는 프라퍼티, 커맨드, 메서드를 구현하지 않았다면, 런타임 예외가 데이터 바인딩 엔진에 의해서 생성될 것이며, 이는 디버깅 동안에 비주얼 스튜디오의 아웃풋 윈도우에 출력될 것입니다.


뷰와 뷰 모델이 런타임에 생성되고 연관되는 방식은 다양합니다. 가장 적절한 접근법은, 당신이 뷰나 뷰 모델을 생성하느냐, 그리고 이를 프로그램적으로 하느냐 선언적으로 하느냐에 달려있을 것입니다. 다음 섹션들은 뷰와 뷰 모델이 런타임에 생성되고 연관되는 일반적인 방식들에 대해 설명합니다.



XAML 을 사용해서 뷰 모델을 생성하기


아마도 뷰를 위한 가장 간단한 접근법은 XAML 에서 뷰와 연관된 뷰 모델을 XAML 에서 선언적으로 인스턴스화하는 것입니다. 뷰가 생성될 때, 관련 뷰 모델 오브젝트도 생성될 것입니다. 당신은 XAML 에서 뷰 모델이 뷰의 데이터 칸텍스트에 설정되었음을 지정할 수도 있습니다.


XAML 기반 접근법은 Basic MVVM QuickStart 의 QuestionnaireView.xaml 파일에 설명되어 있습니다. 그 예제에서, QuestionnaireViewModel 인스턴스는 QuestionnaireView 의 XXAML 에서 정의됩니다. 그 예는 다음과 같습니다.



QuestionnaireView 가 생성될 때, QuestionnaireViewModel 이 자동적으로 생성되며, 뷰의 데이터 칸텍스트로 설정됩니다. 이 접근법은 당신의 뷰 모델이 기본 ( 파라미터 없는 ) 생성자를 가질 것을 요구합니다.


뷰에 의한 뷰 모델의 선언적인 생성 및 할당의 이점은 그것이 단순하고 Microsoft Expression Blend 나 Microsoft Visual Studio 와 같은 디자인 타임 툴들에서 잘 동작한다는 거입니다. 이 접근법의 단점은 뷰가 관련 뷰 모델 타입에 대한 정보를 가지고 있다는 것입니다.



프로그램적으로 뷰 모델 생성하기


이 접근법은 뷰와 연관된 뷰 모델을 프로그램적으로 뷰의 생성자에서 인스턴스화하는 것입니다. 그리고 나서 뷰의 데이터 칸텍스트에 뷰 모델을 설정할 수 있습니다. 그 예가 아래에 나와 있습니다.



뷰 모델을 뷰의 코드-비하인드에서 프로그램적으로 생성하고 할당하는 것의 이점은 그것이 단순하며 Expression Blend 나 Visual Studio 와 같은 디자인 타임 툴에서도 잘 동작한다는 것입니다. 이 접근법의 단점은 뷰가 연관된 뷰 모델 타입에 대한 정보를 가질 것을 필요로 한다는 것이며, 뷰의 코드-비하인드 내의 코드를 요구한다는 것입니다. 유니티나 MEF 와 같은 종속성 주입 컨테이너를 사용하는 것은 뷰와 뷰 모델 간의 느슨한 결합을 유지하는데 도움을 줍니다. 더 많은 정보를 원한다면, 챕터 3 "컴포넌트 간의 종속성 관리하기" 를 참조하십시오.



데이터 템플릿으로 정의된 뷰 생성하기


뷰는 데이터 템플릿으로 정의될 수 있으며, 뷰 모델 타입과 연관될 수 있습니다. 데이터 템플릿은 리소스로서 정의되거나, 뷰 모델을 출력하게 될 컨트롤 내부에 포함되어 정의될 수 있습니다. 컨트롤의 "내용" 은 뷰 모델 인스턴스이며, 데이터 템플릿은 그것을 가시적으로 표현하는데 사용됩니다. WPF 와 실버라이트는 데이터 템플릿을 런타임에 자동적으로 인스턴스화하며 그것의 데이터 칸텍스트를 뷰 모델의 인스턴스로 설정합니다. 이 기법은 뷰 모델이 먼저 생성되고 뷰가 나중에 생성되는 상황의 예입니다.


데이터 템플릿은 유연하고 가볍습니다. UI 설계자는 그것들을 사용해 복잡한 코드를 요구하지 않고도 뷰 모델에 대한 가시적 표현을 쉽게 정의할 수 있습니다. 데이터 템플릿들은 뷰에 제한되는데, 그것은 어떤 UI 로직( 코드-비하인드 )도 요구하지 않습니다. Microsoft Expression Blend 는 데이터 템플릿을 가시적으로 설계하고 편집하기 위해서 사용될 수 있습니다.


다음 예제는 고객 리스트에 바인딩되는 ItemsControl 을 보여 줍니다. 기저 컬렉션의 각 고객 오브젝트는 뷰 모델 인스턴스입니다. 고객을 위한 뷰는 인라인 데이터 템플릿으로 정듸됩니다. 다음 예제에서, 각 고객 뷰 모델을 위한 뷰는 StackPanel  로 정의되는데, 그것은 레이블과 뷰 모델의 Name 프라퍼티에 바인딩된 텍스트 박스로 구성되어 있습니다.



당신은 데이터 템플릿을 리소스로서 정의할 수도 있습니다. 다음 예제는 리소스로 정의된 데이터 템플릿을 보여 주고, StaticRresource 마크업 확장을 통해서 클라이언트 컨트롤에 적용되는 것을 보여 줍니다.



여기에서, 데이터 템플릿은 concrete 뷰 타입을 래핑합니다. 이는 뷰가 코드-비하인드 비헤이비어를 정의하도록 해 줍니다. 이러한 방식으로, 데이터 템플릿 메커니즘은 외부적으로 뷰와 뷰 모델의 연관을 제공하기 위해서 사용될 수 있습니다. 비록 앞의 예제가 UserControl 리소스 안의 템플릿을 보여 주기는 하지만, 그것은 재사용을 위해서 보통 응용프로그램의 리소스에 배치될 것입니다. 데이터 템플릿을 사용하여 뷰를 인스턴스화하고 그것들을 자신의 뷰 모델에 연관시키는 예제를 MVVM QuickStart 의 QuestionnaireView.xaml 에서 찾아볼 수 있습니다.



핵심 결정들



당신의 응용프로그램을 생성하는데 있어 MVVM 패턴을 사용하기로 했다면, 나중에 변경하기 힘든 특정 설계 결정들을 내려야 할 필요가 있습니다. 보통, 이 결정들은 응용프로그램 전반적인 이슈이며, 그것들을 응용프로그램 전반에서 일관성있게 사용하는 것은 개발자나 디자이너의 제품성을 증대시켜줄 것입니다. 다음은 MVVM 패턴을 구현할 때 가장 중요한 결정들을 요약합니다:

  • 당신이 사용할 뷰 및 뷰 모델의 생성과 관련한 접근법을 결정하십시오. 응용프로그램이 뷰나 뷰 모델을 먼저 생성할지, 유니티나 MEF 와 같은 종속성 주입 컨테이너를 사용할지를 결정할 필요가 있습니다. 당신은 보통 이것이 응용프로그램 전반에서 일관적이기를 원할 것입니다. 더 많은 정보를 원한다면, 이 챕터의 "생성 및 엮기" 섹션을 참조하십시오. 그리고 챕터 6 "진보된 MVVM 시나리오들" 의 "진보된 생성 및 엮기" 를 참조하십시오.
  • 뷰 모델로부터의 커맨드를 커맨드 메서드로 노출할지 커맨드 오브젝트로 노출할지를 결정하십시오. 커맨드 메서드는 노출하기 쉬우며 뷰의 비헤이비어를 통해서 실행될 수 있습니다. 커맨드 오브젝트는 커맨의와 활성화/비활성화 로직을 깔끔하게 캡슐화할 수 있으며, 비헤이비어나 ButtonBase-상속 클래스의 Command 프라퍼티를 통해서 실행될 수 있습니다. 개발자나 디자이너가 사용하기 쉽게 하기 위해서, 이를 응용프로그램 전반에서 사용하기로 하는 선택하는 것은 좋은 생각입니다. 더 많은 정보를 원한다면, 이 챕터의 "커맨드들" 섹션을 참조하십시오.
  • 뷰 모델과 모델이 뷰에 에러를 제출하는 방식을 결정하십시오. 당신의 모델은 IDataErrorInfo 나, 실버라이트를 사용할 경우, INotifyDataErrorInfo 를 지원할 수 이습니다. 모든 모델이 에러 정보를 제출할 필요는 없지만, 그래야 하는 경우에, 당신의 개발자들을 위해 일관적인 접근을 하는 것이 좋습니다. 더 많은 정보를 원한다면, 이 챕터의 "데이터 유효화 및 에러 리포팅' 을 참조하십시오.
  • Microsoft Expression Blend 디자인-타임 데이터 지원이 당신의 팀에 있어 중요한지를 결정하십시오. 만약 Expression Blend 를 UI 를 설계하고 유지보수하는데 사용하고 디자인 타임 데이터를 보고 싶다면, 당신의 뷰와 뷰 모델이 파라미터를 가지지 않는 생성자를 제공하고 뷰가 디자인-타임 데이터 칸텍스트를 제공하도록 보장하십시오. 대안적으로, d:DataContextd:DesignSource 와 같은 디자인-타임 애트리뷰트들을 사용하여 Microsoft Expression Blend 가 제공하는 디자인-타임 기능들을 사용하는 것을 고려하십시오. 더 많은 정보를 원한다면, 챕터 7 "유저 인터페이스 만들기" 의 "디자이너 친화적인 뷰를 생성하는 가이드라인" 을 참조하십시오.


더 많은 정보



For more information about data binding in WPF, see "Data Binding" on MSDN:

For data binding in Silverlight, see "Data Binding" on MSDN:

For more information about binding to collections in WPF, see "Binding to Collections" in "Data Binding Overview" on MSDN:

For more information about binding to collections in Silverlight, see "Binding to Collections" in "Data Binding" on MSDN:

For more information about the Presentation Model pattern, see "Presentation Model" on Martin Fowler's website:

For more information about data templates, see "Data Templating Overview" on MSDN:

For more information about MEF, see "Managed Extensibility Framework Overview" on MSDN:

For more information about Unity, see "Unity Application Block" on MSDN:

For more information about DelegateCommand and CompositeCommand, see Chapter 9, "Communicating Between Loosely Coupled Components."


'Programming > Prism4.1' 카테고리의 다른 글

진보된 MVVM 시나리오들  (0) 2014.09.20
모듈러 응용프로그램 개발  (0) 2014.09.13
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
프리즘 응용프로그램 초기화하기  (0) 2014.08.24
왜 프리즘을 사용하는가?  (0) 2014.08.19
소개  (0) 2014.08.18
프리즘 4.1 설치  (0) 2014.08.14

프리즘 4.1 도움말 번역입니다



모듈러 응용프로그램 개발



모듈러 응용프로그램은 더 큰 응용프로그램으로 통합될 수 있는 ( 모듈이라는 이름을 가진 ) 기능적 유닛들의 집합으로 구성된 응용프로그램을 말합니다. 클라이언트 모듈은 응용프로그램의 전체적인 기능들의 일부를 캡슐화함며, 일반적으로 연관된 관심사들의 집합을 표현합니다. 그것은 응용프로그램 기능과 같은 관련 컴포넌트들의 집합을 포함하는데, 이 컴포넌트 집합은 사용자 인터페이스와 비즈니스 로직, 또는 로깅이나 구사용자 인증과 같은 응용프로그램-레벨 서비스들과 같은 응용프로그램 인프라스트럭쳐의 일부를 포함합니다. 모듈들은 서로 돌립적이지만, 느슨학 결합되는 방식으로 서로 통신할 수도 있습니다. 모듈러 응용프로그램들은 응용프로그램을 개발하고 테스트하고 배포하고 확장하기 쉽게 만들어 줍니다.


예를 들어, 개인 뱅킹 응용프로그램을 고려해 보십시오. 사용자는 단일한 유저 인터페이스를 통해 계정간에 돈을 이체하거나, 세금을 내거나, 개인 정보를 갱신하는 것과 같은 다양한 기능들에 접근할 수 있습니다. 그러나 보이는 장면 뒤에는, 이러한 각 기능들이 분리된 모듈로 캡슐화되어 있습니다. 이 모듈들은 서로 통신하며, 데이터베이스 서버나 웹 서비스들과 같은 백엔드 시스템들과 통신하고 있습니다. 응용프로그램 서비스들은 서로 다른 모듈들 내에 존재하는 다양한 컴포넌트들을 통합하고, 사용자와의 통신을 제어합니다. 사용자는 단일 응용프로그램처러 보이는 통합된 뷰를 보게 됩니다.


다음 그림은 다중 모듈들을 가진 모듈러 응용프로그램의 설계를 보여 줍니다.


모듈 조합




모듈러 응용프로그램들을 빌드하는 것의 이점들



당신은 아마도 어셈블리, 인터페이스, 클래스, 좋은 개체지향 설계 원칙의 도입을 통해 잘 구조화된 응용프로그램을 이미 빌드하고 있을 것입니다. 그렇지만, 별로 신경쓰지 않고 있다면, 당신의 응용프로그램은 아마 여전히 모놀리식( 모든 기능들이 응용프로그램 내부에서 강하게 결합되는 방식으로 구현되는 것을 의미 )일 것이며, 이는 응용프로그램을 개발하고, 테스트하고, 확장하고, 유지보수하기 힘들게 만들 수 있습니다.


반면에, 모듈러 응용프로그램 접근법은 응용프로그램의 대규모 기능 영역들을 구분하는데 도움을 줄 수 있으며, 그 기능들을 독립적으로 개발하고 테스트할 수 있도록 해 줍니다. 이는 개발 및 테스팅을 쉽게 만들어 줄 뿐만 아니라, 응용프로그램을 더욱 유연하게 만들어 주며, 나중에 확장하기 쉽게 만들어 줍니다. 모듈러 접근법의 이점은 당신의 전체 응용프로그램 구조가 더욱 유연하고 유지보수 가능하도록 만들어 줄 수 있다는 것입니다. 왜냐하면 그것은 응용프로그램을 관리 가능한 조각들로 나눌 수 있게 해 주기 때문입니다. 각 조각들은 특정 기능들을 캡슐화하고, 각 조각들은 명료하지만 느슨하게 결합된 대화 채널들을 통해서 통합됩니다.


모듈러 응용프로그램 개발을 위한 프리즘의 지원


프리즘은 모듈러 응용프로그램 개발 및 실시간 모듈 관리를 위한 지원을 제공합니다. 프리즘의 모듈러 개발 기능을 사용하면 시간을 절약할 수 있습니다. 왜냐하면 당신이 자신의 모듈성 프레임워크를 구현하고 테스트할 필요가 없어지기 때문입니다. 프리즘은 다음의 모듈러 응용프로그램 개발 기능을 지원합니다:

  • 이름있는 모듈들과 그것의 위치를 등록하기 위한 모듈 카탈로그를 제공; 당신은 다음과 같은 방식으로 모듈 카탈로그를 생성할 수 있습니다:
    • 코드나 Extensible Application Markup Language( XAML )에서 모듈을 정의함으로써...
    • Windows Presentation Foundation( WPF ) 에 대해 : 디렉토리 내의 모듈들을 검색함으로써, 중앙관리되는 카탈로그 내에 명시적으로 모듈들을 정의하지 않아도, 당신의 모든 모듈들을 로드할 수 있습니다.
    • WPF 에 대해: 구성 파일에 모듈들을 정의함으로써...
  • 초기화 모드와 종속성들을 지원하기 위해서, 모듈에 대한 선언적인 메타데이터 애트리뷰트들을 제공.
  • 모듈들 사이의 느슨한 결합을 지원하기 위해서, 종속성 주입 컨테이너와의 통합을 제공.
  • 모듈 로딩을 위해서:
    • 모듈들이 올바른 순서로 한 번만 로드되고 초기화되는 것을 보장하기 위해서, 중복 검사와 사이클 검사를 포함하는 종속성 관리를 제공합니다.
    • 응용프로그램 시작 시간을 최소화하기 위해서 모듈들에 대한 온디맨드 로딩 및 백그라운드 로딩을 제공합니다; 남은 모듈들은 백그라운드로 혹은 요구되는 시점에 로드되고 초기화될 수 있습니다.

핵심 개념들


이 섹션은 프리즘의 모듈성과 관련된 핵심 개념들을 소개하는데, IModule 인터페이스, 모듈 로딩 과정, 모듈 카탈로그, 모듈간 통신, 종속성 주입 컨테이너들과 관련한 내용들을 포함합니다.

IModule : 모듈러 응용프로그램들의 빌딩 블락


모듈은 기능에 대한 논리 컬렉션이며 리소스인데, 이는 개별적으로 개발되고, 테스트되고, 배포되고, 응용프로그램에 통합될 수 있는 방식으로 패키징됩니다. 패키지는 하나 이상의 어셈블리일 수 있으며, 느슨한 컬렉션이나 XAP 파일 내에 묶여 있을 수 있습니다. 각 모듈은 그 모듈을 초기화하고 응용프로그램에 그것의 기능을 통합하는 책임이 있는 중앙 클래스를 가지게 됩니다. 그 클래스는 IModule 인터페이스를 구현합니다. IModule 인터페이스를 구현하는 클래스의 존재는 그 패키지를 모듈로서 식별하는데 충분합니다. IModule 인터페이스는 Initialize 라고 명명된 단일 메서드를 가지는데, 이 안에서 당신은 모듈을 초기화하고 응용프로그램에 모듈의 기능성을 통합하기 위해서 요구되는 로직을 구현할 수 있습니다. 모듈의 목적에 따라, 뷰를 복합 유저 인터페이스에 추가하거나, 응용프로그램에서 이용 가능한 부가적인 서비스들을 만들거나, 응용프로그램의 기능성을 확장할 수 있습니다. 다음 코드는 모듈을 위한 최소 구현을 보여 줍니다.



노트:

IModule 인터페이스에 의해 제공되는 초기화 메커니즘을 사용하는 대신에, Stock Trader RI 는 뷰, 서비스, 타입을 등록하기 위해 선언적인 애트리뷰트 기반 접근법을 사용합니다.


모듈 라이프타임


프리즘에서 모듈 로딩 프로세스는 다음을 포함합니다:

  1. 모듈을 등록하고 검색하기. 특정 응용프로그램을 위해 런타임에 로드될 모듈들은 모듈 카탈로그에 정의됩니다. 이 카탈로그는 로드될 모듈에 대한 정보, 그것들의 위치, 그리고 로드될 순서에 대한 정보를 포함합니다.
  2. 모듈 로드하기. 모듈들을 포함하는 어셈블리는 메모리에 로드됩니다. 이 단계는 모듈들이 웹으로부터 다운로드되거나, 원격 위치나 로컬 디렉토리로부터 획득될 것을 요청합니다.
  3. 모듈 초기화하기. 그 다음, 모듈들이 초기화됩니다.  이는 모듈 클래스의 인스턴스를 생성하고 IModule 인터페이스를 통해 그것들의 Initialize 메서드를 호출하는 것을 의미합니다.

다음 그림은 모듈 로딩 과정을 보여줍니다.

모듈 로딩 과정





모듈 카탈로그


ModuleCatalog 는 응용프로그램에서 사용할 수 있는 모듈들에 대한 정보를 저장합니다. 이 카탈로그는 본질적으로 ModuleInfo 클래스들의 컬렉션입니다. 각 모듈은 ModuleInfo 클래스에서 기술되는데, 이는 이름, 타입, 위치, 그리고 모듈의 다른 애트리뷰트들을 기록합니다. ModuleCatalog ModuleInfo 인스턴스들로 채우기 위한 몇 가지 일반적인 접근법들이 존재합니다:

  • 코드에서 모듈들을 등록하기
  • XAML 에서 모듈들을 등록하기
  • 구성 파일에서 모듈들을 등록하기( WPF 만 )
  • 디스크의 로컬 디렉토리에서 모듈들을 검색하기( WPF 만 )


당신이 사용할 모듈 등록 및 검색 메커니즘은 응용프로그램의 요구에 달려 있습니다. 구성 파일이나 XAML 파일을 사용하는 것은 응용프로그램이 모듈에 대한 레퍼런스들을 요구하지 않도록 해 줍니다. 디렉토리를 사용하는 것은 응용프로그램이 파일에 그것들을 지정하지 않고도 모듈들을 검색할 수 있게 해 줍니다.


모듈을 로드할 시점을 제어하기


프리즘 응용프로그램들은 가능한 한 빨리 모듈들을 초기화할 수 있는데, 이는 "이용 가능할 때( when available )" 로 알려져 있습니다. 그리고 응용프로그램이 그것을 필요로 할 때 초기화할 수 있는데, 이는 "온디맨드( 요청시, on-demand )"로 알려져 있습니다. 실버라이트 응용프로그램에 대해, 모듈들은 응용프로그램과 함께 다운로드되거나, 응용프로그램 시작 후에 백그라운드로 다운로드될 수 있습니다. 모듈을 로드하기 위한 다음 가이드라인을 고려하십시오:

  • 응용프로그램을 실행하기 위해서 요구되는 모듈들은 반드시 응용프로그램과 함께 다운로드되어야만 하고, 응용프로그램이 실행될 때 초기화되어야만 합니다.
  • 응용프로그램의 일반적인 용도를 위해서 항상 사용되는 기능들을 포함하는 모듈들은 그것들이 이용 가능해질 때 백그라운드로 다운로드되거나 초기화될 수 있습니다.
  • 거의 사용되지 않는 모듈들은( 혹은 다른 모듈들을 위해서 선택적인 의존성을 가지는 지원 모듈들은 ) 백그라운드로 다운로드되고 요청시에 초기화될 수 있습니다.

응용프로그램에 모듈들을 통합하기

프리즘은 응용프로그램을 구동( bootstrap )하기 위해 다음의 클래스들을 제공합니다: UnityBootstrapper MefBootstrapper. 이 클래스들은 모듈들을 검색하거나 로드하기 위한 모듈 관리자를 생성하고 구성하는데 사용될 수 있습니다. XAML 파일이나, 구성 파일, 몇 줄의 코드에 기입된 디렉토리 위치에서 지정된 모듈들을 등록하기 위해 구성 메서드를 재정의할 수 있습니다.

모듈 Initialize 메서드를 사용해서 모듈들을 응용프로그램의 나머지 부분과 통합하십시오. 당신이 택할 수 있는 방식은 다양하며, 이는 응용프로그램 구조와 모듈의 내용에 의존하게 됩니다. 다음은 모듈을 응용프로그램ㄹ에 등록하기 위해서 수행해야 하는 공통적인 작업들입니다:
  • 모듈의 뷰를 응용프로그램의 네비게이션 구조에 추가하십시오. 이는 복합 UI 응용프로그램을 뷰 검색이나 뷰 주입을 사용해 빌드할 때 공통적인 작업입니다.
  • 응용프로그램 레벨 이벤트나 서비스를 구독( subscribe )하십시오.
  • 공유 서비스들을 응용프로그램의 종속성 주입 컨테이너에 등록하십시오.

모듈 간에 통신하기


비록 모듈들이 모듈간에 저수준 결합을 하고 있기는 하지만, 모듈간에 서로 통신하는 것은 일반적인 상황입니다. 몇 가지 느슨하게 결합된 통신 패턴이 존재하는데, 각각은 자신만의 강도를 가집니다. 일반적으로, 이 패턴들의 조합은 결과적인 솔루션을 생성하는데 사용됩니다. 다음은 이러한 패턴의 일부를 소개합니다:

  • 느슨하게 결합된 이벤트들. 모듈은 발생한 특정 이벤트를 발송할( broadcast )수 있습니다. 다른 모듈들은 그 이벤트를 구독해서, 언제 이벤트가 발생했는지를 통지받을 것입니다. 느슨하게 결합된 이벤트들은 두 모듈 사이에 통신을 설정하기 위한 가벼운 방식입니다; 그러므로, 그것들은 쉽게 구현됩니다. 그러나, 너무 이벤트에 의존하는 설계는 단일 작업을 만족시키기 위해서 서로를 열심히 조율하는 일을 필요로 하게 만듭니다. 이 경우, 공용 서비스를 고려하는 것이 더 나을 것입니다.
  • 공유 서비스. 공유 서비스는 공통 인터페이스를 통해 접근될 수 있는 클래스를 의미합니다. 일반적으로, 공유 서비스는 공유 어셈블리에서 발견되며, 인증이나 로깅, 혹은 구성과 같이 전 시스템에 걸친 서비스를 제공합니다.
  • 공유 리소스. 만약 당신이 직접적으로 다른 모듈과 서로 통신하는 모듈들을 원하고 있다면, 간접적으로 데이터베이스나 웹 서비스 집합과 같은 공유 리소스들을 통해 그것들을 통신하게 만들 수 있습니다.

의존성 주입과 모듈러 응용프로그램


유니티 응용프로그램 블락( Unity ) 와 관리되는 의존성 프레임워크( MEF ) 같은 컨테이너들은 쉽게 제어 역전( Inversion of Control,  IoC ) 과 의존성 주입을 사용할 수 있게 해 줍니다. 이는 느슨하게 결합하는 방식으로 컴포넌트를 조립하는데 도움을 주는 강력한 설계 패턴입니다. 제어 역전은 어떤 컴포넌트가 자신이 의존하고 있는 다른 컴포넌트에 대한 레퍼런스를 획득하기 위해, 그 레퍼런스에 대한 하드코딩을 하지 않고도, 레퍼런스를 획득할 수 있도록 해 줍니다. 결과적으로 코드 재사용성을 높이고 유연성을 증가시킬 수 있도록 합니다. 의존성 주입은 느슨하게 결합된 모듈러 응용프로그램을 빌드할 때 유용합니다. 프리즘은 응용프로그램 내에서 컴포넌트를 조립하기 위해서 사용되는 의존성 주입 컨테이너에 대한 불가지론( agnostic )으로 설계되었습니다. 컨테이너에 대한 선택은 당신에게 달려 있으며, 그것은 응용프로그램 요구사항 및 선호도에 크게 의존합니다. 그러나, 마이크로소프트가 제안하는 두 가지 주요 의존성 주입 프레임워크가 존재하기는 합니다 - Unity 와 MEF 가 바로 그것입니다.


패턴 & 경험의 유니티 응용프로그램 블락은 완전한 기능을 갖춘 의존성 주입 컨테이너를 제공합니다. 그것은 프라퍼티-기반 주입, 생성자-기반 주입, 그리고 정책( policy ) 주입을 지원합니다. 이는 당신이 컴포넌트 간에 행위와 정책을 투명하게 주입할 수 있도록 해 줍니다; 또한 의존성 주입 컨테이너에서 일반적인 다른 기능들에 대한 호스트도 지원합니다.


( 이제 닷넷 프레임워크 4 와 실버라이트 4 의 일부인 ) MEF는 의존성 주입-기반 컴포넌트 조립을 지원함으로써 확장성 있는 닷넷 응용프로그램을 빌드하기 위한 지원을 제공합니다. 그리고 모듈러 응용프로그램 개발을 지원하는 다른 기능들을 제공합니다. 이는 응용프로그램이 런타임에 컴포넌트들을 검색하고 그 컴포넌트들을 응용프로그램에 느슨하게 결합된 방식으로 통합할 수 있도록 해 줍니다. MEF 는 훌륭한 확장성 및 복합 프레임워크입니다. 이는 어셈블리, 타입 검색, 타임 종속성 결정( resolution ), 의존성 주입, 그리고 일부 좋은 어셈블리와 XAP 다운로드 기능들을 제공합니다. 프리즘은 MEF 기능의 이점을 취하기 위한 지원 외에도 다음을 지원합니다:

  • 모듈 타입과 그것의 XAP 위치를 연관시킵니다.
  • WPF 와 실버라이트 모두에 대해, XAML 과 코드 애트리뷰트를 통한 모듈 등록을 지원합니다.
  • WPF 에 대해, 구성 파일과 디렉토리 스캔을 통한 모듈 등록을 지원합니다.
  • 모듈이 로드될 때 상태 트래킹을 지원합니다.
  • MEF 가 사용될 때 모듈에 대한 선언적인 커스텀 메타데이터를 지원합니다.
유니티와 MEF 는 프리즘을 사용해 유사하게 동작합니다.

핵심 결정들


첫 번째 해야 할 결정은 어떤 모듈러 솔루션을 개발할 것인가 입니다. 이전 섹션들에서 논의된 것처럼, 모듈러 응용프로그램들을 빌드하는 것은 다양한 이점들을 가지고 있습니다만, 이러한 이점들을 취하기 위해 당신이 들여야 하는 시간과 노력의 관점에서 생각을 해 볼 필요가 있습니다. 만약 모듈러 응용프로그램을 개발하기로 했다면, 고려해야 할 점이 몇 가지 있습니다:
  • 당신이 사용할 프레임워크를 결정하십시오. 당신은 자신만의 모듈성 프레임워크를 생성할 수도 있고, 프리즘, MEF 나 다른 프레임워크를 사용할 수도 있습니다.
  • 당신의 솔루션을 조직할 방식을 결정하십시오. 어떤 어셈블리가 각 모듈의 일부가 될 것인지를 포함해, 각 모듈의 경계를 정의함으로써 모듈러 구조에 접근하십시오. 당신은 개발을 쉽게 하기 위해서 모듈성을 사용하기로 결심할 수도 있으며, 또한 그것이 플러그인을 지원하거나 확장성있는 구조를 지원하게될 때 응용프로그램이 배포되는 방식을 제어해야만 하겠다고 결심할 수도 있습니다.
  • 당신의 모듈들을 나누는 방식을 결정하십시오. 모듈들은 요구에 따라 서로 다르게 나뉠 수 있습니다. 예를 들어, 기능 영역, 프로바이더 모듈들, 개발 팀, 배포 요구사항에 의해 나뉠 수 있습니다.
  • 응용프로그램이 모든 모듈들에 대해 제공해야할 핵심 서비스들을 결정하십시오. 핵심 서비스는 에러 기록 서비스이거나 인증 서비스이거나 권한 서비스일 수 있습니다.
  • 만약 프리즘을 사용하고 있다면, 모듈을 모듈 카탈로그에 등록하기 위해서 사용할 접근법을 결정하십시오. WPF 에 대해, 당신은 코드, XAML, 구성 파일, 디스크의 로컬 디렉토리에서 검색된 모듈에서 모듈을 등록할 수 있습니다. 실버라이트에 대해, 당신은 코드나 XAML 에서 모듈들을 등록할 수 있습니다.
  • 당신의 모듈 통신 전략 및 의존성 전략을 결정하십시오. 모듈들은 서로 통신할 필요가 있을 것입니다. 그리고 당신은 모듈간의 종속성들을 다룰 필요가 있습니다.
  • 의존성 주입 컨테이너를 결정하십시오. 일반적으로, 모듈러 시스템은 의존성 주입, 제어 역전, 서비스 로케이터를 요구하며, 이것들을 통해 느슨한 결합과 모듈에 대한 동적 로딩 및 생성이 허용됩니다. 프리즘은 유니티, MEF, 혹은 다른 컨테이너를 사용하는 것을 허용하며, 유니티나 MEF 기반 응용프로그램들을 위한 라이브러리들을 제공합니다.
  • 응용프로그램 시작 시간을 최소화하십시오. 응용프로그램 시작 시간을 최소화하기 위해서, 모듈에 대한 온디맨드 로딩 및 백그라운드 다운로딩을 고려하십시오.
  • 배포 요구사항을 결정하십시오. 당신의 응용프로그램이 배포되는 방식에 대해서 생각할 필요가 있습니다. 이는 XAP 에 설정할 어셈블리의 개수에 영향을 줍니다. 또한 실버라이트에서 어셈블리 캐싱의 이점을 취하기 위해서는 프리즘 라이브러리와 같은 공유 라이브러리를 분할해야 할 것입니다.
다음 섹션에서는 이러한 결정들에 대한 세부 사항들을 제공합니다.

응용프로그램을 모듈로 분할하기

응용프로그램을 모듈화된 방식으로 개발할 때, 응용프로그램을 분리된 클라이언트 모듈로 구조화하게 되는데, 이 모듈들은 개별적으로 개발되고, 테스트되고, 배포될 수 있습니다. 각 모듈은 응용프로그램의 전체적인 기능의 일부를 캡슐화할 것입니다. 당신이 해야할 첫 번째 설계 결정은 응용프로그램의 기능을 분리된 모듈들로 분할하는 방식을 결정하는 것입니다.

모듈은 연관된 관심사들의 집합을 캡슐화해야 하며, 뚜렷한 책임을 지녀야 합니다. 모듈은 응용프로그램에 대한 수직적 슬라이스를 표현하거나 수평적 서비스 레이어를 표현할 수 있습니다. 큰 응용프로그램들은 비슷하게 두 종류의 모듈들을 모두 가지게 될 것입니다.

수직적 슬라이스로 조직화된 모듈을 가진 응용프로그램


수평적 레이어로 조직화된 모듈을 가진 응용프로그램


큰 응용프로그램은 수직적인 슬라이스와 수평적 레이어로 조직화된 모듈들을 가질 것입니다. 그러한 모듈들의 예는 다음을 포함합니다:

  • Stock Trader Reference Implementation( Stock Trader RI ) 의 News 모듈과 같은 특정 응용프로그램 기능을 포함하고 있는 모듈.
  • 구입, 송장, 총 계정 원장과 같은 연관된 유스케이스의 집합에 대한 특정 서브시스템이나 기능들을 포함하는 모듈.
  • 로깅, 캐싱, 권한 서비스, 웹 서비스와 같은 인프라스트럭쳐 서비스들을 포함하는 모듈.
  • 다른 내부 시스템과 더불어 Siebel CRM, SAP 와 같은 기업용 비즈니스 프로그램( line-of-business, LOB ) 시스템들을 실행하는 서비스들을 포함하는 모듈.

모듈은 다른 모듈들에 대한 최소 집합의 의존성들을 가져야 합니다. 모듈이 다른 모듈에 대한 의존성을 가지게 될 때, 그것은 콘크리트 타입 대신에 공유 라이브러리에 정의된 인터페이스를 사용하거나, EventAggregator 이벤트 타입들을 통해 다른 모듈들과 통신하기 위해서 EventAggregator 를 사용함으로써 느슨하게 결합되어야 합니다.

모듈성의 목표는 기능이나 기술이 추가되거나 삭제되더라도 응용프로그램을 유연하고, 유지보수 가능하고, 안전적인 상태로 유지하는 것이 가능한 방식으로 응용프로그램을 분할하는 것입니다. 이를 수행하기 위한 가장 최선의 방법은 모듈들이 가능한 한 독립적이고, 잘 정의된 인터페이스를 가지고, 가능한 한 고립되도록 응용프로그램을 설계하는 것입니다.

모듈들에 대한 프로젝트의 비율을 결정하기

모듈들을 생성하고 패키징하는 몇 가지 방법이 있습니다. 추천할 만하고 가장 공통적으로 사용되는 방법은 모듈당 단일 어셈블리를 생성하는 것입니다. 이는 논리 모듈들이 분리를 유지하고 적절하게 캡슐화 정도를 높이는데 도움을 줍니다. 또한 그것은 어셈블리를 모듈 경계라고 이야기하기 쉽게 만들어 줄 뿐만 아니라 모듈을 배포하는 방식에 대한 패키징을 쉽게 만들어 줍니다. 그러나, 단일 어셈블리가 다중의 모듈을 가지는 것을 막을 수는 없으며, 어떤 경우에 이는 솔루션 내의 프로젝트 개수를 최소화하기 위해서 선호되기도 합니다. 큰 응용프로그램에서, 10 개에서 50 개의 모듈들을 가지는 것은 일반적이지 않습니다. 각 모듈들을 자신의 프로젝트에 분리해 넣는 것은 솔루션에 너무 많은 복잡성을 추가하는 것이며, 비주얼 스튜디오의 성능을 저하시킬 수 있습니다. 만약 당신이 하나의 모듈당 하나의 어셈블리/비주얼 스튜디오 프로젝트로 가기로 결정했다면, 모듈이나 모듈 집합을 관리하기 위해서 자신의 솔루션에 나눠서 넣는 것도 이치에는 맞습니다.

XAP 와 모듈 팩토링

실버라이트 응용프로그램에 대해, 모듈들은 일반적으로 분리된 XAP 파일에 패키징됩니다만, 일부 경우에는 XAP 당 하나 이상의 모듈들을 가지고 있을 수 있습니다. 응용프로그램을 시작하고 새로운 기능들을 가능하게 만들기 위해서 요구되는 다운로드 요청의 횟수와 크기를 최소화하기 위해서 필요한 XAP 파일의 개수가 몇 개나 될지 고려해야 합니다. 만약 각 모듈을 자신만의 프로젝트/어셈블리로 분리하기로 했다면, 각 어셈블리를 그것들만의 XAP 에 배치할지 혹은 다중 어셈블리들을 하나의 XAP 에 포함시킬지를 결정할 필요가 있습니다.

다중 모듈들을 하나의 XAP 에 포함시킬지 그것들을 분리할지를 선택하는데 영향을 주는 요소들은 다음과 같습니다:
  • 다운로드 크기 및 공유 종속성들. 각 XAP 파일은 자신의 매니페스트 및 .zip 패키징에서 작은양의 부가적인 크기 오우버헤드를 가지게 됩니다. 또한, 모듈들 간에 공통 종속성이 존재하고 그것들이 종속적 모듈이나 캐싱된 라이브러리와 분리되어 있지 않다면, 각 XAP 는 그러한 종속적인 라이브러리들을 포함할 것이고, 이는 다운로드 크기를 심각하게 증가시킬 수도 있습니다.
  • 다중 모듈들이 응용프로그램에 의해 필요하게 될 때의 시점. 응용프로그램의 시작시에 뷰를 제출하는 것과 같이 다중 모듈들이 동시에 로드되고 사용된다면,그것들을 하나의 XAP 에 패키징하는 것이 약간 더 빠르며, 동시에 클라이언트에 대해 물리적으로 이용 가능한 상태가 되도록 보장하는데도 도움이 됩니다. 프리즘의 모듈성 기능은 종속성들을 지정하는 모듈들이 올바른 순서로 로드되는 것을 보장하며, 그래서 모듈들이 다중 XAP 들에 나뉘어 있을 때도 임포터 로드 오더가 필요하지 않도록 만들 수 있습니다. 그러나 하나가 아니라 두 개의 다운로드를 만들면 약간의 성능 오우버헤드가 존재합니다. 심지어는 전체적으로 같은 다운로드 크기를 가지고 있더라도 그렇습니다.
  • 모듈 버저닝( Versioning ). 만약 서로 다른 모듈들이 독립적인 타임라인에서 개발되고 잠재적으로 개별적으로 배포될 것이라고 하면, 당신은 그것들을 개별 XAP 들에 배치하기를 원할 것이고, 그것들은 명확하고 독립적으로 갱신되도록 하기 위해서 서로 다른 버전으로 표기될 수 있을 것입니다.

개별 XAP 파일들에서 같은 어셈블리를 중복으로 다운로드하는 것을 피하기 위한 두 가지 접근법들이 사용될 수 있습니다:

  • 공유 종속성들을 개별 인프라스트럭쳐로 분리하는 것을 고려하고, 사용되는 모듈들이 그 공유 모듈들에 대한 종속성을 가지도록 하십시오.
  • 실버라이트의 공유 라이브러리 캐싱을 사용해서 공유 타입들을 공유 라이브러리에 배치하십시오. 이는 프리즘 모듈 다운로더 대신에 실버라이트에 의해서 한 번만 다운로드되고 캐싱됩니다.


느슨한 결합을 위해 종속성 주입 사용하기


하나의 모듈은 호스트 응용프로그램이나 다른 모듈들이 제공하는 컴포넌트들과 서비스들에 의존할 것입니다. 프리즘은 모듈들 간의 종속성을 등록하는 기능을 지원하고 있어서, 그것들은 올바른 순서로 로드되고 초기화됩니다. 또한 프리즘은 모듈들이 응용프로그램에 로드될 때 초기화되도록 지원합니다. 모듈 초기화 동안에, 모듈은 그것이 요구하는 부가적인 컴포넌트들과 서비스들에 대한 참조를 획득할 수 있습니다. 그리고 다른 모듈들에서 이용 가능하게 만들기 위해서 그것이 포함하고 있는 컴포넌트들과 서비스들을 등록할 수도 있습니다.


모듈은 콘크리트 타입을 직접적으로 인스턴스화하는 대신에 외부 인터페이스의 인스턴스를 획득하기 위한 독립적인 메커니즘을 사용해야 합니다. 예를 들어, 종속성 주입 컨테이너나 팩토리 서비스를 사용할 수 있습니다. 유니티나 MEF 와 같은 종속성 주입 컨테이너들은 종속성 주입을 통해 어떤 타입이 필요로 하는 인터페이스나 타입의 인스턴스를 자동적으로 요청해 줍니다. 프리즘은 유니티와 MEF 를 통합하고 있으며, 이는 모듈들이 쉽게 종속성 주입을 사용할 수 있도록 해 줍니다.


다음 다이어그램은 컴포넌트들과 서비스들에 대한 참조를 요청하거나 등록할 필요가 있는 모듈들이 로드될 때의 일반적인 작업 과정을 보여 줍니다.


종속성 주입의 예




이 예제에서, OrdersModule 어셈블리는 OrdersRepository 클래스를 정의합니다( 이와 함께 주문 기능을 구현하는 다른 뷰들과 클래스들도 정의합니다 ). CustomersModule 어셈블리는 CustomersViewModel 클래스를 정의하는데, 이는 OrdersRepository 에 의존하며, 일반적으로 서비스에 의해 노출된 인터페이스에 의존합니다. 응용프로그램 시작과 부트스트래핑 절차는 다음과 같은 단계들을 포함합니다:

  1. 부트스트래퍼는 모듈 초기화 프로세스를 시작합니다. 그리고 나서 모듈 로더는 OrdersModule 을 로드하고 초기화합니다.
  2. OrdersModule 초기화 과정중에, 그것은 OrdersRepository 를 컨테이너에 등록합니다.
  3. 그리고 나서 모듈 로더는 CustomersModule 을 로드합니다. 모듈 로딩의 순서는 모듈 메타데이터의 종속성들에 의해 지정될 수 있습니다.
  4. CustomersModule CustomersViewModel 의 인스턴스를 컨테이너를 통해 그것을 결정( resolve )함으로써 생성합니다. 그 CustomersViewModel OrdersRepository 에 ( 일반적으로 인터페이스에 기반해 ) 종속성을 가지고 있으며, 생성자 주입이나 종속성 주입을 통해서 그것을 지정합니다. 컨테이너는 OrdersModule 에 의해 등록된 타입에 기반한 뷰 모델을 생성하는 와중에 종속성을 주입합니다. 최종 결과물은 CustomerViewModel 에서 OrderRepository 로의 인터페이스 레퍼런스이며, 두 클래스들 간에는 강한 결합이 존재하지 않습니다.
노트:
OrderRepository( IOrderRespository ) 를 노출하는 인터페이스는 분리된 "공유 서비스들" 어셈블리나 "주문 서비스들" 어셈블리에 존재할 수도 있습니다. 그 어셈블리들은 단지 서비스 인터페이스들과 그 서비스들을 노출하기 위해 요구되는 타입들 만을 포함하고 있습니다. 이러한 방식으로, CustomersModuleOrdersModule 사이에는 강한 종속성이 존재하지 않습니다.


두 모듈은 공히 묵시적인 종속성을 종속성 주입 컨테이너 상에서 가지고 있음에 주의하십시오. 이 종속성은 모듈 로더에서 모듈 생성을 하는 동안에 주입됩니다.


핵심 시나리오들



이 섹션에서는 응용프로그램에서 모듈을 사용해서 작업할 때 직면하게 될 공통적인 시나리오들에 대해 기술합니다. 이 시나리오들은 모듈 정의하기, 모듈 등록하고 검색하기, 모듈 로드하기, 모듈 초기화하기, 모듈 종속성 지정하기, 요청시 모듈 로드하기, 백그라운드로 원격 모듈 다운로드하기, 모듈이 이미 로드되었는지 확인하기 등이 포함됩니다. 코드나 XAML, 혹은 응용프로그램 구성 파일, 혹은 로컬 디렉토리 스캐닝을 통해서 모듈을 등록하고 검색할 수 있습니다.


모듈 정의하기


모듈은 개별적으로 개발되고 테스트되고 배포되고 응용프로그램에 통합될 수 있는 방식으로 패키징되는 기능 및 리소스에 대한 논리 컬렉션입니다. 각 모듈은 모듈을 초기화하고 그것의 기능을 응용프로그램에 통합하기 위한 책임이 있는 중앙 클래스를 가지고 있습니다. 이 클래스는 다음과 같이 IModule 인터페이스를 구현합니다.



Initialize 메서드를 구현하는 방법은 응용프로그램의 요구에 달려 있습니다. 모듈 클래스 타입, 초기화 모드, 그리고 모든 모듈 종속성들은 모듈 카탈로그에 정의됩니다. 카탈로그 안의 각 모듈에 대해, 모듈 로더는 모듈 클래스의 인스턴스를 생성하고, 그 다음 Initialize 메서드를 호출합니다. 모듈들은 모듈 카탈로그에서 지정된 순서로 처리됩니다. 런타임 초기화 순서는 모듈들이 다운로드되고, 이용가능해지고, 종속성들을 만족시킬 때가 언제인지에 기반합니다.


응용프로그램이 사용중인 모듈 카타로그의 타입에 기반해서, 모듈 클래스 자체의 선언적인 애트리뷰트들에 의해서 모듈 종속성들이 설정되거나, 모듈 카탈로그 파일 내부에서 모듈 종속성들이 설정될 수 있습니다. 다음 섹션들은 세부 사항들을 제공합니다.


모듈을 등록하고 검색하기


응용프로그램이 로드할 수 있는 모듈들은 모듈 카탈로그에 정의됩니다. 프리즘 모듈 로더는 모듈 카탈로그를 사용해서 응용프로그램에 로드될 수 있는 모듈이 무엇인지, 언제 그것을 로드할지, 어떤 순서로 로드할지를 결정합니다.


모듈 카탈로그는 IModuleCatalog 인터페이스를 구현하는 클래스에 의해 표현됩니다. 모듈 카탈로그 클래스는 응용프로그램 부트스트래퍼 클래스에 의해서 응용프로그램 초기화 동안에 생성됩니다. 프리즘은 모듈 카탈로그의 여러 구현을 제공하기 때문에, 여러 분은 그것을 선택할 수 있습니다. 또한 AddModule 메서드를 호출하거나 사용자 정의 행위를 가진 모듈 카탈로그를 생성하기 위해 ModuleCatalog 를 상속함으로써, 다른 데이터 소스로부터 모듈 카탈로그를 덧붙일 수 있습니다.


노트:

일반적으로, 프리즘에서의 모듈은 종속성 주입 컨테이너와 공용 서비스 로케이터를 사용해서 모듈 초기화를 위해 요구되는 타입들의 인스턴스들을 획득합니다. 유니티와 MEF 는 모두 프리즘에 의해 지원됩니다. 비록 등록, 검색, 다운로딩, 초기화의 전체 과정이 동일하지만, 그 세부사항은 유니티가 사용되느냐 아니면 MEF 가 사용되느냐에 따라서 매우 달라질 수 있습니다. 접근법들 사이의 컨테이너-지정 차이점들은 이 토픽을 통해 설명됩니다.


코드에서 모듈 등록하기


가장 기본적인 모듈 카탈로그는 ModuleCatalog 클래스에 의해 제공됩니다. 이 모듈 카탈로그를 사용해서 여러 분은 모듈 클래스 타입을 지정함으로써 모듈들을 프로그램적으로 등록할 수 있습니다. 또한 프로그램적으로 모듈 이름과 초기화 모드를 지정할 수도 있습니다. ModuleCatalog 클래스에 모듈을 직접적으로 등록하기 위해서는, AddModule 메서드를 응용프로그램의 Bootstrapper 클래스에서 호출하십시오. 그 예는 다음 코드에 나와 있습니다.



앞의 예제에서, 모듈들은 쉘에 의해 직접적으로 참조되며, 그 모듈 클래스 타입들이 정의되어, AddModule 에 대한 호출에서 사용될 수 있습니다. 이것이 예제가 모듈들을 카탈로그에 등록하기 위해 typeof( Module ) 을 사용하는 이유입니다.


노트:

만약 응용프로그램이 모듈 타입에 대한 직접 참조를 가지고 있다면, 여러 분은 위에서 보이듯이 타입에 의해 그것을 추가할 수 있습니다; 그렇지 않다면 fully qualified type name 과 어셈블리의 위치를 제공할 필요가 있습니다.


코드에서 모듈 카탈로그를 정의하는 다른 예제를 보고 싶다면, Stock Trader Reference Implementation( Stock Trader RI ) 의 StockTraderRIBootstrapper.cs 를 살펴 보십시오.


노트:

Bootstrapper 클래스는 CreateModuleCatalog 메서드를 제공해서 ModuleCatalog 의 생성을 돕도록 합니다. 기본적으로, 이 메서드는 ModuleCatalog 인스턴스를 생성합니다. 그러나 다른 유형의 모듈 카탈로그를 생성하기 위해, 상속 클래스에서 이 메서드를 재정의할 수 있습니다.


XAML 파일을 사용해서 모듈 등록하기


XAML 파일에서 모듈 카탈로그를 지정함으로써 모듈 카탈로그를 선언적으로 정의할 수 있습니다. XAML 파일은 어떤 종류의 모듈 카탈로그 클래스를 생성하고, 어떤 모듈들이 그것에 추가되어야 하는지를 지정합니다. 보통, .xaml 파일은 쉘 프로젝트의 리소스로 추가됩니다. 모듈 카탈로그는 부트스트래퍼에 의해 생성되는데, CreateFromXaml 메서드를 호출해야 합니다. 기술적인 관점에서 볼 때, 이 접근법은 코드에서 ModuleCatalog 를 정의하는 것과 매우 유사합니다. 왜냐하면 XAML 파일은 단순히 인스턴스화될 오브젝트의 계층을 정의할 뿐이기 때문입니다.


다음 코드 예제는 XAML 파일이 모듈 카탈로그를 정의하는 것을 보여 줍니다.



노트:

ModuleInfoGraoups 는 같은 .xap 파일이나 어셈블리에 존재하고, 같은 방식으로 초기화되고, 같은 그룹 내의 모듈에 대한 종속성만을 가지는 모듈들을 그룹화하는 편리한 방법을 제공합니다. 

모듈들 간의 종속성들은 같은 ModuleInfoGroup 의 모듈들 내에서 정의될 수 있습니다; 그러나, 다른 ModuleInfoGroups 내의 모듈들 사이의 종속성을 정의할 수는 없습니다.

모듈들을 모듈 그룹 내에 배치하는 것은 선택적입니다. 그룹을 위해서 설정되는 프라퍼티들은 그것이 포함하는 모든 모듈들에 적용될 것입니다. 모듈들은 그룹 내부에 존재하지 않아도 등록될 수 있다는 것도 기억하기 바랍니다.


응용프로그램의 Bootstrapper 클래스에서, XAML 파일이 ModuleCatalog 의 소스임을 지정할 필요가 있습니다. 그 예가 다음 코드에 나와 있습니다.



구성 파일을 사용해서 모듈을 등록하기


WPF 에서는, 모듈 정보를 App.config 파일에 저장하는 것이 가능합니다. 이 접근법은 이점은 이 파일이 응용프로그램으로 컴파일되지 않는다는 것입니다. 이는 응용프로그램을 재컴파일하지 않고도 런타임에 모듈을 추가하거나 삭제하는 것을 쉽게 만들어 줍니다.


다음 코드 예제는 모듈 카탈로그를 지정하는 구성 파일을 보여 줍니다. 만약 모듈이 자동으로 로드되는 것을 원한다면, startupLoaded="true" 를 설정하면 됩니다.



노트:

여러 분은 여전히 코드상에서 ConfigurationModuleCtalog 에다가 모듈들을 추가할 수 있습니다. 예를 들어, 응용프로그램이 반드시 기능하기를 바라는 모듈들이 카탈로그에 정의되도록 하기 위해서 이를 사용할 수 있습니다.


노트:

실버라이트는 구성 파일들을 사용하는 것을 지원하지 않습니다. 만약 구성파일 형식의 접근법을 원한다면, 자신만의 ModuleCatalog 를 생성해서 서버의 웹 서비스로부터 모듈 구성파일을 읽어 들이는 접근법을 추천합니다.


디렉토리에서 모듈 검색하기


WPF 에서 프리즘 DirectoryModuleCatalog 클래스는 모듈 카탈로그로 로컬 디렉토리를 지정하도록 해 줍니다. 이 모듈 카탈로그는 지정된 폴더를 스캔하고 응용프로그램을 위한 모듈을 정의하는 어셈블리들을 검색합니다. 이 접근법을 사용하려면, 모듈 이름과 그것이 가지는 모든 종속성을 지정하기 위해서 모듈 클래스에다가 선언적인 애트리뷰트를 사용할 필요가 있습니다. 다음 코드 예제는 디렉토리에서 어셈블리들을 검색해서 덧붙여진 모듈 카탈로그를 보여 줍니다.



노트:

이 기능은 실버라이트에서는 지원되지 않습니다. 왜냐하면 실버라이트 보안 모델은 어셈블리를 파일 시스템에서 로드하는 것을 허용하지 않기 때문입니다.


모듈 로드하기


ModuleCatalog 가 덧붙여지면, 모듈들이 로드되고 초기화될 준비가 된 것입니다. 모듈 로딩은 모듈 어셈블리가 디스크에서 메모리로 이동되었음을 의미합니다. 만약 어셈블리가 디스크에 존재하지 않는다면, 먼저 그걳을 획득해야만 합니다. 이것의 예는 실버라이트 .xap 파일을 사용해서 웹으로부터 어셈블리를 다운로드하는 것입니다. ModuleManager 는 로딩 및 초기화 과정을 다룰 책임이 있습니다.


모듈 초기화하기


모듈이 로드된 후에는, 그것들이 초기화됩니다. 이는 모듈 클래스의 인스턴스가 생성되고 그것의 Initialize 메서드가 호출되는 것을 의미합니다. 초기화는 응용프로그램에 모듈을 통합하는 장소입니다. 모듈 초기화를 위해 다음과 같은 가능성들을 고려하십시오:

  • 모듈의 뷰를 응용프로그램에 등록하십시오. 만약 모듈이 뷰 검색이나 뷰 주입을 사용해 유저 인터페이스( UI ) 조립하는 것이라면, 모듈은 그것의 뷰나 뷰 모델을 적절한 영역( region ) 이름과 연관시킬 필요가 있습니다. 이는 뷰가 메뉴에서 동적으로 나타날 수 있도록 해 주거나, 응용프로그램의 다른 비주얼 영역에서 나타날 수 있도록 해 줍니다.
  • 응용프로그램 레벨 이벤트나 서비스를 구독하십시오. 보통, 응용프로그램은 여러분의 모듈이 관심을 가지게 되는 응용프로그램-지정 서비스나 이벤트를 노출합니다. 모듈의 기능들을 그러한 응용프로그램-레벨 이벤트와 서비스에 추가하기 위해서는 Initialize 메서드를 사용하십시오. 예를 들어, 응용프로그램은 아마도 그것이 종료될 때나 여러분의 모듈이 그 이벤트에 반응하기를 원할 때 이벤트를 발생시킬 것입니다. 또한 여러분의 모듈은 응용프로그램 레벨 서비스에 어떤 데이터를 제공해야만 할 가능성도 있습니다. 예를 들어, 만약 MenuService( 이것은 메뉴 아이템을 추가하거나 삭제할 책임이 있습니다 )를 생성했을 때, 모듈의 Initialize 메서드가 여러분이 올바른 메뉴 아이템을 추가할 위치입니다.

노트:

모듈 인스턴스 라이프타임은 기본적으로 매우 짧습니다. 로딩 과정 동안 Initialize 메서드를 호출한 후에, 모듈 인스턴스에 대한 참조가 해제됩니다. 만약 모듈 인스턴스에 대한 강한 참조 체인을 구성하지 않는다면, 그것은 가비지 수집될 것입니다.

이 동작은 여러분이 모듈에 대한 약한 참조를 유지하는 이벤트를 구독하고 있을 때 디버깅을 어렵게 만듭니다. 왜냐하면 여러분의 모듈은 가비지 수집기가 실행될 때 이미 "사라지기" 때문입니다.

  • 종속성 주입 컨테이너에 타입들을 등록하십시오. 만약 유니티나 MEF 와 같은 종속성 주입 패턴을 사용하고 있다면, 모듈은 아마도 응용프로그램이나  사용할 다른 모듈들을 위한 타입들을 등록할 것입니다. 또한 그것은 컨테이너에 그것이 필요로 하는 타입의 인스턴스를 결정해 줄 것을 요구할 것입니다.

모듈 종속성들을 지정하기


모듈들은 다른 모듈들에 의존합니다. 만약 모듈 A 가 모듈 B 에 의존한다면, 모듈 B 는 반드시 모듈 A 전에 초기화되어야 합니다. ModuleManager 는 이러한 종속성들에 대한 트랙을 유지하며, 모듈을 적절히 초기화합니다. 여러분이 모듈 카탈로그를 정의했던 방식에 의존해, 모듈 종속성을 코드, 구성 파일, XAML 에서 정의할 수 있습니다.


코드에서 종속성들을 지정하기


코드에서 모듈을 등록하거나 디렉토리에 의해 모듈들을 검색하는 WPF 응용프로그램을 위해, 프리즘은 모듈을 생성할 때 사용하기 위한 선언적 애트리뷰트들을 제공합니다. 그 예가 아래에 나와 있습니다.



XAML 에서 종속성들을 지정하기


다음 XAML 은 모듈 F 가 모듈 E 에 의존하는 것을 보여 줍니다.



구성파일에서 종속성들을 지정하기


다음 App.config 파일 예제는 모듈 D 가 모듈 B 에 의존함을 보여 줍니다.



온디맨드로( 요청시에 ) 모듈을 로드하기


모듈을 온디맨드로 로드하려면, 모듈이 InitializeMode OnDemand 로 설정한 상태로 모듈 카탈로그에 로드되어야 합니다. 그렇게 하고 나서, 모듈이 로드될 것을 요청하는 응용프로그램 코드를 작성할 필요가 있습니다.


코드에서 온디맨드 로딩을 지정하기


모듈은 애트리뷰트를 사용해서 온디맨드로 지정되는데, 아래 코드 예제를 보십시오.



XAML 에서 온디맨드 로딩 지정하기


XAML 에서 모듈 카탈로그를 정의할 때, InitializationMode.OnDemand 를 지정할 수 있습니다. 아래 코드 예제를 보십시오.



구성파일에서 온디맨드 로딩 지정하기


App.config 파일에서 모듈 카탈로그를 정의할 때 InitializationMode.OnDemand 를 지정할 수 있습니다. 아래 코드 예제를 보십시오.



모듈의 온디맨드 로딩 요청하기


모듈이 온디맨드로 지정되면, 응용프로그램은 모듈이 로드될 것을 요청할 수 있습니다. 로딩을 초기화하기 원하는 코드는 부트스트래퍼에 의해 컨테이너에 등록된 IModuleManager 서비스에 대한 참조를 획득할 필요가 있습니다.



백그라운드로 원격 모듈들을 다운로드하기


응용프로그래 시작 후나 사용자가 모듈을 필요로 할 때 백그라운드로 모듈들을 다운로드하는 것은 응용프로그램 시작 시간을 개선해 줍니다.


원격 다운로드를 위해 모듈을 준비하기


실버라이트 응용프로그램에서, 모듈들은 .xap 파일들에 패키징됩니다. 응용프로그램으로부터 분리된 모듈을 다운로드하기 위해서는 개별적인 .xap 파일을 생성하십시오. 여러분은 다중 모듈들을 하나의 .xap 파일에 배치해서 다운로드 요청의 개수를 최적화하거나, 각 .xap 파일의 다운로드 사이즈를 최적화할 수 있습니다.


노트:

각 .xap 파일들에 대해, 새로운 실버라이트 응용프로그램 프로젝트를 생성할 필요가 있습니다. 비주얼 스튜디오 2008 과 2010 에서는, 하나의 응용프로그램 프로젝트만 .xap 파일을 만들 수 있습니다. 이런 프로젝트들 내에서 App.xaml 이나 MainPage.xaml 파일들을 만들 필요는 없을 것입니다.


다운로드 과정을 트래킹하기


ModuleManager 클래스는 응용프로그램을 위해서 모듈에 대한 다운로드 과정을 추적하기 위한 이벤트를 제공합니다. 이는 다운로드된 바이트 대 전체 받은 바이트를 제공하며, 다운로드 과정의 퍼센트도 제공합니다. 이를 사용해서 비주얼 프로그레스 인디케이터를 출력해 사용자에게 다운로드되는 과정을 보여 줄 수 있습니다.




모듈이 다 로드되었는지를 확인하기


ModuleManager 서비스는 응용프로그램을 위해서 모듈이 로드되거나 로드에 실패했을 때를 추적하기 위한 이벤트를 제공합니다. IModuleManager 인터페이스의 종속성 주입을 통해서 이 서비스에 대한 참조를 획득할 수 있습니다.




응용프로그램과 모듈의 느슨한 결합을 유지하기 위해서, 응용프로그램은 이 이벤트를 모듈을 응용프로그램에 통합하기 위한 목적으로 사용하는 것을 피해야만 합니다. 그 대신에, 모듈의 Initialize 메서드가 응용프로그램과의 통합을 다뤄야 합니다.


LoadModuleCompletedEventArgs IsErrorHandled 프라퍼티를 포함합니다. 만약 모듈을 로드하는데 실패했는데, 응용프로그램이 ModuleManager 가 에러를 로깅하거나 예외를 던지지 않기를 원한다면, 이 프라퍼티를 true 로 설정할 수 있습니다.


노트:

모듈이 로드되고 초기화된 후에, 그 모듈 어셈블리는 언로드될 수 없습니다. 모듈 인스턴스 참조는 프리즘 라이브러리에 의해 유지되지 않을 것입니다. 그래서 모듈 클래스 인스턴스는 초기화가 완료되면 가비지 수집될 것입니다.


MEF 에서 모듈들


이 섹션은 당신이 종속성 주입 컨테이너로서 MEF 를 선택했을 때의 차이점에만 집중합니다.


노트:

MEF 를 사용할 때, MefModuleManager MefBootstrapper 에 의해 사용됩니다. 이는 ModuleMnager 를 확장하고, IPartImportsSatisfiedNotification 인터페이스를 구현하여, MEF 에 의해 새로운 타입들이 임포트될 때 ModuleCatalog 가 갱신되도록 보장합니다.


MEF 를 사용하여 코드에서 모듈들을 등록하기


MEF 를 사용할 때, 여러분은 MEF 가 자동으로 타입들을 검색하도록 만들기 위해서 모듈 클래스에 ModuleExport 애트리뷰트를 적용할 수 있습니다. 다음은 그 예제입니다.



여러분은 AssemblyCatalog 클래스를 사용해서 모듈들을 검색하고 로드하도록 할 수도 있습니다. 이 클래스는 어셈블리 내에서 익스포드되는 모든 모듈 클래스들을 검색하기 위해서 사용될 수 있습니다. 그리고 AggregateCatalog 클래스는 하나의 논리 카탈로그에 다중 카탈로그들이 결합될 수 있도록 해 줍니다. 기본적으로, 프리즘 MefBootstrapper 클래스는 AggregateCatalog 인스턴스를 생성합니다. 그러면 여러분은 ConfigureAggregateCatalog 메서드를 재정의해서 어셈블리들을 등록할 수 있습니다. 그 예가 아래에 나와 있습니다.



프리즘 MefModuleManager 구현은 MEF AggregateCatalog 와 프리즘 ModuleCatalog 가 동기화되도록 유지합니다. 결국, ModuleCatalog AggregateCatalog 를 통해 추가된 모듈들을 프리즘이 검색할 수 있도록 해 줍니다.


노트:

MEF 는 Lazy<T> 를 확장해서 사용하는데, 익스포트되거나 임포트된 타입들의 초기화를 Value 프라퍼티가 사용되기 전까지 지연시키기 위해서입니다.


MEF 를 사용하여 디렉토리 내의 모듈들을 검색하기


MEF 는 모듈들을 포함하고 있는 어셈블리들( 그리고 다른 MEF 익스포트 타입들 )을 위해 디렉토리를 검색하는 데 사용되는 DirectoryCatalog 를 제공합니다. 이 경우, 여러분은 ConfigureAggregateCatalog 메서드를 재정의햇, 디렉토리를 등록하게 됩니다. 이 접근법은 WPF 에서만 이용 가능합니다.


이 접근법을 사용하기 위해서는, 먼저 ModuleExport 애트리뷰트를 사용해 모듈에 모듈 이름들과 종속성들을 적용할 필요가 있습니다. 아래 코드에 그 예가 나와 있습니다. 이는 MEF 가 모듈들을 임포트할 수 있도록 해 주고, 프리즘이 ModuleCatalog 를 갱신할 수 있도록 해 줍니다.



MEF 를 사용해 코드에서 종속성들을 지정하기


MEF 를 사용하는 WPF 응용프로그램에 대해, ModuleExport 애트리뷰트를 아래와 같이 사용하십시오.



MEF 는 런타임에 모듈들을 검색하도록 해 주기 때문에, 모듈들 간의 새로운 종속성도 런타임에 검색될 수 있습니다. ModuleCatalog 를 MEF 와 함께 사용할 수 있기는 하지만, ModuleCatalog 는 XAML 이나 구성 파일로부터 그것이 로드될 때 ( 모듈이 로드되기 전에 ) 종속성 체인에 대한 유효성 검사를 수행한다는 것을 기억하는 것이 중요합니다. 만약 ModuleCatalog 에 모듈이 열거되어 있고 MEF 를 사용하여 로드된다면, ModuleCatalog 종속성들이 사용될 것이며, DependsOnModuleNames 애트리뷰트가 무시될 것입니다. 개별 XAP 파일에 모듈들을 가지고 있는 실버라이트 응용프로그램에서는 ModuleCatalog 와 함께 MEF 를 사용하는 것이 가장 일반적입니다.


MEF 를 사용해 온디맨드 로딩을 지정하기


MEF 를 사용하고 모듈들과 모듈 종속성들을 지정하기 위해서 ModuleExport 애트리뷰트를 사용하고 있다면, 온디맨드로 로드되어야 하는 모듈을 지정하기 위해서 InitializationMode 프라퍼티를 사용할 수 있습니다. 다음 예제를 보십시오.



MEF 를 사용해 원격 다운로드를 위한 모듈을 준비하기


내부적으로 MEF 를 사용하는 프리즘 응용프로그램은 MEF 의 DeploymentCatalog 클래스를 사용하여 .xap 파일들을 다운로드하고 그 .xap 파일들 내의 어셈블리들과 타입들을 검색합니다. MefXapModuleTypeLoader AggregateCatalog 에 각 DeploymentCatalog 를 추가합니다.


두 가지 다른 .xap 파일들이 추가되고 같은 공유 어셈블리들을 포함한다면, 같은 타입들이 다시 임포트됩니다. 이는 타입이 싱글톤으로 지정되었는데 모듈들 간에 공유되는 어셈블리에 존재할 때 recomposition 에러를 발생시킬 수 있습니다. Microsoft.Practices.Prism.MefExtensions.dll 은 그런 어셈블리의 예입니다.


중복 임포트를 막기 위해서는, 각 모듈 프로젝트의 참조를 열고, 그러한 공유 DLL 들에 대해 'Copy Local'=false 로 설정하십시오. 이는 모듈의 .xap 파일에 그 어셈블리가 패키징되는 것을 막으며, 다시 임포트되는 것을 막습니다. 또한 각 .xap 파일의 전체적인 크기도 줄여 줍니다. 이를 위해서, 모듈 .xap 파일이 다운로드되기 전에, 응용프로그램이 공유 어셈블리를 참조하거나 응용프로그램이 공유 어셈블리를 포함하는 .xap 파일을 다운로드하는 것을 보장할 필요가 있습니다.


더 많은 정보들



어셈블리 캐싱에 대한 더 많은 정보를 원한다면, MSDN 의 "방법: 어셈블리 캐싱 사용하기" 를 참조하심시오: http://msdn.microsoft.com/en-us/library/dd833069(VS.95).aspx


프리즘의 모듈성에 대한 더 많은 정보를 원한다면, Modularity with MEF for WPF QuickStart 나 Modularity with Unity for WPF QuickStart 를 참조하십시오. QuickStart 에 대한 더 많은 정보를 원한다면, 도움말의 Modularity QuickStarts for WPF 를 참조하십시오.


프리즘 라이브러리에서 확장될 수 있는 모듈성 기능들에 대한 더 많은 정보를 원한다면, 도움말에서 "Extending Prism" 의 "Modules" 를 참조하십시오.


'Programming > Prism4.1' 카테고리의 다른 글

진보된 MVVM 시나리오들  (0) 2014.09.20
MVVM 패턴 구현하기  (0) 2014.09.16
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
프리즘 응용프로그램 초기화하기  (0) 2014.08.24
왜 프리즘을 사용하는가?  (0) 2014.08.19
소개  (0) 2014.08.18
프리즘 4.1 설치  (0) 2014.08.14

도움말 번역입니다



부트스트래퍼란 무엇인가?




부트스트래퍼Bootstrapper란 프리즘 라이브러리를 사용하여 빌드된 응용프로그램의 초기화에 책임이 있는 클래스입니다. 부트스트래퍼를 사용함으로써, 당신은 프리즘 라이브러리 컴포넌트들이 응용프로그램과 연결되는 방식을 더 많이 제어할 수 있습니다.


프리즘 라이브러리는 기본 추상 Bootstraper 기저 클래스를 포함하는데, 이는 다른 컨테이너들을 사용하기 위해서 특수화될 수도 있습니다. 부트스트래퍼 클래스들 상의 많은 메서드들은 가상 함수입니다. 당신은 이러한 메서드들을 당신만의 커스텀 부트스트래퍼 구현을 위해 적절하게 재정의할 수 있습니다.



프리즘 라이브러리는 몇 개의 부가적인 기저 클래스들을 제공하는데, 이것들은 Bootstrapper 를 상속합니다. 그리고 대부분의 응용프로그램들에 적합한 기본 구현들을 가지고 있습니다. 당신의 응용프로그램 부트스트래퍼에서 구현하도록 남겨진 스테이지는 쉘을 생성하고 초기화하는 것 뿐입니다.


의존성 주입Dependency Injection



프리즘 라이브러리를 사용해 빌드된 응용프로그램들은 컨테이너가 제공하는 의존성 주입에 의존합니다. 이 라이브러리는 유니티 응용프로그램 블락( Unity Application Block, Unity )이나 관리되는 확장성 프레임워크( Managed Extensibility Framework, MEF ) 를 사용해 동작하는 어셈블리들을 제공하며, 그것은 당신이 다른 의존성 주입 컨테이너들을 사용하는 것을 허용해 줍니다. 부트스트래핑 과정 중의 일부는 이 컨테이너를 구성하고 컨테이너에 타입들을 등록하는 것입니다.


프리즘 라이브러리는 UnityBootstrapperMefBootstrapper 클래스들을 포함하는데, 이것들은 Unity 나 MEF 를 사용하기 위해서 필요한 거의 대부분의 기능들을 당신의 응용프로그램 내의 의존성 주입 컨테이너로서 구현합니다. 이전 그림에서 보여 준 이 스테이지와는 별개로, 각 부트스트래퍼들은 자신의 컨테이너가 무엇이냐에 따라 몇 개의 단계를 추가합니다.


Shell 생성하기



일반적인 윈도우즈 프리젠테이션 파운데이션( Windows Presentation Foundation, WPF ) 응용프로그램에서, 메인 윈도우를 띄우는 App.xaml 파일에서 스타트업 통합 리소스 식별자( Uniform Resource Identifier, URI )가 지정됩니다. 실버라이트 응용프로그램에서는, 응용프로그램상의 RootVisual  속성이 App.xaml 파일의 코드 비하인드에서 설정됩니다.


프리즘 라이브러리를 사용해서 생성된 응용프로그램에서, 쉘이나 메인 윈도우를 생성하는 책임은 부트스트래퍼에게 있습니다. 그것은 쉘이 영역 관리자Region Manager와 같은 서비스들에 의존하며, 그것들은 쉘이 디스플레이되기 전에 등록될 필요가 있기 때문입니다.


중요한 결정들



응용프로그램에서 프리즘 라이브러리를 사용하겠다고 결정한 후에, 몇 가지 부가적인 결정들을 해야 할 필요가 있습니다:


  • 의존성 주입 컨테이너를 위해 MEF 컨테이너를 사용할지 Unity 컨테이너를 사용할지, 아니면 다른 컨테이너를 사용할지 결정해야 할 것입니다. 이는 당신이 사용해야만 할 부트스트래퍼 클래서가 무엇인지를 결정하게 할 것이고, 다른 컨테이너를 위한 부프트스트래퍼를 생성할 필요가 있는지 여부를 결정하게 할 것입니다.
  • 응용프로그램에서 사용하기 원하는 특별한 서비스들이 있는지 생각해 봐야 합니다. 그 서비스들은 컨테이너에 등록될 필요가 있습니다.
  • 어떤 빌트인 로깅 서비스가 당신의 필요에 맞는지, 혹은 다른 로깅 서비스를 생성해야할 필요가 있는지 결정해야 합니다.
  • 응용프로그램이 모듀들을 어떻게 발견할지를 결정해야 합니다: 명시적인 코드 선언들을 통해서, 디렉토리 스캐닝을 통해 발견된 모듈들 상의 코드 애트리뷰트들을 통해서, configuration 을 통해서, XAML 을 통해서.
나머지 챕터들은 좀 더 세부적인 내용을 제공합니다.

코드 시나리오들



스타트업 시퀀스를 생성하는 것은 프리즘 응용프로그램을 빌드하는 매우 중요한 과정입니다. 이 섹션은 부트스트래퍼를 생성하는 방법과, 쉘을 생성하고 의존성 주입 컨테이너를 구성하고 응용프로그램 수준 서비스들을 등록하기 위해서 부트스트래퍼를 커스터마이즈하는 방법에 대해서 설명합니다. 그리고 모듈들을 로드하고 초기화하는 방법에 대해 설명합니다.


응용프로그램을 위해서 부트스트래퍼 생성하기



의존성 주입 컨테이너로서 Unity 나 MEF 를 선택했다면, 응용프로그램을 위한 간단한 부트스트래퍼를 생성하는 것은 쉽습니다. MefBootstrapperUnityBootstrapper 를 상속하는 새로운 클래스를 생성하기만 하면 될 것입니다. 그리고 CreateShell 메서드를 구현합니다. 추가적으로 InitializeShell 메서드를 특별한 쉘 초기화를 위해 재정의할 수도 있습니다.


CreateShell 메서드 구현하기



CreateShell 메서드는 개발자가 프리즘 응용프로그램을 위한 최상위 수준 윈도우를 지정할 수 있도록 허용합니다. 그 쉘은 보통 MainWindow 이거나 MainPage 입니다. 이 메서드를 응용프로그램의 쉘 클래스들 중의 인스턴스 중 하나를 반환하도록 구현하십시오. 프리즘 응용프로그램에서, 당신은 쉘 오브젝트를 생성하거나 그것을 컨테이너로부터 가지고 올 수 있습니다. 이는 응용프로그램의 요구사항에 의존합니다.


ServiceLocator 를 사용해서 쉘 오브젝트를 가지고 오는 예제가 아래 코드 예제에서 보여집니다.



노트 :


당신은 종종 특정 의존성 주입 컨테이너 대신에 ServiceLocator 가 타입들의 인스턴스들을 가지고 오기 위해서 사용되는 것을 보게 될 것입니다. ServiceLocator 는 컨테이너를 호출함으로써 구현되므로, 컨테이너 불가지론적 코드를 위해서는 그것이 좋은 선택입니다. 당신은 ServiceLocator 대신에 컨테이너를 직접적으로 참조하고 사용할 수도 있습니다.


InitializeShell 메서드 구현하기



쉘을 생성했다면, 쉘이 디스플레이될 준비가 되었는지를 명확하게 하기 위한 초기화 단계를 수행할 필요가 있습니다. 당신이 WPF 를 작성하느냐 실버라이트를 작성하느냐에 따라, InitializeShell 메서드 구현은 다양해질 것입니다. 실버라이트 응용프로그램을 위해서, 당신은 아래에 보이는 것처럼, 응용프로그램의 visual root 로서 쉘을 설정할 수 것입니다.



WPF 응용프로그램을 위해서, 당신은 아래에 보이는 것처럼, 쉘 응용프로그램 오브젝트를 생성하고, 그것을 응용프로그래의 메인 윈도우로서 설정할 것입니다( WPF 를 위한 모듈성 퀵스타트의 코드 중 일부 ).



IniializeShell 의 기저 구현은 아무것도 수행하지 않습니다. 기저 클래스 구현을 호출하지 않는 것이 안전합니다.


모듈 카탈로그를 생성하고 구성하기



모듈 응용프로그램을 빌드하고 있다면, 모듈 카탈로그를 생성하고 빌드할 필요가 있을 것입니다. 프리즘은 견고한 IModuleCatalog 인스턴스를 사용해, 응용프로그램에 대해 이용 가능한 모듈들이 무엇인지, 어떤 모듈들이 다운로드될 수 있는지, 어디에 모듈들이 위치하는지에 대한 트랙을 유지합니다.


Bootstrapper 는 카탈로그와 CreateModuleCatalog 가상 메서드에 대한 기저 구현을 참조하기 위해 보호된 ModuleCatalog 속성을 제공합니다. 기저 구현은 새로운 ModuleCatalog 를 반환합니다; 그러나 이 메서드는, 실버라이트를 위한 MEF 를 사용한 모듈성 퀵스타트의 QuickStartBootstrapper 로부터의 코드에서 볼 수 있듯이, 다른 IModuleCatalog 인스턴스를 제공하기 위해서 재정의될 수 있습니다. 



UnityBootstrapperMefBootstrapper 클래스들에서, Run 메서드는 CreateModuleCatalog 메서드를 호출하며, 클래스들의 ModuleCatalog 속성은 그 반환값을 사용해서 설정됩니다. 만약 이 메서드를 재정의하면, 기저 클래스의 기저 구현을 호출할 필요가 없습니다. 왜냐하면 당신은 제공되는 기능을 대체할 것이기 때문이다. 모듈성에 대한 더 많은 정보를 원한다면, 도움말의 "Modular Application Development" 를 참조하십시오.


컨테이너를 생성하고 구성하기



컨테이너들은 프리즘 라이브러리를 사용해서 생성된 응용프로그램에서 핵심적인 역할을 수행합니다. 컨테이너의 최상위에서 빌드된 프라즘 라이브러리와 응용프로그램은 요청된 종속성들과 서비스들을 주입inject하기 위해서 컨테이너에 의존합니다. 컨테이너 구성 단계 동안에, 몇몇 코어 서비스들이 등록됩니다. In addition to these core services, you may have application-specific services that provide additional functionality as it relates to composition.


코어 서비스들



다음 표는 프리즘 라이브러리 내의 core non-application specific service 들을 열거합니다.



 서비스 인터페이스

설명 

 IModuleManager

 응용프로그램의 모듈들을 획득하고 초기화할 서비스들에 대한 인터페이스들을 정의합니다.

 IModuleCatalog

 응용프로그램 내의 모듈들에 대한 메타데이터를 포함합니다. 프리즘 라이브러리는 몇 가지 다양한 카탈로그들을 제공합니다.

 IModuleInitializer

 모듈들을 초기화합니다.

 IRegionManager

 영역들을 등록하고 획득하는데, 영역들은 레이아웃을 위한 비쥬얼 컨테이너들입니다.

 IEventAggregator

 발행자와 구독자 사이에 느슨하게 연결된 이벤트들의 집합입니다.

 ILoggerFacade

 로깅 메커니즘을 위한 래퍼입니다. 자신만의 로깅 메커니즘을 선택할 수 있습니다. Stock Trader Reference Implementation( Staock Trader RI ) 은 EnterpriseLibraryLoggerAdapter 클래스를 통해서 엔터프라이즈 라이브러리 로깅 응용프로그램 블락을 사용하는데, 해당 응용프로그램은 자신만의 로거를 사용하는 방법에 대한 예제로 제공됩니다. 로깅 서비스는 부트스트래퍼의 Run 메서드에 의해 컨테이너에 등록되는데, CreateLogger 메서드의 반환값을 사용합니다. 컨테이너에 또 다른 로거를 등록하는 것은 불가능합니다; 대신에 부트스트래퍼의 CreateLogger 메서드를 재정의하십시오.

 IServiceLocator

 프리즘 라이브러리가 컨테이너에 접근할 수 있도록 합니다. 만약 라이브러리를 커스터마이즈하거나 확장하려고 한다면, 이것이 유용할 것입니다.


응용프로그램 특정 서비스들



다음 표는 응용프로그램 틍정 서비스들을 열거하는데, 그것들은 Stock Trader RI 에서 사용된 것들입니다. 이는 응용프로그램이 제공할 수 있는 서비스들의 유형을 이해하는 예제로서 사용될 수 있습니다.



 Stock Traader RI 의 서비스들

 설명

 IMarketFeedService

 실시간 ( 가상의 ) 마킷 데이터를 제공합니다. PositionSummaryPresentationModel 은 이 서비스로부터 획득한 통지에 기반한 position screen 을 갱신합니다.

 IMarketHistoryService

 선택된 펀드에 대한 트렌드 라인을 디스플레이하기 위해서 사용되는 히스토리컬 마킷 데이터를 제공합니다.

 IAccountPositionService

 포트폴리오에 있는 펀드들의 리스트를 제공합니다.

 IOrderService

 제출된 판매/구입 주문들을 넣습니다.

 INewsFeedService

 선택된 펀드에 대한 뉴스 아이템들의 리스트를 제공합니다.

 IWatchListService

 새로운 관심 아이템들이 관심 리스트에 추가될 때를 다룹니다.


두 종류의 Bootstrapper 를 상속한 클래스들이 프리즘에서 이용가능한데, 그것들은 UnityBootstrapperMefBootstrapper 입니다. 서로 다른 컨테이너들을 생성하고 구성하는 것은 서로 다르게 구현된 비슷한 개념들을 포함합니다.


UnityBootstrapper 에서 컨테이너를 생성하고 구성하기



UnityBootstrapper 클래스의 CreateContainer 메서드는 단순하게 UnityContainer 를 생성하고 그것의 인스턴스를 반환합니다. 대부분의 경우에, 이 동작을 변경할 필요는 없을 것입니다; 그러나 이 메서드는 가상 메서드이며, 그러므로 유연성을 가지고 있습니다.


컨테이너가 생성되면, 아마도 응용프로그램을 위해 구성될 필요가 있습니다. UnityBootstrapper 에서 ConfigureContainer 구현은 기본적으로 몇 개의 코어 프리즘 서비스들을 다음과 같이 등록합니다.


노트 :


이 예제는 모듈이 자신의 Initialize 메서드 내에서 모듈 수준 서비스들을 등록할 때의 것입니다.




부트스트래퍼의 RegisterTypeIfMissing 메서드는 서비스가 이미 등록되었는지 여부를 판단합니다 - 그것은 두 번 등록하지 않도록 할 것입니다. 이는 구성을 통해서 당신이 기본 등록을 재정의할 수 있도록 허용합니다. 또한 기본값으로 어떤 서비스들의 등록을 해제할 수도 있습니다; 이를 위해서는, 중복정의된 Bootstrapper.Run 메서드에 false 를 넘겨서 사용하십시오. 또한 ConfigureContainer 메서드를 재정의해서 이벤트 어그리게이터와 같은 원하지 않는 서비스들을 비활성화할 수도 있습니다.


노트 :


만약 기본 등록을 해제하고 싶다면, 수동적으로 요청된 서비스들을 등록해줄 필요가 있습니다.


ConfigureContainer 의 기본 동작을 확장하려면, 단순하게 당신의 응용프로그램의 부트스트래퍼에 재정의를 추가하고, 선택적으로 기저 구현을 호출합니다. 이는 ( Unity 를 사용하는 ) WPF 를 위한 모듈성 퀵스타트로부터 가져온 QuickStartBootstraper 의 코드에 나와 있습니다. 이 구현은 기저 클래스의 구현을 호출하며, IModuleTracker 의 완전한 구현인 ModuleTracker 타입을 등록합니다. 그리고 Unity 를 사용하는 CallbackLogger 의 싱글톤 인스턴스로서 callbackLogger 를 등록합니다.



MefBootstrapper 에서 컨테이너 생성하고 구성하기



MefBootstrapper 클래스의 CreateContainer 메서드는 몇 가지 일을 수행합니다. 먼저 그것은 AssemblyCatalog CatalogExportProvider 를 생성합니다. CatalogExportProvider MefExtension 어셈블리가 여러 프리즘 타입들에 대한 기본 익스포트를 수행할 수 있도록 해 주며, 여전히 당신으로 하여금 기본 타입 등록을 재정의할 수 있게 해 줍니다. CreateContainer CatalogExportProvider 를 사용해서 CompositionContainer 의 새로운 인스턴스를 생성하고 반환합니다. 대부분의 경우에, 이 동작을 변경할 필요는 없습니다; 그러나 이 메서드는 가상 메서드이고, 그러므로 유연성을 허용합니다.


노트 :


실버라이트에서는, 보안 제약때문에, 타입을 사용해서 어셈블리를 획득하는 것이 불가능합니다. 대신에 프리즘은 Assembly.GetCllingAssembly 메서드를 사용하는 다른 메서드를 사용합니다.


컨테이너가 생성되면, 응용프로그램을 위해 구성될 필요가 있습니다. MefBootstrapper 의 ConfigureContainer 구현은 다음 예제에 나와 있는 것처럼 기본적으로 몇 개의 코어 프리즘 서비스들을 등록합니다.만약 이 메서드를 재정의한다면, 코어 프리즘 서비스들을 등록하기 위해서 기저 클래스의 구현을 호출해야 할 것인지, 당신의 구현에 이 서비스들을 제공할 것인지 주의깊게 결정해야 합니다.



노트 :


MefBootstrapper 에서, 프리즘 코어 서비스들은 싱글톤으로 컨테이너에 추가되므로, 그것들은 응용프로그램 전체에서 컨테이를 통해서 찾을 수 있습니다.


CreateContainer 메서드와 ConfigureContainer 메서드를 제공하는 것과 더불어, MefBootstrapper 는MEF 에 의해 사용되는 AggregateCatalog 를 생성하고 구성하는 두 개의 메서드들도 제공합니다. CreateAggregateCatalog 메서드는 단순히 AggregateCatalog 오브젝트를 생성하고 반환하기만 합니다. MefBootstrapper 의 다른 메서드들과 마찬가지로, CreateAggregateCatalog 는 가상 메서드이며, 필요할 때 재정의될 수 있습니다.


ConfigureAggregateCatalog 메서드는 부득이한 경우에 AggregateCatalog 에 타입 등록을 추가할 수 있도록 허용합니다. 예를 들어 실버라이트를 위한 MEF 를 사용하는 모듈성 퀵스트타의 QuickStartBootstrapper 는 명시적으로 ModuleA ModuleC AggregateCatalog 에 다음과 같이 추가합니다.



더 많은 정보



MEF, AggregateCatalog, AssemblyCatalog 에 대한 더 많은 정보를 원한다면, MSDN 의 "Managed Extensibility Framework Overview" 를 참조하십시오 : http://msdn.microsoft.com/en-us/library/dd460648.aspx


'Programming > Prism4.1' 카테고리의 다른 글

진보된 MVVM 시나리오들  (0) 2014.09.20
MVVM 패턴 구현하기  (0) 2014.09.16
모듈러 응용프로그램 개발  (0) 2014.09.13
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
왜 프리즘을 사용하는가?  (0) 2014.08.19
소개  (0) 2014.08.18
프리즘 4.1 설치  (0) 2014.08.14

도움말 번역입니다



유연하고 유지보수가 쉬운 다채로운 WPF 나 실버라이트 클라이언트 응용프로그램들을 설계하고 빌드하는 것은 도전적인 일이 될 수 있습니다. 이 섹션에서는 WPF 나 실버라이트 응용프로그램들을 빌드할 때 만날지도 모르는 일반적인 시련들의 일부에 대해서 설명하고, 프리즘이 어떤 방식으로 그러한 시련들을 다루도록 도와주는 지에 대해 설명합니다.


클라이언트 응용프로그램 개발에서의 시련들


일반적으로, 클라이언트 응용프로그램 개발자들은 매우 많은 시련들에 직면합니다. 응용프로그램 요구사항들은 계속해서 바뀝니다. 새로운 비즈니스 기회들과 시련들을 스스로 겪을 수도 있으며, 새로운 기술들이 이용 가능하게 될 수도 있고, 혹은 심지어는 개발 도중에 소비자의 피드백이 응용프로그램의 요구사항들에 중요한 영향을 미칠 수도 있습니다. 결국, 응용프로그램을 유연하고 시간이 지나도 쉽게 수정하거나 확장할 수 있도록 빌드하는 것이 중요합니다. 이러한 유형의 유연성을 위한 설계를 하는 것은 매우 어려울 수 있습니다. 그것은 응용프로그램의 개발 부분들이 서로 독립적으로 개발되고, 검사되는 것을 요구하며, 응용프로그램의 다른 부분들에 영향을 미치지 않고 별개로 수정되고 갱신되기를 원합니다.


거의 대부분의 엔터프라이즈 응용프로그램들은 충분히 복잡하며, 그것들은 한 명 이상의 개발자나 심지어는 개발자와 함께 유저 인터페이스 디자이너나 로컬라이저를 포함하는  여러 개발자들로 구성된 큰 팀을 요구합니다. 여러 개발자들이나 하위 팀들이 응용프로그램의 서로 다른 조각들 상에서 효율적으로 작업할 수 있도록 하면서 응용프로그램에 그 조각들이 통합될 때 서로 끈김없이 연결되도록 하는 응용프로그램을 설계하는 것은 매우 큰 도전입니다.


모놀리식monolithic 스타일로 응용프로그램을 설계하고 빌드하는 것은 유지보수하기 매우 어렵고 불충분한 응용프로그램으로 만들 수 있습니다. 이 경우, "모놀리식" 은 컴포넌트들이 매우 타이트하게 결합되며, 그것들 사이의 명확한 분리는 존재하지 않는 응용프로그램을 가리킵니다. 보통 이러한 방식으로 설계되고 빌드된 응용프로그램들은 개발자의 삶을 매우 어렵게 만듭니다. 그 시스템에 새로운 기능을 추가하거나 현존하는 기능을 대체하는 것은 어렵습니다. 그리고 시스템의 다른 부분들을 깨 먹지 않고 버그를 해결하기 어렵습니다. 그리고 테스트하거나 배포하기 어렵습니다. 또한 그것은 개발자들과 디자이너들이 효율적으로 협업하기 어렵게 합니다.


복합 접근법Compsite Approach


이러한 시련들을 위한 효율적인 해결방법은 응용프로그램을 여러 개의 끊어진, 느슨하게 결합된, 약간 독립적인 컴포넌트들로 나누는 것입니다. 이 컴포넌트들은 일관성있는 솔루션을 형성하기 위해서 응용프로그램 "쉘" 에 쉽게 통합될 수 있습니다. 이러한 방식으로 설계되고 빌드된 응용프로그램들은 보통 복합 응용프로그램이라고 알려져 있습니다.


복합 응용프로그램들은 많은 이점들을 가지고 있는데, 다음을 포함합니다 :


  • 그것들은 모듈들이 서로 다른 개인이나 하위 팀들에 의해 개별적으로 개발되고, 테스트되고, 배포되는 것을 허용합니다; 또한 새로운 기능들을 사용해 더욱 쉽게 수정되고 확장되는 것을 허용하며, 결국 응용프로그램이 쉽게 확장되고 유지되도록 만들어 줍니다. 심지어는 한 사람이 개발하는 프로젝트라고 할지라도 이 복합 접근법을 사용하면 더욱 검사하기 좋고 유지보수하기 좋은 응용프로그램을 생성하는데 있어서 이점을 누릴 수 있습니다.
  • 그것들은 느슨하게 결합되는 방식으로 상호작용하는 다양한 모듈들로부터 제공되는 UI 컴포넌트들로 구성된 공용 쉘을 제공합니다.이는 UI 에 새로운 기능을 추가하는데 있어 다수의 개발자들로부터 발생하는 언쟁을 줄여 주고, 공통적인 외형을 제공합니다.
  • 그것들은 로깅이나 권한과 같은 응용프로그램의 수평적 능력들과 응용프로그램에 특정한 비즈니스 기능과 같은 수직적 능력들 사이에서의 명확한 관심사 분리와 재사용성을 제공합니다. 또한 응용프로그램 컴포넌트들 사이의 종속성과 상호작용을 더욱 쉽게 관리할 수 있도록 해 줍니다.
  • 그것들은 서로 다른 개인들이나 하위 팀들이 그들의 관심사나 전문성과 관련된 특정 작업이나 기능조각에 집중할 수 있도록 함으로써, 역할 분리를 유지하는데 도움을 줍니다. 특히, 그것은 응용프로그램의 UI 와 비즈니스 로직 사이의 명확한 분리를 제공합니다 - 이는 UI 디자이너가 다채로운 사용자 경험을 생성하는데 집중할 수 있다는 것을 의미합니다.

복합 응용프로그램들은 클라이언트 응용프로그램 시나리오의 영역에 매우 잘 들어 맞습니다. 예를 들어, 그것은 복합 응용프로그램은 이질적인 백-엔드 시스템들 상에서 다채로운 엔드-유저 경험을 생성하는데 있어 이상적입니다.다음 그림은 이러한 유형의 복합 응용프로그램의 예를 보여 줍니다.




이러한 유형의 응용프로그램에서, 사용자는 다중 백-엔드 시스템들, 서비스들, 데이터 저장소들을 포괄하는 기능들 위에서 태스크-지향 포커스를 제공하는 다채롭고 유연한 사용자 경험을 제공받을 수 있습니다. 여기에서 각각의 기능들은 하나 이상의 전용 모듈들로 표현되어 있습니다. 응용프로그램 로직과 UI 사이에서의 명확한 분리는 응용프로그램이 모든 구성 모듈들 사이에서 일관적이고 차별화된 외향을 제공하도록 해 줍니다.


부가적으로 복합 응용프로그램은, 다른 컴포넌트들과 통합되어 있고 개별 팀들에 의해 자주 보수되는 UI 내에, 독립적으로 발전하는 컴포넌트들이 존재할 때 유용합니다. 다음 그림은 이러한 유형의 응용프로그램에 대한 스크린 샷을 보여 줍니다. 하이라이트된 각 영역은 UI 를 구성하는 독립적인 컴포넌트들을 의미합니다.




이 경우에, 복합 응용프로그램은 UI 가 동적으로 구성되는 것을 허용합니다. 이는 유연한 사용자 경험을 전달합니다. 예를 들어, 그것은 런타임에 응용프로그램으로 동적으로 추가될 수 있는 새로운 기능을 허용할 수 있습니다. 이는 엔드-유저 커스터마이제이션과 확장성을 가능하게 합니다.


프리즘으로 해결할 수 없는 시련들


비록 프리즘이 WPF 나 실버라이트 응용프로그램들을 빌드하면서 직면할 수 있는 많은 시련들을 해결하는데 도움을 주기는 하지만, 당신의 응용프로그램 상에서의 시나리오나 요구사항에 의존해서 직면할 수 있는 많은 다른 시련들이 존재합니다. 예를 들어 프리즘은 다음과 같은 주제들을 직접적으로 해결해줄 수 없습니다 :


  • 우연한 연결성occasional connectivity 및 데이터 동기화
  • 서비스 및 메시징 인프라스트럭쳐 설계
  • 인증authentication 및 승인authorization
  • 응용프로그램 성능
  • 응용프로그램 버전 관리
  • 에러 핸들링 및 내고장성fault tolerance


'Programming > Prism4.1' 카테고리의 다른 글

진보된 MVVM 시나리오들  (0) 2014.09.20
MVVM 패턴 구현하기  (0) 2014.09.16
모듈러 응용프로그램 개발  (0) 2014.09.13
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
프리즘 응용프로그램 초기화하기  (0) 2014.08.24
소개  (0) 2014.08.18
프리즘 4.1 설치  (0) 2014.08.14

도움말 번역입니다.




프리즘은 윈도우즈 프리젠테이션 파운데이션( WPF ) 데스크탑 응용프로그램, 실버라이트 리치 인터넷 응용프로그램( RIA ), 윈도우즈 폰 7 응용프로그램 등을 더욱 쉽게 설계하고 풍부하고 유연하고 유지보수가 쉽도록 빌드하는 것을 도와주기 위해서 설계된 가이드를 제공합니다. 관심사 분리seperation of concern, 느슨한 커플링loose coupling과 같은 중요한 구조적 설계 원칙을 포함하는 디자인 패턴을 사용함으로써, 프리즘은 당신이 느슨하게 커플링된 컴포넌트들을 사용하여 응용프로그램을 설계하고 빌드하는 것을 돕습니다. 이 컴포넌트들은 독립적으로 진화할 수 있지만, 쉽고 끊김없이 전체 응용프로그램으로 통합될 수도 있습니다. 이러한 유형의 응용프로그램들은 복합 응용프로그램composite appliccation이라고 알려져 있습니다.


노트

프리즘은 WPF 와 실버라이트를 위한 복합 응용프로그램 가이드라고 공식적으로 알려진 가이드의 코드명입니다. 간결함과 사용자 요구에 의해, 이 가이드는 간단하게 프리즘이라고 불립니다.


프리즘은 보통 다중 스크린, 풍부한 유저 상호작용, 데이터 가시화를 형성하며, 중요한 프리젠테이션과 비즈니스 로직을 포함하는 WPF 혹은 실버라이트 응용프로그램 빌드하고 있는 소프트웨어 개발자드를 위해서 만들어졌습니다. 이 응용프로그램들은 보통 레이어 구조를 사용하는 다중 백엔드 시스템들과 상호작용하며, 물리적으로 다중 티어 사이에서 배치되어 있을 수 있습니다. 그 응용프로그램들은 새로운 요구들과 비즈니스 기회들에 응답해 그것의 생명주기가 끝나면 크게 진화되기를 기대받습니다. 짧게 말하자면, 이 응용프로그램들은 "지속적이며" "변화를 반영합니다". 이러한 특징들을 만족시키지 않는 응용프로그램들은 프리즘을 사용해서 이득을 보기 힘들 것입니다.


프리즘은 참조 구현들, 퀵스타트, 재사용 가능한 라이브러리 코드( 프리즘 라이브러리 ), 방대한 문서를 포함합니다. 이 버전의 프리즘은 마이크로 소프트 닷넷 4.0 과 실버라이트 4 를 대상으로 하며, 모델-뷰-뷰모델( MVVM ) 패턴, 네이게이션, 관리되는 확장 가능성 프레임워크Managed Extensibility Framework( MEF ) 등과 관련된 새로운 가이드를 포함합니다. 프리즘은 ( WPF 를 포함하는 ) 닷넷 프레임워크 4.0 과 실버라이트 상에서 빌드되기 때문에, 이 기술들사이의 유사성은 프리즘을 평가하고 받아들이는데 도움이 됩니다.


명심할 것은, 프리즘이 배우기 어렵지는 않지만, 개발자들은 그들에게는 생소할 만한 패턴 및 경험들을 받아들일 준비와 의지를 가져야만 합니다. 관리에 대한 이해와 책임감이 중요하며, 프로젝트 마감은 이 패턴들과 경험들을 배우는데 필요한 시간 투자를 감안해야만 합니다.

'Programming > Prism4.1' 카테고리의 다른 글

진보된 MVVM 시나리오들  (0) 2014.09.20
MVVM 패턴 구현하기  (0) 2014.09.16
모듈러 응용프로그램 개발  (0) 2014.09.13
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
프리즘 응용프로그램 초기화하기  (0) 2014.08.24
왜 프리즘을 사용하는가?  (0) 2014.08.19
프리즘 4.1 설치  (0) 2014.08.14

요구 사항



프리즘 4.1 을 사용하기 위해서는 다음의 요구사항을 만족해야 한다.



실버라이트 관련 도구를 받을 때 주의할 사항은 자신의 비쥬얼 스튜디오의 언어팩에 맞는 언어를 선택해서 받아야 한다는 것이다.


설치하기


Prism4.1_Source.EXE 파일을 받으면 그냥 관리자 권한으로 실행하면 된다. 그러면 압축을 풀 폴더를 지정하라고 하는데, 반드시 폴더를 생성한 후에 지정하기 바란다. 아니면 선택한 폴더에 그냥 풀려 버린다. 필자는 C 에다가 그냥 풀었다가 그냥 C 에 다 풀리는 바람에 그것을 원하는 폴더로 다시 모으느라고 삽질을 해야 했다.


등록하기


프리즘을 비쥬얼 스튜디오에 등록할 수도 있고 등록하지 않을 수도 있다. 등록하게 되면 프로젝트에서 프리즘 라이브러리를 참조하기 편해진다. 등록하면 비쥬얼 스튜디오에서 "참조 추가" 를 누르면 나오는 다이얼로그에서 라이브러리들을 쉽게 찾을 수 있다.



프리즘 라이브러리를 등록하려면, 프리즘이 설치된 폴더의 "RegisterPrismBinaries.bat" 를 더블 클릭해 주는 것만으로 끝이다.


만약 프리즘 라이브러리가 여러 번 등록되면 마지막에 등록된 것을 사용하게 된다.



참고



설치에 대한 더 자세한 내용을 알고 싶다면 프리즘 라이브러리 설치 후에, 해당 폴더의 "Prism4.chm" 을 실행해 "Prism 4.0 Readme" 토픽을 읽어 보기 바란다.

'Programming > Prism4.1' 카테고리의 다른 글

진보된 MVVM 시나리오들  (0) 2014.09.20
MVVM 패턴 구현하기  (0) 2014.09.16
모듈러 응용프로그램 개발  (0) 2014.09.13
컴포넌트 간의 종속성 관리하기  (0) 2014.09.10
프리즘 응용프로그램 초기화하기  (0) 2014.08.24
왜 프리즘을 사용하는가?  (0) 2014.08.19
소개  (0) 2014.08.18

+ Recent posts