본문 바로가기

IT 살이/04. 기술 - 프로그래밍

어셈블리 바인딩3-정리

어셈블리 바인딩 관련 최종 버전은 아래 링크로 바로 갈 수 있다.

2015/10/15 - [04.기술-APP/.NET InDepth] - 어셈블리 바인딩(최종)



요즘 일하면서 어셈블리 바인딩 문제를 몇 건 만나고 있어서, 오래전에 이에 대해서 올린 포스트를 훑어 보았다.

어셈블리 바인딩1

어셈블리 바인딩2

시간이 꽤 지난 지금 읽어보니 글을 컴팩트하게 정리할 필요를 느꼈다.

 

어셈블리 바인딩이란?

 

하나의 .NET 어셈블리는 여러개의 다른 어셈블리를 참조한다. 개발시, 참조하는 어셈블리와 참조되는 어셈블리는 주로 같은 디렉토리에 있기때문에 많은 개발자들은 하나의 어셈블리가 다른 어셈블리에 있는 메소드를 사용하는 것을 당연하게 생각한다. 그러나 참조되는 어셈블리는 크게 "검색"하고, 찾게 되면 "로딩"하는 과정을 거치게 된다. 그 단계 사이에서도 좀 더 많은 절차들이 있는데 아래에서 정리하려고 한다.

 

어셈블리 바인딩을 이해하고 나면? 

바인딩 단계를 이해하고 나면 이미 배포된 프로덕션 환경에서 유지보수를 해야 하는 담당자들이나 또는 프로젝트의 공통을 담당하는 이들은 .NET에 대한 막강한 파워를 얻을 수 있게 된다.

예를 들어 프로덕션 환경에서 특정 모듈의 오류로 인해 업데이트를 해야 하는 경우 전체 시스템을 빌드하지 않고 단순히 해당 모듈만을 수정, 빌드해서 configruation의 수정만으로 기존 모듈을 대체할 수 있게 된다. 또는 바인딩 과정을 이해하고 나면 어셈블리 바인딩 로그 뷰어 fuslogvw.exe같은 툴을 이해할 수 있어서 .net 어플리케이션의 디버깅에 사용할 수도 있게 된다. 이 툴은 어셈블리를 검색하고 로딩하는 단계까지의 로그를 보여준다.

이제 어셈블리가 참조하고 있는 다른 어셈블리를 검색하고 메모리로 로딩하는 절차를 최대한 최대한 실무적으로 깔끔하게 정리하도록 한다. 만약 너무 컴팩트하다고 느껴진다면 이전 포스팅을 읽어보기 바란다.

 

어셈블리 바인딩 절차 정리

 

하나의 어셈블리가 참조하는 다른 어셈블리를 찾아서 로딩하는 절차를 그린 그림이다. 

http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx

http://msdn.microsoft.com/en-us/magazine/dd727509.aspx

 

 

 

검색할 어셈블리 즉 참조되는 어셈블리에 대한 최초 정보는 참조하는 어셈블리 즉 호출하는 어셈블리가 가지고 있는 메타 정보이다. 검색하는 절차를 거치면서 Configuration의 설정값을 통해서 검색할 어셈블리의 버전이 변경되기도 하고 위치가 변경되기도 한다. 그림을 따라가면서 이해할 수 있을 것이다.

 

Configuration 예 - <bindingRedirect>, <codeBase>

 

bindingRedirect 설정

<configuration>

 <runtime>

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

         <dependentAssembly>

             <assemblyIdentity name="myAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="neutral" />

              <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>

          </dependentAssembly>

      </assemblyBinding>

   </runtime>

</configuration>

 

위 설정은 찾고 있는 어셈블리의 아이덴터티가  name="myAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="neutral"  이고 그리고 버전이 "1.0.0.0"이라면 새로운 버전 "2.0.0.0"의 어셈블리를 찾으라는 의미이다. 다른 어셈블리에 대한 버전 리다이렉트를 추가하고 싶으면  <dependentAssembly> 요소를 추가하면 된다. 

 

codeBase 설정

<configuration>

   <runtime>

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

         <dependentAssembly>

            <assemblyIdentity name="myAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="neutral" />

            <codeBase version="2.0.0.0" href="http://www.litwareinc.com/myAssembly02.dll"/>

         </dependentAssembly>

        <dependentAssembly>

            <assemblyIdentity name="yourAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="neutral" />

            <codeBase version=“3.0.0.0" href="http://www.litwareinc.com/myAssembly03.dll"/>

         </dependentAssembly>

      </assemblyBinding>

   </runtime>

</configuration>

 

위 설정은 어셈블리 아이덴터티가 name="myAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="neutral" 

이고 버전이 "2.0.0.0"인 어셈블리는  http://www.litwareinc.com/myAssembly02.dll 에서 다운받아 사용하라는 의미이다.   위 설정에서는 "3.0.0.0" 버전인 "yourAssembly"에 대한 <codebase>를  추가하는 방법도 보여주고 있다.

 

 

CodeBase 적용하는 어셈블리는 서명된 공개 어셈블리( signed assembly)여야 한다. 다음처럼 서명되비 않은 어셈블리를 다운로드 하려고 한다고 해 보자.

 

      <dependentAssembly>

        <assemblyIdentity name="assemblyA" publicKeyToken="null" culture="neutral" />

        <codeBase version="1.0.0.0" href="http://xxx.yyy.com/~/assemblyA.dll"/>

      </dependentAssembly>

 

서명되지 않은 어셈블리를 codebase를 이용해서 다운로드하려 할때 fuslogvw.exe에 남는 로그를 보면 다음과 같습니다..

 

오류 : 전용 어셈블리가 appbase밖에 있습니다. 소스 Url은 http://xxx.yyy.com/~/assemblyA.dll 입니다.

로그 : 전용 어셈블리가 잘못된 위치에 있습니다.

 

어셈블리 바인딩 관련 예외

 

어셈블리 바인딩은 크게 두 단계가 있는데, 참조되는 어셈블리를 "검색"하고 그리고 검색이 되면 "로딩"하는 단계이다. 각 단계에서 검색이 안되거나 로딩하는데 문제가 발생하면 예외가 발생한다 : FileNotFoundException, FileLoadException. 그리고 파일을 로딩하는 단계에서는 다른 두개의 예외가 더 발생할 수 있다 : BadImageFormatException, SecurityException.

 

FileNotFoundException

- 어셈블리 파일을 찾지 못해서 발생하는 예외

BadImageFormatException

- 찾은 파일(이미지)이 잘못된 경우( 파일이 깨지거나 변경된 경우 )

SecurityException

- 어셈블리 파일의 위치에 대한 권한(CAS)이 부족해서 발생한 예외

FileLoadException

-  "Access is denied" (for hresult E_ACCESSDENIED, 0x80070005)

-   "The located assembly's manifest definition with name [yourAssembly] does not match the assembly reference"

- 파일을 찾고, 권한 검사를 통하고 실제로 로딩하는 과정에서 발생하는 예외.
  
어셈블리 파일을 다른 프로세스에서 락킹(lock)하고 있거나 또는 검색한 파일이 실제로 참조하고 있는 어셈블리 Identity불일치하는 경우

 

아래 링크의 페이지를 보면 예외들에 대한 디버깅을 하는 방법도 알 수 있다.

Debugging Assembly Loading Failures

http://jaworskig.blogspot.kr/2010/03/debugging-assembly-loading-failures.html

 

■ 바인딩 절차 요약

 

qSigned 어셈블리 :
§1) 버전 확인 : <bindingRedirect>
§2) 파일 위치 확인 :
GAC 확인
위치 리다이렉팅(<codebase>) 확인
어플리케이션 베이스 디렉토리( .exe 폴더, bin 폴더 )
[LoadFrom 위치]
§3) 어셈블리 Identity 확인
어셈블리 Identity : 어셈블리이름, 버전, culture, publickeytoken
qUnsigned 어셈블리
§파일 위치확인
어플리케이션 베이스 디렉토리( .exe 폴더, bin 폴더 )

[LoadFrom 위치]

 

적용 시나리오 예

 

 

 

위 그림은, 실행 어플리케이션은 ClickOnce로 배포되고(1단계) 화면 어셈블리는 메뉴를 클릭할때(2단계) 서버에서 해당 화면이 포함된 어셈블리를 다운로드해서 화면을 출력하는 구조의 시스템이다.

전사 공통으로 사용하고 있는 버전이 10( v10)인 Interop.Excell.dll인 어셈블리는 ClickOnce 어플리케이션에 포함되어 있어서, 어플리케이션을 시작하면 1단계에서  자동으로 로컬 머신에 다운로드되어 v10 어셈블리가 설치된다.

v14의 interop.Excell.dll에는 추가된 기능이 있어서, 특정 업무의 화면에서는 v14를 사용하고 싶어하는 상황이다. 즉 v10이 이미 어플리케이션 디렉토리에 있는 상황에서 이것을 사용하지 않고 서버측에 있는 v14를 사용하고 싶은 것이다.

그림은 이 요구사항을 <codebase>로 해결하고 있는 것을 보여주고 있다. 

메뉴를 클릭하면 메뉴 정보에서 해당 화면이 포함된 어셈블리를 찾고 그 어셈블리는 서버측에서 다운로드된다( 3단계). NTD 방식으로 다운로드된 업무 어셈블리는 v14 버전의 interop.Excell.dll을 참조하고 있다. 따라서 v14의 어셈블리를 바인딩하기 위해서 앞에서 설명한 바인딩 절차를 따르게 된다. 강력한 이름의 어셈블리임을 확인하고 GAC에서 먼저 검색하고 그곳에 v14이 없으면 <codebase>를 확인하는 단계로 넘어가서 설정된 값을 확인하게 된다. 최종적으로 <codeBase>에 설정된 v14 어셈블리의 위치를 확인하고 다운로드하게 된다.

 

 

 

 

'IT 살이 > 04. 기술 - 프로그래밍' 카테고리의 다른 글

.NET 캐시  (0) 2013.08.09
back to the basic : .NET Interoperablility  (1) 2013.02.11
LoadFrom 컨텍스트  (0) 2013.02.09