WPF 애플리케이션 컴파일

WPF(Windows Presentation Foundation) 애플리케이션을 .NET Framework 실행 파일(.exe), 라이브러리(.dll) 또는 두 어셈블리 형식의 조합으로 빌드할 수 있습니다. 이 항목에서는 WPF 애플리케이션을 빌드하는 방법과 빌드 프로세스의 주요 단계에 대해 설명합니다.

WPF 애플리케이션 빌드

다음과 같은 방법으로 WPF 애플리케이션을 컴파일할 수 있습니다.

WPF 빌드 파이프라인

WPF 프로젝트를 빌드할 때 언어별 및 WPF별 대상 조합이 호출됩니다. 이러한 대상을 실행 중인 프로세스를 빌드 파이프라인이라고 하며 주요 단계는 다음 그림에서와 같이 설명됩니다.

WPF 빌드 프로세스

빌드 전 초기화

빌드하기 전에 MSBuild는 다음과 같은 중요 도구 및 라이브러리의 위치를 확인합니다.

  • .NET Framework

  • Windows SDK 디렉터리

  • WPF 참조 어셈블리의 위치

  • 어셈블리 검색 경로의 속성

MSBuild가 어셈블리를 검색하는 첫 번째 위치는 참조 어셈블리 디렉터리(%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0\)입니다. 이 단계에서 빌드 프로세스는 다양한 속성 및 항목 그룹을 초기화하고 필요한 정리 작업을 수행합니다.

참조 확인

빌드 프로세스는 애플리케이션 프로젝트를 빌드하는 데 필요한 어셈블리를 찾아서 바인딩합니다. 이 논리는 ResolveAssemblyReference 작업에 포함되어 있습니다. 프로젝트 파일에서 Reference로 선언된 모든 어셈블리는 시스템에 이미 설치된 어셈블리의 메타데이터 및 검색 경로에 대한 정보와 함께 작업에 제공됩니다. 이 작업은 어셈블리를 찾고 설치된 어셈블리의 메타데이터를 사용하여 출력 매니페스트에 표시할 필요 없는 이러한 코어 WPF 어셈블리를 필터링합니다. 이 작업의 목적은 ClickOnce 매니페스트에서 중복되는 정보를 방지하는 것입니다. 예를 들어 PresentationFramework.dll은 WPF용으로 빌드된 애플리케이션의 대표로 간주될 수 있고, 모든 WPF 어셈블리가 .NET Framework가 설치된 모든 시스템의 동일한 위치에 존재하기 때문에 매니페스트의 모든 .NET Framework 참조 어셈블리에 모든 정보를 포함할 필요가 없습니다.

태그 컴파일 - 패스 1

이 단계에서는 런타임 시 XML을 구문 분석하고 속성 값을 검증하는 데 시간을 소비하지 않도록 XAML 파일을 구문 분석 및 컴파일합니다. 컴파일된 XAML 파일은 사전 토큰화되어 있으므로 런타임 시 이 파일을 로드하면 XAML 파일을 로드하는 것보다 훨씬 속도가 빨라집니다.

이 단계에서 다음 작업은 Page 빌드 항목인 모든 XAML 파일에 대해 수행됩니다.

  1. XAML 파일은 태그 컴파일러에 의해 구문 분석됩니다.

  2. 컴파일된 표현은 해당 XAML에 대해 만들어져서 obj\Release 폴더에 복사됩니다.

  3. 새 partial 클래스의 CodeDOM 표현이 만들어져서 obj\Release 폴더에 복사됩니다.

또한 모든 XAML 파일에 대해 언어별 코드 파일이 생성됩니다. 예를 들어 Visual Basic 프로젝트의 Page1.xaml 페이지에 대해 Page1.g.vb가 생성되고, C# 프로젝트의 Page1.xaml 페이지에 대해서는 Page1.g.cs가 생성됩니다. 파일 이름에 ".g"가 있으면 파일이 태그 파일(예: Page 또는 Window)의 최상위 수준 요소에 대한 partial 클래스 선언을 포함하여 생성된 코드라는 의미입니다. 이 클래스는 C#의 partial 한정자(Visual Basic에서는 Extends)를 통해 선언되며, 이 한정자는 다른 위치(일반적으로 Page1.xaml.cs의 코드 숨김 파일)에 클래스에 대한 다른 선언이 있음을 나타냅니다.

partial 클래스는 적절한 기본 클래스(예: 페이지의 Page)에서 확장되어 System.Windows.Markup.IComponentConnector 인터페이스를 구현합니다. IComponentConnector 인터페이스에는 구성 요소를 초기화하고 해당 콘텐츠 내의 요소에서 이벤트와 이름을 연결하는 메서드가 있습니다. 결과적으로 생성된 코드 파일에는 다음과 같은 메서드 구현이 있습니다.

public void InitializeComponent() {
    if (_contentLoaded) {
        return;
    }
    _contentLoaded = true;
    System.Uri resourceLocater =
        new System.Uri(
            "window1.xaml",
            System.UriKind.RelativeOrAbsolute);
    System.Windows.Application.LoadComponent(this, resourceLocater);
}
Public Sub InitializeComponent() _

    If _contentLoaded Then
        Return
    End If

    _contentLoaded = True
    Dim resourceLocater As System.Uri = _
        New System.Uri("mainwindow.xaml", System.UriKind.Relative)

    System.Windows.Application.LoadComponent(Me, resourceLocater)

End Sub

기본적으로 태그 컴파일은 MSBuild 엔진과 동일한 AppDomain에서 실행됩니다. 따라서 성능이 크게 향상됩니다. 이 동작은 AlwaysCompileMarkupFilesInSeparateDomain 속성을 통해 전환할 수 있습니다. 이 경우 개별 AppDomain을 언로드하여 모든 참조 어셈블리를 언로드할 수 있는 장점이 있습니다.

태그 컴파일 - 패스 2

일부 XAML 페이지는 태그 컴파일의 패스 1에서 컴파일됩니다. 이때 로컬로 정의된 형식 참조(동일한 프로젝트의 코드에서 정의된 형식에 대한 참조)가 있는 XAML 파일은 컴파일에서 제외됩니다. 이는 로컬로 정의된 형식이 소스에만 있어 아직 컴파일되지 않았기 때문입니다. 이를 확인하기 위해 파서는 추론을 사용하여 태그 파일에서 x:Name과 같은 항목을 찾습니다. 이러한 인스턴스를 찾은 경우 코드 파일이 컴파일될 때까지 해당 태그 파일의 컴파일이 연기되며 나중에 두 번째 태그 컴파일 패스가 이러한 파일을 처리합니다.

파일 분류

빌드 프로세스는 출력 파일을 배치할 애플리케이션 어셈블리를 기반으로 다른 리소스 그룹에 출력 파일을 배치합니다. 일반적으로 지역화되지 않은 애플리케이션에서 Resource로 표시된 모든 데이터 파일은 주 어셈블리(실행 파일 또는 라이브러리)에 배치됩니다. 프로젝트에서 UICulture가 설정된 경우 컴파일된 모든 XAML 파일 및 해당 리소스(특히 언어별로 표시된 리소스)가 위성 리소스 어셈블리에 배치됩니다. 또한 모든 언어 중립 리소스는 주 어셈블리에 배치됩니다. 빌드 프로세스의 이 단계에서 해당 사항이 결정됩니다.

프로젝트 파일의 ApplicationDefinition, PageResource 빌드 작업은 Localizable 메타데이터(허용 가능한 값은 truefalse)를 통해 보강할 수 있으며 이는 파일이 언어와 관련되었는지 또는 언어 중립적인지 지정합니다.

핵심 컴파일

핵심 컴파일 단계에서는 코드 파일을 컴파일합니다. 이 작업은 언어별 대상 파일 Microsoft.CSharp.targets 및 Microsoft.VisualBasic.targets의 논리에 의해 오케스트레이션됩니다. 추론을 통해 태그 컴파일러의 단일 패스로 충분하다고 판단된 경우 주 어셈블리가 생성됩니다. 그러나 프로젝트에서 하나 이상의 XAML 파일에 로컬로 정의된 형식에 대한 참조가 있는 경우 태그 컴파일의 두 번째 패스가 완료된 후 최종 애플리케이션 어셈블리가 만들어질 수 있도록 임시 .dll 파일이 생성됩니다.

매니페스트 생성

빌드 프로세스의 마지막 단계에서는 모든 애플리케이션 어셈블리와 콘텐츠 파일이 준비되면 애플리케이션에 대한 ClickOnce 매니페스트가 생성됩니다.

배포 매니페스트 파일은 배포 모델, 즉 현재 버전, 업데이트 동작 및 게시자 ID를 디지털 시그니처와 함께 설명합니다. 이 매니페스트는 배포를 처리하는 관리자가 작성합니다. 파일 확장명은 XAML 브라우저 애플리케이션(XBAP)의 경우 .xbap이고, 설치된 애플리케이션의 경우 .application입니다. .xbap는 HostInBrowser 프로젝트 속성에 의해 지정되며 그 결과 매니페스트는 애플리케이션이 브라우저에 호스트되는 것으로 식별합니다.

애플리케이션 매니페스트(.exe.manifest 파일)는 애플리케이션 어셈블리 및 종속 라이브러리를 설명하고 애플리케이션에 필요한 사용 권한을 나열합니다. 이 파일은 애플리케이션 개발자가 작성합니다. ClickOnce 애플리케이션을 시작하려면 사용자가 애플리케이션의 배포 매니페스트 파일을 엽니다.

이러한 매니페스트 파일은 XBAP에 대해 항상 만들어집니다. 설치된 애플리케이션의 경우 프로젝트 파일에서 GenerateManifests 속성 값이 true로 지정되지 않는 한 만들어지지 않습니다.

XBAP는 일반적인 인터넷 영역 애플리케이션에 할당된 권한 외에 WebBrowserPermissionMediaPermission이라는 2개의 추가적 권한을 얻습니다. WPF 빌드 시스템은 애플리케이션 매니페스트에서 이러한 권한을 선언합니다.

증분 빌드 지원

WPF 빌드 시스템은 증분 빌드에 대한 지원을 제공합니다. 태그 또는 코드에 대한 변경 사항을 지능적으로 검색하고 변경 사항의 영향을 받는 아티팩트만 컴파일합니다. 증분 빌드 메커니즘은 다음 파일을 사용합니다.

  • 현재 컴파일러 상태를 유지 관리하는 $(AssemblyName)_MarkupCompiler.Cache 파일

  • 로컬로 정의된 형식에 대한 참조가 있는 XAML 파일을 캐시하는 $(AssemblyName) _MarkupCompiler.lref 파일

다음은 증분 빌드를 제어하는 규칙 집합입니다.

  • 파일은 빌드 시스템이 변경을 검색하는 가장 작은 단위입니다. 따라서 코드 파일의 경우 빌드 시스템에서 형식이 변경되었는지 또는 코드가 추가되었는지 확인할 수 없습니다. 이는 프로젝트 파일도 마찬가지입니다.

  • 증분 빌드 메커니즘은 XAML 페이지가 클래스를 정의하거나 다른 클래스를 사용한다는 것을 인식해야 합니다.

  • Reference 항목이 변경된 경우 모든 페이지를 다시 컴파일합니다.

  • 코드 파일이 변경되면 로컬로 정의된 형식 참조가 있는 모든 페이지를 다시 컴파일합니다.

  • XAML 파일이 변경되는 경우:

    • 프로젝트에서 XAML이 Page로 선언된 경우: XAML에 로컬로 정의된 형식 참조가 없는 경우 XAML 및 모든 XAML 페이지를 로컬 참조와 함께 다시 컴파일합니다. XAML에 로컬 참조가 있는 경우 모든 XAML 페이지를 로컬 참조와 함께 다시 컴파일합니다.

    • 프로젝트에서 XAML이 ApplicationDefinition으로 선언된 경우: 모든 XAML 페이지를 다시 컴파일합니다. 이는 각 XAML에 변경 가능성이 있는 Application 형식에 대한 참조가 있기 때문입니다.

  • 프로젝트 파일에서 XAML 파일 대신 애플리케이션 정의로 코드 파일을 선언한 경우:

    • 프로젝트 파일의 ApplicationClassName 값이 변경되었는지(새로운 애플리케이션 형식이 있는지) 확인합니다. 변경된 경우 전체 애플리케이션을 다시 컴파일합니다.

    • 변경되지 않은 경우 모든 XAML 페이지를 로컬 참조와 함께 다시 컴파일합니다.

  • 프로젝트 파일이 변경된 경우: 앞에서 설명한 규칙을 모두 적용하고 다시 컴파일해야 할 항목을 확인합니다. AssemblyName, IntermediateOutputPath, RootNamespaceHostInBrowser 속성이 변경되면 전체 다시 컴파일됩니다.

다시 컴파일 작업은 다음과 같은 시나리오로 수행될 수 있습니다.

  • 전체 애플리케이션이 다시 컴파일됩니다.

  • 로컬로 정의된 형식 참조가 있는 XAML 파일만 다시 컴파일됩니다.

  • 모든 항목이 다시 컴파일되지 않습니다(프로젝트의 모든 항목이 변경되지 않음).

참고 항목