다음을 통해 공유


.NET Framework 사용하여 배포 간소화 및 DLL 지옥 해결

 

스티븐 프라치너
Microsoft Corporation

업데이트 날짜: 2001년 11월

요약: 이 문서에서는 어셈블리의 개념을 소개하고 .NET Framework 어셈블리를 사용하여 버전 관리 및 배포 문제를 해결하는 방법을 설명합니다. (16페이지 인쇄)

콘텐츠

소개
문제 설명
솔루션의 특징
어셈블리: 구성 요소
버전 관리 및 공유
버전 정책
배포
요약

소개

Microsoft® .NET Framework 애플리케이션 배포를 간소화하고 DLL Hell을 해결하기 위한 몇 가지 새로운 기능을 소개합니다. 최종 사용자와 개발자는 모두 오늘날의 구성 요소 기반 시스템에서 발생할 수 있는 버전 관리 및 배포 문제에 대해 잘 알고 있습니다. 예를 들어 거의 모든 최종 사용자가 컴퓨터에 새 애플리케이션을 설치했습니다. 기존 애플리케이션이 작동을 신비하게 중지한다는 것을 알 수 있습니다. 대부분의 개발자는 COM 클래스를 활성화하기 위해 필요한 모든 레지스트리 항목을 일관되게 유지하기 위해 Regedit와 함께 시간을 보냈습니다.

DLL 지옥을 해결하기 위해 .NET Framework 사용되는 디자인 지침 및 구현 기술은 DLL 지옥의 끝에 있는 Rick Anderson과 애플리케이션에서 병렬 구성 요소 공유 구현(확장됨)의 David D'Souza, BJ Whalen 및 Peter Wilson에 의해 설명된 대로 Microsoft Windows® 2000에서 수행된 작업을 기반으로 합니다. 이 .NET Framework .NET 플랫폼에서 관리 코드로 빌드된 애플리케이션에 대한 애플리케이션 격리 및 병렬 구성 요소를 포함한 기능을 제공하여 이 이전 작업을 확장합니다. 또한 Windows XP는 COM 클래스 및 Win32 DLL을 포함하여 관리되지 않는 코드에 대해 동일한 격리 및 버전 관리 기능을 제공합니다(자세한 내용은 격리된 애플리케이션 빌드 및 서비스 방법 및 Windows XP용 Side-by-Side 어셈블리 참조).

이 문서에서는 어셈블리 의 개념을 소개하고 .NET에서 어셈블리를 사용하여 버전 관리 및 배포 문제를 해결하는 방법을 설명합니다. 특히 어셈블리의 구조화 방법, 어셈블리의 이름을 지정하는 방법, 컴파일러와 CLR(공용 언어 런타임)이 어셈블리를 사용하여 애플리케이션 조각 간에 버전 종속성을 기록하고 적용하는 방법에 대해 설명합니다. 또한 애플리케이션 및 관리자가 버전 정책이라고 하는 것을 통해 버전 관리 동작을 사용자 지정하는 방법에 대해서도 설명합니다.

어셈블리가 도입되고 설명되면 여러 배포 시나리오가 표시되어 .NET Framework 사용할 수 있는 다양한 패키징 및 배포 옵션의 샘플링을 제공합니다.

문제 설명

버전 관리

고객 관점에서 가장 일반적인 버전 관리 문제는 DLL Hell이라고 부르는 것입니다. 간단히 말해서 DLL Hell은 여러 애플리케이션이 DLL(동적 연결 라이브러리) 또는 COM(구성 요소 개체 모델) 클래스와 같은 공통 구성 요소를 공유하려고 할 때 발생하는 문제 집합을 나타냅니다. 가장 일반적인 경우 한 애플리케이션은 컴퓨터에 이미 있는 버전과 호환되지 않는 새 버전의 공유 구성 요소를 설치합니다. 방금 설치한 애플리케이션은 정상적으로 작동하지만 이전 버전의 공유 구성 요소에 의존하는 기존 애플리케이션은 더 이상 작동하지 않을 수 있습니다. 경우에 따라 문제의 원인은 훨씬 더 미묘합니다. 예를 들어 사용자가 일부 웹 사이트를 방문하는 부작용으로 Microsoft ActiveX® 컨트롤을 다운로드하는 시나리오를 고려해 보세요. 컨트롤이 다운로드되면 컴퓨터에 있던 컨트롤의 기존 버전이 바뀝니다. 컴퓨터에 설치된 애플리케이션이 이 컨트롤을 사용하는 경우 이 컨트롤도 작동을 중지할 수 있습니다.

대부분의 경우 사용자가 애플리케이션 작동이 중지되었음을 발견하기 전에 상당한 지연이 발생합니다. 따라서 앱에 영향을 줄 수 있는 컴퓨터가 변경된 시기를 기억하기 어려운 경우가 많습니다. 사용자는 일주일 전에 무언가를 설치한 것을 기억할 수 있지만 해당 설치와 현재 표시되는 동작 간에는 명백한 상관 관계가 없습니다. 설상가상으로, 현재 사용자(또는 그들을 돕는 지원 담당자)가 무엇이 잘못되었는지 확인하는 데 도움이 되는 진단 도구는 거의 없습니다.

이러한 문제의 이유는 애플리케이션의 다양한 구성 요소에 대한 버전 정보가 시스템에 의해 기록되거나 적용되지 않기 때문입니다. 또한 한 애플리케이션을 대신하여 시스템을 변경하면 일반적으로 컴퓨터의 모든 애플리케이션에 영향을 줍니다. 현재 변경 내용과 완전히 격리된 애플리케이션을 빌드하는 것은 쉽지 않습니다.

격리된 애플리케이션을 빌드하기 어려운 이유 중 하나는 현재 런타임 환경에서 일반적으로 단일 버전의 구성 요소 또는 애플리케이션만 설치할 수 있기 때문입니다. 이 제한은 구성 요소 작성자가 이전 버전과 호환되는 방식으로 코드를 작성해야 하며, 그렇지 않으면 새 구성 요소를 설치할 때 기존 애플리케이션을 손상시킬 위험이 있음을 의미합니다. 실제로, 영원히 이전 버전과 호환되는 코드를 작성하는 것은 불가능하지는 않더라도 매우 어렵습니다. .NET에서 나란히 라는 개념은 버전 관리 스토리의 핵심입니다. 나란히 컴퓨터에 동일한 구성 요소의 여러 버전을 동시에 설치하고 실행하는 기능이 있습니다. 서로 다른 애플리케이션이 서로 다른 버전의 공유 구성 요소를 자유롭게 사용할 수 있기 때문에 작성자가 반드시 엄격한 이전 버전과의 호환성을 유지하는 것과 관련이 있는 것은 아닙니다.

배포 및 설치

현재 애플리케이션을 설치하는 것은 다단계 프로세스입니다. 일반적으로 애플리케이션을 설치하려면 여러 소프트웨어 구성 요소를 디스크에 복사하고 해당 구성 요소를 설명하는 일련의 레지스트리 항목을 시스템에 만드는 작업이 포함됩니다.

레지스트리의 항목과 디스크의 파일을 분리하면 애플리케이션을 복제하고 제거하기가 매우 어렵습니다. 또한 레지스트리에서 COM 클래스를 완전히 설명하는 데 필요한 다양한 항목 간의 관계는 매우 느슨합니다. 이러한 항목에는 코드 클래스, 인터페이스, typelibs 및 DCOM 앱 ID에 대한 항목이 포함되어 문서 확장 또는 구성 요소 범주를 등록하기 위해 만든 항목을 멘션 않는 경우가 많습니다. 종종 이러한 항목을 수동으로 동기화 상태로 유지합니다.

마지막으로 COM 클래스를 활성화하려면 이 레지스트리 공간이 필요합니다. 따라서 적절한 레지스트리 항목을 만들기 위해 각 클라이언트 머신을 터치해야 하므로 분산 애플리케이션을 배포하는 프로세스가 크게 복잡해질 수 있습니다.

이러한 문제는 주로 구성 요소 자체와 별도로 유지되는 구성 요소에 대한 설명으로 인해 발생합니다. 즉, 애플리케이션은 자체 설명이나 자체 포함이 아닙니다.

솔루션의 특징

.NET Framework 방금 설명한 문제를 해결하려면 다음과 같은 기본 기능을 제공해야 합니다.

  • 애플리케이션은 자체 설명이어야 합니다. 자체 설명하는 애플리케이션은 레지스트리에 대한 종속성을 제거하여 영향을 주지 않는 설치를 사용하도록 설정하고 제거 및 복제를 간소화합니다.
  • 버전 정보를 기록하고 적용해야 합니다. 런타임에 적절한 버전의 종속성이 로드되도록 버전 관리 지원을 플랫폼에 빌드해야 합니다.
  • "마지막으로 알려진 선"을 기억해야 합니다 . 애플리케이션이 성공적으로 실행되면 플랫폼은 함께 작동하는 해당 버전을 포함한 구성 요소 집합을 기억해야 합니다. 또한 관리자가 애플리케이션을 "마지막으로 알려진 양수" 상태로 쉽게 되돌리기 수 있는 도구를 제공해야 합니다.
  • 병렬 구성 요소에 대한 지원. 여러 버전의 구성 요소를 컴퓨터에서 동시에 설치하고 실행할 수 있도록 허용하면 호출자는 무의식적으로 "강제" 버전 대신 로드할 버전을 지정할 수 있습니다. .NET Framework 여러 버전의 프레임워크 자체가 단일 컴퓨터에서 공존할 수 있도록 하여 한 단계 더 멀리 나란히 진행됩니다. 관리자가 필요한 경우 다른 버전의 .NET Framework 다른 애플리케이션을 실행하도록 선택할 수 있으므로 업그레이드 스토리가 크게 간소화됩니다.
  • 애플리케이션 격리. .NET Framework 다른 애플리케이션을 대신하여 컴퓨터의 변경 내용에 영향을 받을 수 없는 애플리케이션을 작성하기 위해 쉽고 실제로 기본값이어야 합니다.

어셈블리: 구성 요소

어셈블리는 방금 설명한 버전 관리 및 배포 문제를 해결하기 위해 .NET Framework 사용하는 구성 요소입니다. 어셈블리는 형식 및 리소스에 대한 배포 단위입니다. 어셈블리는 여러 면에서 오늘날의 세계에서 DLL과 동일합니다. 기본적으로 어셈블리는 "논리적 DLL"입니다.

어셈블리는 매니페스트라는 메타데이터를 통해 자체 설명합니다. .NET은 메타데이터를 사용하여 형식을 설명하는 것처럼 메타데이터를 사용하여 형식을 포함하는 어셈블리를 설명합니다.

어셈블리는 배포 그 이상입니다. 예를 들어 .NET의 버전 관리가 어셈블리 수준에서 수행됩니다. 모듈 또는 형식과 같이 더 작은 것은 버전이 지정되지 않습니다. 또한 어셈블리는 애플리케이션 간에 코드를 공유하는 데 사용됩니다. 형식이 포함된 어셈블리는 형식의 ID에 속합니다.

코드 액세스 보안 시스템은 권한 모델의 핵심에 어셈블리를 사용합니다. 매니페스트의 어셈블리 작성자가 코드를 실행하는 데 필요한 사용 권한 집합을 기록하고 관리자는 코드가 포함된 어셈블리를 기반으로 코드에 권한을 부여합니다.

마지막으로 어셈블리는 형식에 대한 표시 경계를 설정하고 형식에 대한 참조를 확인하기 위한 런타임 scope 역할을 한다는 측면에서 형식 시스템 및 런타임 시스템의 핵심이기도 합니다.

어셈블리 매니페스트

특히 매니페스트에는 어셈블리에 대한 다음 데이터가 포함됩니다.

  • ID. 어셈블리의 ID는 간단한 텍스트 이름, 버전 번호, 선택적 문화권 및 어셈블리가 공유를 위해 빌드된 경우 선택적 공개 키의 네 부분으로 구성됩니다(아래 공유 어셈블리의 섹션 참조).
  • 파일 목록입니다. 매니페스트에는 어셈블리를 구성하는 모든 파일 목록이 포함됩니다. 각 파일에 대해 매니페스트는 매니페스트를 빌드할 때 해당 콘텐츠의 이름과 암호화 해시를 기록합니다. 이 해시는 런타임에 확인되어 배포 단위가 일관된지 확인합니다.
  • 참조 된 어셈블리입니다. 어셈블리 간의 종속성은 호출 어셈블리의 매니페스트에 저장됩니다. 종속성 정보에는 런타임에 올바른 버전의 종속성이 로드되도록 하는 데 사용되는 버전 번호가 포함됩니다.
  • 내보낸 형식 및 리소스. 형식 및 리소스에 사용할 수 있는 표시 유형 옵션에는 "내 어셈블리 내에서만 표시" 및 "내 어셈블리 외부의 호출자에게 표시됨"이 포함됩니다.
  • 권한 요청. 어셈블리에 대한 권한 요청은 세 집합으로 그룹화됩니다. 1) 어셈블리를 실행하는 데 필요한 집합, 2) 원하는 집합이지만 어셈블리에 부여되지 않은 경우에도 어셈블리에는 여전히 일부 기능이 있으며 3) 작성자가 어셈블리를 부여하지 않으려는 기능입니다.

IL 디스어셈블러(Ildasm) SDK 도구는 어셈블리에서 코드 및 메타데이터를 보는 데 유용합니다. 그림 1은 Ildasm에서 표시하는 예제 매니페스트입니다. .assembly 지시문은 어셈블리를 식별하고 .assembly extern 지시문에는 이 어셈블리가 의존하는 다른 어셈블리에 대한 정보가 포함됩니다.

그림 1. IL 디스어셈블러에서 표시하는 예제 매니페스트

어셈블리 구조체

지금까지 어셈블리는 주로 논리적 개념으로 설명되었습니다. 이 섹션에서는 어셈블리가 물리적으로 표현되는 방법을 설명하여 어셈블리를 보다 구체적으로 만드는 데 도움이 됩니다.

일반적으로 어셈블리는 네 가지 요소, 즉 어셈블리 메타데이터(매니페스트), 형식을 설명하는 메타데이터, 형식을 구현하는 IL(중간 언어) 코드 및 리소스 집합으로 구성됩니다. 이러한 모든 항목이 각 어셈블리에 있는 것은 아닙니다. 매니페스트만 엄격하게 필요하지만 어셈블리에 의미 있는 기능을 제공하려면 형식이나 리소스가 필요합니다.

이러한 네 가지 요소를 "패키지"하는 방법에 대한 몇 가지 옵션이 있습니다. 예를 들어 그림 2는 매니페스트, 형식 메타데이터, IL 코드 및 리소스와 같은 전체 어셈블리를 포함하는 단일 DLL을 보여 줍니다.

그림 2. 모든 어셈블리 요소를 포함하는 DLL

또는 어셈블리의 내용이 여러 파일에 분산될 수 있습니다. 그림 3에서 작성자가 일부 유틸리티 코드를 다른 DLL로 분리하고 큰 리소스 파일(이 경우 JPEG)을 원래 파일에 유지하도록 선택했습니다. 이 작업을 수행하는 한 가지 이유는 코드 다운로드를 최적화하기 위한 것입니다. .NET Framework 파일을 참조할 때만 다운로드하므로 어셈블리에 자주 액세스하지 않는 코드 또는 리소스가 포함된 경우 파일을 개별 파일로 분리하면 다운로드 효율성이 높아집니다. 여러 파일이 사용되는 또 다른 일반적인 시나리오는 둘 이상의 언어에서 코드로 구성된 어셈블리를 빌드하는 것입니다. 이 경우 각 파일(모듈)을 별도로 빌드한 다음, .NET Framework SDK(al.exe)에 제공된 어셈블리 링커 도구를 사용하여 어셈블리로 그룹화합니다.

그림 3. 여러 파일에 분산된 어셈블리 요소

버전 관리 및 공유

DLL Hell의 주요 원인 중 하나는 현재 구성 요소 기반 시스템에서 사용되는 공유 모델입니다. 기본적으로 개별 소프트웨어 구성 요소는 컴퓨터의 여러 애플리케이션에서 공유됩니다. 예를 들어 설치 프로그램이 시스템 디렉터리에 DLL을 복사하거나 COM 레지스트리에 클래스를 등록할 때마다 해당 코드는 컴퓨터에서 실행되는 다른 애플리케이션에 영향을 줄 수 있습니다. 특히 기존 애플리케이션이 이전 버전의 공유 구성 요소를 사용한 경우 해당 애플리케이션은 자동으로 새 버전을 사용하기 시작합니다. 공유 구성 요소가 엄격하게 이전 버전과 호환되는 경우 괜찮을 수 있지만 대부분의 경우 이전 버전과의 호환성을 유지하는 것은 불가능하지 않은 경우 어렵습니다. 이전 버전과의 호환성이 유지되지 않거나 유지 관리할 수 없는 경우 다른 애플리케이션이 설치되는 부작용으로 인해 애플리케이션이 손상되는 경우가 많습니다.

.NET의 원칙 디자인 지침은 격리된 구성 요소(또는 어셈블리)의 지침입니다. 어셈블리를 격리하면 어셈블리는 한 애플리케이션에서만 액세스할 수 있습니다. 즉, 어셈블리는 컴퓨터의 여러 애플리케이션에서 공유되지 않으며 다른 애플리케이션의 시스템 변경 내용의 영향을 받을 수 없습니다. 격리는 개발자가 애플리케이션에서 사용하는 코드를 절대로 제어할 수 있도록 합니다. 격리된 어셈블리 또는 애플리케이션-프라이빗 어셈블리는 .NET 애플리케이션의 기본값입니다. 격리된 구성 요소에 대한 추세는 .local 파일이 도입된 Microsoft Windows 2000에서 시작되었습니다. 이 파일은 요청된 구성 요소를 찾으려고 할 때 OS 로더와 COM이 애플리케이션 디렉터리를 먼저 확인하도록 하는 데 사용되었습니다. (MSDN 라이브러리, 애플리케이션에서 병렬 구성 요소 공유 구현의 관련 문서를 참조하세요.)

그러나 애플리케이션 간에 어셈블리를 공유해야 하는 경우가 있습니다. 모든 애플리케이션이 System.Windowns.Forms, System.Web 또는 일반적인 Web Forms 컨트롤의 자체 복사본을 전달하는 것은 의미가 없습니다.

.NET에서 애플리케이션 간에 코드를 공유하는 것은 명시적인 결정입니다. 공유되는 어셈블리에는 몇 가지 추가 요구 사항이 있습니다. 특히 공유 어셈블리는 동일한 어셈블리의 여러 버전을 동일한 컴퓨터 또는 동일한 프로세스 내에서 동시에 설치하고 실행할 수 있도록 나란히 지원해야 합니다. 또한 공유 어셈블리에는 더 엄격한 명명 요구 사항이 있습니다. 예를 들어 공유되는 어셈블리에는 전역적으로 고유한 이름이 있어야 합니다.

격리와 공유가 모두 필요하기 때문에 두 가지 어셈블리의 "종류"를 생각하게 됩니다. 이것은 둘 사이에 실제 구조적 차이가 없다는 측면에서 다소 느슨한 분류이지만, 오히려 차이점은 그들이 사용되는 방법에 있습니다 : 하나의 애플리케이션에 개인 또는 많은 사이에 공유 여부.

Application-Private 어셈블리

애플리케이션-프라이빗 어셈블리는 하나의 애플리케이션에만 표시되는 어셈블리입니다. .NET에서 가장 일반적인 경우가 될 것으로 예상됩니다. 프라이빗 어셈블리의 명명 요구 사항은 간단합니다. 어셈블리 이름은 애플리케이션 내에서만 고유해야 합니다. 전역적으로 고유한 이름은 필요하지 않습니다. 애플리케이션 개발자가 애플리케이션에 격리된 어셈블리를 완전히 제어할 수 있으므로 이름을 고유하게 유지하는 것은 문제가 되지 않습니다.

애플리케이션-프라이빗 어셈블리는 사용되는 애플리케이션의 디렉터리 구조 내에 배포됩니다. 프라이빗 어셈블리는 애플리케이션 디렉터리 또는 해당 하위 디렉터리에 직접 배치할 수 있습니다. CLR은 검색이라는 프로세스를 통해 이러한 어셈블리 를 찾습니다. 검색은 단순히 어셈블리 이름을 매니페스트가 포함된 파일 이름에 매핑하는 것입니다.

특히 CLR은 어셈블리 참조에 기록된 어셈블리의 이름을 사용하고 ".dll"를 추가하고 애플리케이션 디렉터리에서 해당 파일을 찾습니다. 이 체계에는 런타임이 어셈블리에 의해 명명된 하위 디렉터리 또는 어셈블리의 문화권에 의해 명명된 하위 디렉터리에서 표시되는 몇 가지 변형이 있습니다. 예를 들어 개발자는 "de"라는 하위 디렉터리에서 독일어로 지역화된 리소스가 포함된 어셈블리를 "es"라는 디렉터리에 스페인어로 배포하도록 선택할 수 있습니다. 자세한 내용은 .NET Framework SDK 가이드를 참조하세요.

설명한 대로 각 어셈블리 매니페스트에는 해당 종속성에 대한 버전 정보가 포함됩니다. 개발자가 애플리케이션 디렉터리에 배포된 어셈블리를 완전히 제어할 수 있으므로 이 버전 정보는 프라이빗 어셈블리에 적용되지 않습니다.

공유 어셈블리

.NET Framework 공유 어셈블리의 개념도 지원합니다. 공유 어셈블리는 컴퓨터의 여러 애플리케이션에서 사용하는 어셈블리입니다. .NET을 사용하면 애플리케이션 간에 코드를 공유하는 것은 명시적인 결정입니다. 공유 어셈블리에는 현재 발생하는 공유 문제를 방지하기 위한 몇 가지 추가 요구 사항이 있습니다. 앞에서 설명한 나란히 설명에 대한 지원 외에도 공유 어셈블리에는 훨씬 더 엄격한 명명 요구 사항이 있습니다. 예를 들어 공유 어셈블리에는 전역적으로 고유한 이름이 있어야 합니다. 또한 시스템은 "이름 보호"를 제공해야 합니다. 즉, 다른 사용자가 다른 사용자의 어셈블리 이름을 다시 사용하지 못하도록 합니다. 예를 들어 그리드 컨트롤의 공급업체이고 어셈블리 버전 1을 릴리스했다고 가정해 보겠습니다. 작성자로서 다른 누구도 버전 2 또는 그리드 컨트롤이라고 주장하는 어셈블리를 해제할 수 없다는 확신이 필요합니다. .NET Framework 강력한 이름(다음 섹션에서 자세히 설명)이라는 기술을 통해 이러한 명명 요구 사항을 지원합니다.

일반적으로 애플리케이션 작성자가 애플리케이션에서 사용하는 공유 어셈블리를 동일한 수준의 제어할 수 없습니다. 결과적으로, 버전 정보는 공유 어셈블리에 대한 모든 참조에서 확인됩니다. 또한 .NET Framework 애플리케이션 및 관리자가 버전 정책을 지정하여 애플리케이션에서 사용하는 어셈블리의 버전을 재정의할 수 있습니다.

공유 어셈블리가 반드시 하나의 애플리케이션에 비공개로 배포되는 것은 아니지만, 특히 xcopy 배포가 요구 사항인 경우 이러한 접근 방식은 여전히 실행 가능합니다. 프라이빗 애플리케이션 디렉터리 외에도 어셈블리의 위치를 설명하는 코드베이스가 애플리케이션의 구성 파일에 제공되는 한 공유 어셈블리를 전역 어셈블리 캐시 또는 URL에 배포할 수도 있습니다. 전역 어셈블리 캐시는 둘 이상의 애플리케이션에서 사용되는 어셈블리에 대한 머신 전체 저장소입니다. 설명한 대로 캐시에 배포하는 것은 요구 사항이 아니지만 몇 가지 장점이 있습니다. 예를 들어 여러 버전의 어셈블리에 대한 병렬 스토리지가 자동으로 제공됩니다. 또한 관리자는 저장소를 사용하여 컴퓨터의 모든 애플리케이션에서 사용할 버그 수정 또는 보안 패치를 배포할 수 있습니다. 마지막으로 전역 어셈블리 캐시에 배포하는 것과 관련된 몇 가지 성능 향상이 있습니다. 첫 번째는 아래의 강력한 이름 섹션에 설명된 대로 강력한 이름 서명의 확인을 포함합니다. 두 번째 성능 향상에는 작업 집합이 포함됩니다. 여러 애플리케이션이 동일한 어셈블리를 동시에 사용하는 경우 디스크의 동일한 위치에서 해당 어셈블리를 로드하면 OS에서 제공하는 코드 공유 동작이 활용됩니다. 반대로 여러 다른 위치(애플리케이션 디렉터리)에서 동일한 어셈블리를 로드하면 동일한 코드의 복사본이 많이 로드됩니다. 최종 사용자의 컴퓨터에서 캐시에 어셈블리를 추가하는 작업은 일반적으로 Windows Installer 또는 다른 설치 기술을 기반으로 하는 설치 프로그램을 사용하여 수행됩니다. 어셈블리는 일부 애플리케이션을 실행하거나 웹 페이지로 검색하는 부작용으로 캐시에서 끝나지 않습니다. 대신 캐시에 어셈블리를 설치하려면 사용자 부분에 대한 명시적 작업이 필요합니다. Windows XP 및 Visual Studio .NET과 함께 제공되는 Windows Installer 2.0은 어셈블리, 어셈블리 캐시 및 격리된 애플리케이션의 개념을 완전히 이해하도록 향상되었습니다. 즉, 주문형 설치 및 애플리케이션 복구와 같은 모든 Windows Installer 기능을 .NET 애플리케이션과 함께 사용할 수 있습니다.

개발 및 테스트 머신에서 캐시에 어셈블리를 추가하려는 경우마다 설치 패키지를 빌드하는 것이 실용적이지 않은 경우가 많습니다. 따라서 .NET SDK에는 어셈블리 캐시 작업을 위한 몇 가지 도구가 포함되어 있습니다. 첫 번째는 캐시에 어셈블리를 추가하고 나중에 제거할 수 있는 gacutil이라는 도구입니다. /i 스위치를 사용하여 캐시에 어셈블리를 추가합니다.

gacutil /i:myassembly.dll 
See the .NET Framework SDK documentation for a full description of the 
      options supported by gacutil.

다른 도구는 Windows Explorer 및 .NET Framework 구성 도구를 사용하여 캐시를 조작할 수 있는 Windows 셸 확장입니다. Windows 디렉터리 아래의 "어셈블리" 하위 디렉터리로 이동하여 셸 확장에 액세스할 수 있습니다. .NET Framework 구성 도구는 제어판 관리 도구 섹션에서 찾을 수 있습니다.

그림 4는 셸 확장을 사용하여 전역 어셈블리 캐시의 보기를 보여 주는 그림입니다.

그림 4. 전역 어셈블리 캐시

강력한 이름

강력한 이름은 공유 어셈블리와 관련된 더 엄격한 명명 요구 사항을 사용하도록 설정하는 데 사용됩니다. 강력한 이름에는 다음 세 가지 목표가 있습니다.

  • 이름 고유성. 공유 어셈블리에는 전역적으로 고유한 이름이 있어야 합니다.
  • 이름 스푸핑을 방지합니다. 개발자는 다른 사람이 어셈블리 중 하나의 후속 버전을 릴리스하고 실수로 또는 의도적으로 사용자로부터 왔다고 거짓으로 주장하는 것을 원하지 않습니다.
  • 참조에서 ID를 제공합니다. 어셈블리에 대한 참조를 확인할 때 강력한 이름을 사용하여 로드된 어셈블리가 예상 게시자에서 온 것을 보장합니다.

강력한 이름은 표준 공개 키 암호화를 사용하여 구현됩니다. 일반적으로 프로세스는 다음과 같이 작동합니다. 어셈블리 작성자는 키 쌍을 생성하거나 기존 쌍을 사용하며, 매니페스트가 포함된 파일에 프라이빗 키로 서명하고, 호출자가 공개 키를 사용할 수 있도록 합니다. 어셈블리에 대한 참조가 만들어지면 호출자는 강력한 이름을 생성하는 데 사용되는 프라이빗 키에 해당하는 공개 키를 기록합니다. 그림 5에서는 메타데이터에 키를 저장하는 방법 및 서명이 생성되는 방법을 포함하여 개발 시 이 프로세스의 작동 방식을 간략하게 설명합니다.

시나리오는 "MyLib"라는 어셈블리를 참조하는 "Main"이라는 어셈블리입니다. MyLib에는 공유 이름이 있습니다. 중요한 단계는 다음과 같습니다.

그림 5. 공유 이름 구현 프로세스

  1. 개발자는 키 쌍과 어셈블리에 대한 소스 파일 집합을 전달하는 컴파일러를 호출합니다. 키 쌍은 SN이라는 SDK 도구를 사용하여 생성됩니다. 예를 들어 다음 명령은 새 키 쌍을 생성하고 파일에 저장합니다.

    Sn –k MyKey.snk
    The key pair is passed to the compiler using the custom attribute 
            System.Reflection.AssemblyKeyFileAttribute as follows:
    
       <assembly:AssemblyKeyFileAttribute("TestKey.snk")>
    
  2. 컴파일러가 어셈블리를 내보내면 공개 키가 매니페스트에 어셈블리 ID의 일부로 기록됩니다. 공개 키를 ID의 일부로 포함하면 어셈블리에 전역적으로 고유한 이름이 지정됩니다.

  3. 어셈블리를 내보낸 후 매니페스트가 포함된 파일은 프라이빗 키로 서명됩니다. 결과 서명은 파일에 저장됩니다.

  4. 컴파일러에서 Main을 생성하면 MyLib의 공개 키가 MyLib에 대한 참조의 일부로 Main의 매니페스트에 저장됩니다.

런타임에는 강력한 이름이 개발자에게 필요한 이점을 제공하기 위해 .NET Framework 수행하는 두 단계가 있습니다. 먼저, MyLib의 강력한 이름 서명은 어셈블리가 전역 어셈블리 캐시에 설치된 경우에만 확인됩니다. 애플리케이션에서 파일을 로드할 때 서명이 다시 확인되지 않습니다. 공유 어셈블리가 전역 어셈블리 캐시에 배포되지 않으면 파일이 로드될 때마다 서명이 확인됩니다. 서명을 확인하면 어셈블리가 빌드된 이후 MyLib의 내용이 변경되지 않았는지 확인합니다. 두 번째 단계는 Main의 MyLib 참조의 일부로 저장된 공개 키가 MyLib ID의 일부인 공개 키와 일치하는지 확인하는 것입니다. 이러한 키가 동일한 경우 Main의 작성자는 로드된 MyLib 버전이 Main이 빌드된 MyLib 버전을 작성한 동일한 게시자에서 제공되었는지 확인할 수 있습니다. 이 키 동등성 검사 Main에서 MyLib으로의 참조가 확인될 때 런타임에 수행됩니다.

"서명"이라는 용어는 종종 Microsoft Authenticode®를 떠올립니다. 강력한 이름과 Authenticode는 어떤 식으로든 관련이 없다는 것을 이해하는 것이 중요합니다. 두 가지 기술은 서로 다른 목표를 가지고 있습니다. 특히 Authenticode는 게시자와 연결된 신뢰 수준을 의미하지만 강력한 이름은 그렇지 않습니다. 강력한 이름과 연결된 인증서 또는 타사 서명 기관이 없습니다. 또한 강력한 이름 서명은 빌드 프로세스의 일부로 컴파일러 자체에서 수행하는 경우가 많습니다.

주목할 만한 또 다른 고려 사항은 "서명 지연" 프로세스입니다. 어셈블리 작성자가 전체 서명을 수행하는 데 필요한 프라이빗 키에 액세스할 수 없는 경우가 많습니다. 대부분의 회사는 소수의 사용자만 액세스할 수 있는 잘 보호된 매장에 이러한 키를 유지합니다. 따라서 .NET Framework 개발자가 공개 키만 사용하여 어셈블리를 빌드할 수 있도록 하는 "서명 지연"이라는 기술을 제공합니다. 이 모드에서는 프라이빗 키가 제공되지 않으므로 파일이 실제로 서명되지 않습니다. 대신 나중에 SN 유틸리티를 사용하여 파일에 서명됩니다. 지연 서명을 사용하는 방법에 대한 자세한 내용은 .NET Framework SDK에서 어셈블리 서명 지연을 참조하세요.

버전 정책

설명한 대로 각 어셈블리 매니페스트는 빌드된 각 종속성의 버전에 대한 정보를 기록합니다. 그러나 애플리케이션 작성자 또는 관리자가 런타임에 다른 버전의 종속성으로 실행하려는 몇 가지 시나리오가 있습니다. 예를 들어 관리자는 수정 사항을 선택하기 위해 모든 애플리케이션을 다시 컴파일할 필요 없이 버그 수정 릴리스를 배포할 수 있어야 합니다. 또한 관리자는 보안 구멍이나 기타 심각한 버그가 발견되면 특정 버전의 어셈블리를 사용하지 않도록 지정할 수 있어야 합니다. 이 .NET Framework 버전 정책을 통해 버전 바인딩에서 이러한 유연성을 가능하게 합니다.

어셈블리 버전 번호

각 어셈블리에는 ID의 일부로 네 부분으로 구성된 버전 번호가 있습니다(즉, 일부 어셈블리의 버전 1.0.0.0 및 버전 2.1.0.2는 클래스 로더에 관한 한 완전히 다른 ID임). ID의 일부로 버전을 포함하는 것은 나란히 사용할 목적으로 어셈블리의 다른 버전을 구분하는 데 필수적입니다.

버전 번호의 부분은 주, 부, 빌드 및 수정 버전입니다. 버전 번호의 부분에는 의미 체계가 적용되지 않습니다. 즉, CLR은 버전 번호가 할당되는 방식에 따라 호환성 또는 어셈블리의 다른 특성을 유추하지 않습니다. 개발자는 이 숫자의 일부를 원하는 대로 자유롭게 변경할 수 있습니다. 버전 번호 형식에 적용되는 의미 체계는 없지만 개별 조직에서는 버전 번호가 변경되는 방식에 대한 규칙을 설정하는 것이 유용할 수 있습니다. 이렇게 하면 organization 전체에서 일관성을 유지하고 특정 어셈블리의 빌드를 쉽게 확인할 수 있습니다. 한 가지 일반적인 규칙은 다음과 같습니다.

주 또는 부입니다. 버전 번호의 주 또는 부 부분을 변경하면 호환되지 않는 변경 내용이 표시됩니다. 이 규칙에 따라 버전 2.0.0.0은 버전 1.0.0.0과 호환되지 않는 것으로 간주됩니다. 호환되지 않는 변경의 예는 일부 메서드 매개 변수의 형식을 변경하거나 형식 또는 메서드를 완전히 제거하는 것입니다.

빌드. 빌드 번호는 일반적으로 일일 빌드 또는 더 작은 호환 릴리스를 구분하는 데 사용됩니다.

수정. 수정 번호에 대한 변경 내용은 일반적으로 특정 버그를 수정하는 데 필요한 증분 빌드를 위해 예약됩니다. 특정 버그에 대한 수정 사항이 고객에게 배송될 때 수정 내용이 변경되는 경우가 많다는 측면에서 "긴급 버그 수정" 번호라고 하는 경우가 있습니다.

기본 버전 정책

공유 어셈블리에 대한 참조를 확인할 때 CLR은 코드에서 해당 어셈블리에 대한 참조를 통해 로드할 종속성의 버전을 결정합니다. .NET의 기본 버전 정책은 매우 간단합니다. 참조를 확인할 때 CLR은 호출 어셈블리의 매니페스트에서 버전을 가져와서 정확히 동일한 버전 번호로 종속성 버전을 로드합니다. 이러한 방식으로 호출자는 빌드되고 테스트된 정확한 버전을 가져옵니다. 이 기본 정책에는 다른 애플리케이션이 기존 애플리케이션이 의존하는 새 버전의 공유 어셈블리를 설치하는 시나리오로부터 애플리케이션을 보호하는 속성이 있습니다. .NET 이전에는 기존 애플리케이션이 기본적으로 새 공유 구성 요소를 사용하기 시작했습니다. 그러나 .NET에서 새 버전의 공유 어셈블리 설치는 기존 애플리케이션에 영향을 주지 않습니다.

사용자 지정 버전 정책

애플리케이션이 제공된 정확한 버전에 바인딩하는 것이 원하는 것이 아닌 경우가 있을 수 있습니다. 예를 들어 관리자는 공유 어셈블리에 중요한 버그 수정을 배포하고 빌드된 버전에 관계없이 모든 애플리케이션이 이 새 버전을 사용하도록 할 수 있습니다. 또한 공유 어셈블리의 공급업체는 서비스 릴리스를 기존 어셈블리에 제공했을 수 있으며 모든 애플리케이션이 원래 버전 대신 서비스 릴리스 사용을 시작하도록 할 수 있습니다. 이러한 시나리오 및 기타 시나리오는 버전 정책을 통해 .NET Framework 지원됩니다.

버전 정책은 XML 파일에 명시되어 있으며 한 버전의 어셈블리를 다른 버전 대신 로드하기 위한 요청일 뿐입니다. 예를 들어 다음 버전 정책은 MARINECtrl이라는 어셈블리의 버전 5.0.0.0 대신 버전 5.0.0.1을 로드하도록 CLR에 지시합니다.

 <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly
   <assemblyIdentity name="MarineCtrl" publicKeyToken="9335a2124541cfb9" />
      <bindingRedirect oldVersion="5.0.0.0" newVersion="5.0.0.1" />   
  </dependentAssembly>
</assemblyBinding>

특정 버전 번호에서 다른 버전으로 리디렉션하는 것 외에도 다양한 버전에서 다른 버전으로 리디렉션할 수도 있습니다. 예를 들어 다음 정책은 모든 버전을 MarineCtrl의 0.0.0.0에서 5.0.0.0으로 버전 5.0.0.1로 리디렉션합니다.

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly
   <assemblyIdentity name="MarineCtrl" publicKeyToken="9335a2124541cfb9" />
      <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.1" />   
  </dependentAssembly>
</assemblyBinding>

버전 정책 수준

.NET에서 버전 정책을 적용할 수 있는 세 가지 수준( 애플리케이션별 정책, 게시자 정책 및 머신 전체 정책)이 있습니다.

애플리케이션별 정책. 각 애플리케이션에는 다른 버전의 종속 어셈블리에 바인딩하려는 애플리케이션의 욕망을 지정할 수 있는 선택적 구성 파일이 있습니다. 구성 파일의 이름은 애플리케이션 유형에 따라 달라집니다. 실행 파일의 경우 구성 파일의 이름은 실행 파일의 이름 + ".config" 확장명입니다. 예를 들어 "myapp.exe"에 대한 구성 파일은 "myapp.exe.config"입니다. ASP.NET 애플리케이션에 대한 구성 파일은 항상 "web.config"입니다.

게시자 정책. 애플리케이션별 정책은 애플리케이션 개발자 또는 관리자에 의해 설정되지만 게시자 정책은 공유 어셈블리의 공급업체에 의해 설정됩니다. 게시자 정책은 다양한 버전의 어셈블리에 대한 공급업체의 호환성 설명입니다. 예를 들어 공유 Windows Forms 컨트롤의 공급업체가 컨트롤에 대한 여러 버그 수정이 포함된 서비스 릴리스를 배송한다고 가정해 봅시다. 원래 컨트롤은 버전 2.0.0.0이고 서비스 릴리스 버전은 2.0.0.1입니다. 새 릴리스에는 버그 수정(호환성이 손상되는 API 변경 없음)만 포함되어 있으므로 제어 공급업체는 2.0.0.0을 사용한 기존 애플리케이션이 이제 2.0.0.1 사용을 시작하도록 하는 새 릴리스와 함께 게시자 정책을 실행할 가능성이 높습니다. 게시자 정책은 애플리케이션 및 컴퓨터 전체 정책이 있는 것처럼 XML로 표현되지만 다른 정책 수준과 달리 게시자 정책은 어셈블리 자체로 배포됩니다. 이에 대한 주된 이유는 특정 어셈블리에 대한 정책을 해제하는 organization 어셈블리 자체를 해제한 organization 동일하도록 하기 위한 것입니다. 이 작업은 원래 어셈블리와 정책 어셈블리 모두 동일한 키 쌍을 사용하여 강력한 이름을 지정하도록 요구하여 수행됩니다.

컴퓨터 전체 정책. 최종 정책 수준은 컴퓨터 전체 정책(관리자 정책이라고도 함)입니다. 컴퓨터 전체 정책은 .NET Framework 설치 디렉터리의 "구성" 하위 디렉터리에 있는 machine.config 저장됩니다. 설치 디렉터리가 %windir%\microsoft.net\framework\%runtimeversion%입니다. machine.config 정책 설명은 컴퓨터에서 실행되는 모든 애플리케이션에 영향을 줍니다. 컴퓨터 전체 정책은 관리자가 지정된 컴퓨터의 모든 애플리케이션이 특정 버전의 어셈블리를 사용하도록 강제하는 데 사용됩니다. 가장 많이 사용되는 주석 시나리오는 보안 또는 기타 중요한 버그 수정이 전역 어셈블리 캐시에 배포된 경우입니다. 고정 어셈블리를 배포한 후 관리자는 컴퓨터 전체 버전 정책을 사용하여 애플리케이션이 이전의 손상된 어셈블리 버전을 사용하지 않도록 합니다.

정책 평가

강력한 이름의 어셈블리에 바인딩할 때 CLR이 가장 먼저 수행하는 작업은 바인딩할 어셈블리의 버전을 결정하는 것입니다. 이 프로세스는 참조를 만드는 어셈블리의 매니페스트에 기록된 원하는 어셈블리의 버전 번호를 읽는 것으로 시작합니다. 그런 다음 정책을 평가하여 정책 수준에 다른 버전으로의 리디렉션이 포함되어 있는지 확인합니다. 정책 수준은 애플리케이션 정책부터 순서대로 평가되고 게시자와 관리자가 차례로 평가됩니다.

모든 수준에서 찾은 리디렉션은 이전 수준의 문을 재정의합니다. 예를 들어 어셈블리 A가 어셈블리 B를 참조한다고 가정해 봅시다. A의 매니페스트에서 B에 대한 참조는 버전 1.0.0.0입니다. 또한 어셈블리 B와 함께 제공되는 게시자 정책은 참조를 1.0.0.0에서 2.0.0.0으로 리디렉션합니다. 또한 버전 정책에는 참조를 버전 3.0.0.0으로 안내하는 컴퓨터 전체 구성 파일이 있습니다. 이 경우 컴퓨터 수준에서 만든 문은 게시자 수준에서 만든 문을 재정의합니다.

게시자 정책 우회

세 가지 유형의 정책이 적용되는 순서 때문에 게시자의 버전 리디렉션(게시자 정책)은 호출 어셈블리의 메타데이터에 기록된 버전과 설정된 애플리케이션별 정책을 모두 재정의할 수 있습니다. 그러나 애플리케이션이 항상 버전 관리에 대한 게시자의 권장 사항을 수락하도록 강요하면 DLL Hell로 돌아갈 수 있습니다. 결국 DLL Hell은 주로 공유 구성 요소에서 이전 버전과의 호환성을 유지하는 데 어려움을 겪습니다. 새 버전의 공유 구성 요소를 설치하여 애플리케이션이 손상되는 시나리오를 방지하기 위해 .NET의 버전 정책 시스템을 사용하면 개별 애플리케이션이 게시자 정책을 무시할 수 있습니다. 즉, 애플리케이션은 사용할 버전에 대한 게시자의 권장 사항을 거부할 수 있습니다. 애플리케이션은 애플리케이션 구성 파일에서 "publisherPolicy" 요소를 사용하여 게시자 정책을 무시할 수 있습니다.

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

<publisherPolicy apply="no"/>

</assemblyBinding>

.NET 구성 도구를 사용하여 버전 정책 설정

다행히 .NET Framework 그래픽 관리 도구와 함께 제공되는 것이므로 XML 정책 파일을 직접 편집하는 것에 대해 걱정할 필요가 없습니다. 이 도구는 애플리케이션 및 컴퓨터 전체 버전 정책을 모두 지원합니다. 도구는 제어판 관리 도구 섹션에서 찾을 수 있습니다. 관리 도구의 초기 화면은 그림 6과 같습니다.

그림 6. 관리 도구

다음 단계에서는 애플리케이션별 정책을 설정하는 방법을 설명합니다.

  1. 트리 뷰의 애플리케이션 노드에 애플리케이션을 추가합니다. 애플리케이션 노드를 마우스 오른쪽 단추로 클릭하고 추가를 클릭합니다. 추가 대화 상자에는 선택할 .NET 애플리케이션 목록이 표시됩니다. 애플리케이션이 목록에 없는 경우 기타를 클릭하여 추가할 수 있습니다.

  2. 정책을 설정할 어셈블리를 선택합니다. 구성된 어셈블리 노드를 마우스 오른쪽 단추로 클릭하고 추가를 클릭합니다. 옵션 중 하나는 애플리케이션이 참조하는 어셈블리 목록에서 어셈블리를 선택하는 것이며 그림 7과 같이 다음 대화 상자를 표시합니다. 어셈블리를 선택하고 선택을 클릭합니다.

    그림 7. 어셈블리 선택

  3. 속성 대화 상자에서 버전 정책 정보를 입력합니다. 바인딩 정책 탭을 클릭하고 그림 8과 같이 테이블에 원하는 버전 번호를 입력합니다.

    그림 8. 바인딩 정책 탭

배포

배포에는 코드를 패키징하고 애플리케이션이 실행되는 다양한 클라이언트 및 서버에 패키지를 배포하는 두 가지 이상의 측면이 포함됩니다. .NET Framework 주요 목표는 제로 임팩트 설치 및 xcopy 배포를 가능하게 하여 배포(특히 배포 측면)를 간소화하는 것입니다. 어셈블리의 자체 설명 특성을 사용하면 레지스트리에 대한 종속성을 제거하여 설치, 제거 및 복제를 훨씬 간단하게 만들 수 있습니다. 그러나 xcopy가 배포 메커니즘으로 충분하지 않거나 적절한 시나리오가 있습니다. 이러한 경우 .NET Framework 광범위한 코드 다운로드 서비스와 Windows Installer와의 통합을 제공합니다.

패키징

.NET Framework 첫 번째 릴리스에서 사용할 수 있는 세 가지 패키징 옵션이 있습니다.

  • 기본 제공(DLL 및 EXE). 대부분의 시나리오에서는 특별한 패키징이 필요하지 않습니다. 애플리케이션은 개발 도구에서 생성된 형식으로 배포할 수 있습니다. 즉, DLL 및 EXE의 컬렉션입니다.
  • Cab 파일. Cab 파일을 사용하여 보다 효율적인 다운로드를 위해 애플리케이션을 압축할 수 있습니다.
  • Windows Installer 패키지. Microsoft Visual Studio .NET 및 기타 설치 도구를 사용하면 Windows Installer 패키지(.msi 파일)를 빌드할 수 있습니다. Windows Installer를 사용하면 애플리케이션 복구, 주문형 설치 및 기타 Microsoft Windows 애플리케이션 관리 기능을 활용할 수 있습니다.

배포 시나리오

.NET 애플리케이션은 xcopy, 코드 다운로드 및 Windows Installer를 통해 다양한 방법으로 배포할 수 있습니다.

웹 애플리케이션 및 웹 서비스를 비롯한 많은 애플리케이션의 경우 배포는 파일 집합을 디스크에 복사하고 실행하는 것만큼 간단합니다. 제거 및 복제는 파일을 삭제하거나 복사하는 것만큼 쉽습니다.

이 .NET Framework 웹 브라우저를 사용하여 광범위한 코드 다운로드 지원을 제공합니다. 이 영역에서 다음과 같은 몇 가지 개선 사항이 수행되었습니다.

  • 영향을 주지 않습니다. 컴퓨터에 레지스트리 항목이 만들어지지 않습니다.
  • 증분 다운로드. 어셈블리 조각은 참조될 때만 다운로드됩니다.
  • 애플리케이션에 격리된 다운로드. 한 애플리케이션을 대신하여 다운로드한 코드는 컴퓨터의 다른 사용자에게 영향을 줄 수 없습니다. 코드 다운로드 지원의 주요 목표는 사용자가 특정 웹 사이트로 이동하고 해당 새 버전이 다른 애플리케이션에 부정적인 영향을 미치는 부작용으로 일부 공유 구성 요소의 새 버전을 다운로드하는 시나리오를 방지하는 것입니다.
  • Authenticode 대화 상자가 없습니다. 코드 액세스 보안 시스템은 모바일 코드가 부분 신뢰 수준으로 실행되도록 허용하는 데 사용됩니다. 사용자에게 코드를 신뢰하는지 여부를 결정하도록 요청하는 대화 상자가 표시되지 않습니다.

마지막으로 .NET은 Windows Installer 및 Windows의 애플리케이션 관리 기능과 완전히 통합됩니다.

요약

이 .NET Framework 영향을 주지 않으며 DLL Hell을 해결합니다. 어셈블리는 이러한 기능을 사용하도록 설정하는 데 사용되는 자체 설명 버전 관리 가능한 배포 단위입니다.

격리된 애플리케이션을 만드는 기능은 다른 애플리케이션의 시스템 변경으로 인해 영향을 받지 않는 애플리케이션을 빌드할 수 있기 때문에 매우 중요합니다. 이 .NET Framework 애플리케이션의 디렉터리 구조 내에 배포된 애플리케이션-프라이빗 어셈블리를 통해 이러한 유형의 애플리케이션을 권장합니다.

나란히 .NET에서 공유 및 버전 관리 스토리의 핵심 부분입니다. 여러 버전의 어셈블리를 머신에서 동시에 설치하고 실행할 수 있으며 각 애플리케이션에서 해당 어셈블리의 특정 버전을 요청할 수 있습니다.

CLR은 애플리케이션의 일부 간에 버전 정보를 기록하고 런타임에 해당 정보를 사용하여 적절한 버전의 종속성이 로드되도록 합니다. 버전 정책을 애플리케이션 개발자와 관리자가 사용하여 지정된 어셈블리의 버전을 유연하게 선택할 수 있습니다.

Windows Installer, 코드 다운로드 및 간단한 xcopy를 포함하여 .NET Framework 제공되는 몇 가지 패키징 및 배포 옵션이 있습니다.