본문 바로가기

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

어셈블리 바인딩 1

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

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


어셈블리 바인딩 1

이번 포스트를 이해한다면 .NET 애플리케이션 특히 NTD 스마트클라이언트 애플리케이션의 많은 부분을 이해할 수 있는 기초가 다져지게 될 것이다.

우리는 다른 어셈블리를 사용하고 싶을때는 VS.NET의 솔루션 탐색기의 프로젝트에서 참조 추가를 통해서 원하는 작업을 쉽게 수행한다. 또는 코드상에서 직접 Assembly.Load(), Assembly.LoadFrom() 메소드를 사용해서 원하는 어셈블리를 메모리로 로딩시키는 경우도 있다.

정적이든 동적이든 대상 어셈블리 파일을 로딩하는 과정은 어셈블리 바인딩(assembly binding)이라는 다소 복잡한 과정을 거친다. 그러나 .NET 애플리케이션을 제작하다 보면 이 바인딩 과정에 대한 이해가 필요한 경우가 자주 등장한다. 특히 웹 서버로부터 자동 배포를 구현하는 NTD 애플리케이션에서는 아주 중요한 개념으로 다가온다.

이제 이 바인딩과 로딩에 대해서 알아볼텐데 우선 이 바인딩을 주관하는 엔진이 있는데 이른바 "퓨전(fusion)"이라고 부른다. 퓨전이 바인딩과 로딩을 수행하는 과정에서 남기는 로그는 fuslogvw.exe 툴을 통해서 볼 수 있다.  이 로그는 스마트클라이언트 애플리케이션의 디버깅 및 어셈블리의 바인딩 과정 이해 및 디버깅에 중요한 단서로 사용된다.

최종적으로 어셈블리의 완전한 로딩이 목표인데 이 단계까지 가기 위해서는 퓨전은 몇 단계의 중요 과정을 거친다.

첫번째 단계는 어셈블리가 이미 로딩되어 있는지, 바인딩 컨텍스트라는 캐시를 확인하는 단계이다. 그렇지 않다면 두번째 단계로 어셈블리 파일을 찾아가는 과정이 있고 그 다음은 그 어셈블리 파일이 가지고 있는 어셈블리가 자신이 찾는 어셈블리인지를 확인하는 단계를 거친다.

바인딩의 방법에는 다음 두가지로 분류가 되는데 미리 정의를 하고 시작하자.
1) 이름 기반의 바인딩
이 방법은 호출하는 어셈블리(referencing or calling assembly)가 가지고 있는 대상 어셈블리(referenced assembly)에 대한 이름 정보를 기반으로 해서 어셈블리를 찾아가는 과정을 말한다.
2) 경로 기반의 바인딩
이 방법은 대상 어셈블리 파일에 대한 경로가 주어지게 되는데 이 경로를 통해서 바로 해당 파일을 찾고 로딩하는 과정을 말한다. 경로 기반의 바인딩에서는 바인딩이 두번 일어난다. 첫번째 바인딩에서는 인자로 주어진 경로의 어셈블리에서 어셈블리 아이덴터티 정보( 이름, 버전, 컬쳐, 공개키)를 가져온다. 두번째 바인딩에서는 그 정보를 이용해서 Load()와 같은 바인딩을 한번 더 수행한다. 이것은 v2.0에서 변경된 LoadFrom()의 바인딩 방식이다.

어셈블리가 이미 로딩되어 있는지를 확인하는 단계에 대해서는 이전 포스트에서 별도로 다뤘다. 만약 이미 로딩되어 있지 않은 경우라면, 어셈블리를 찾아나서게 된다.

■ 어셈블리 찾아가기( Assembly Locating )

어셈블리 바인딩 로직을 많은 책들이 설명하고 있지만 그 복잡한 로직을 모두 다 전하고 있지는 않고 있다. 그리고 중요한 포인트가 될 만한 곳이 누락된 곳도 있다. MSDN에서도 그 로직을 정확하게 전하고 있지는 않다. 달봉이는 어셈블리 바인딩에서 대해서 자세히 알면 알 수록 시스템의 개발과 운영에 나름대로의 노하우와 융통성이 생길 수 있다고 본다.

■ 퓨전의 어셈블리 바인딩

어셈블리가 다른 어셈블리에 속하는 타입을 사용하려 할 때는 그 어셈블리에 대한 이름풀이 과정이 수행되어야 한다. 그리고 나서 그 어셈블리 파일의 위치를 결정하고 그 다음 해당 파일을 로딩하는 단계를 거친다.

이런 과정을 어셈블리 바인딩이라고 하는데, 말 그대로 풀이하자면 논리적인 정보를 이용해서 실제 존재하는 물리적인 파일과 연결시킨다는 의미로 말할 수 있을 것이다.  대상 어셈블리에 대해 주어지는 논리적인 정보에는 어셈블리의 이름정보(4가지, 사용자 친화적인 어셈블리명, 버전, 컬쳐, 공개키(토큰))일 수도 있고 위치 정보 일 수도 있다. 여기서 위치 정보란 것은 "어디에 가면 어떤 파일이 있을 것이다"라는 말 그대로 정보이다. 이 정보를 받아서 실제로 파일이 있는지를 확인하는 과정이 바인딩에 포함된다.

이름 정보를 기반으로 해서 어셈블리의 위치를 확인하는 것을 이름 기반의 바인딩(name-based binding)이라 하고 위치 정보를 이용해서 어셈블리의 실제 위치를 확인하는 것을 위치 기반의 바인딩(location-based binding)이라고 한다. 이런 바인딩 과정을 수행하는 .NET 프레임워크 하부 시스템의 이름을 퓨전(fusion)이라고 부른다. 퓨전이 바인딩 작업을 하면서 로그를 남기고 fuslogvw.exe라는 어셈블리 바인딩 로그 뷰어라는 툴로 볼 수 있는 것이다.

어셈블리 바인딩을 영어에서는 resolving이라고 표현하고 이것을 담당하는 주체를 퓨전이라는 말 대신에 assembly resolver라는 말로 표현하기도 한다. 이 resolver라는 표현을 우리나라 말로 옮겨 놓은 것이 재밌다. MSDN에서는 "어셈블리 확인기"라는 말로 옮겼고 어떤 곳에서는 "어셈블리 이름풀이기"라는 말을 쓰기도 했다. "어셈블리 이름풀이"라는 말은 이름 기반의 바인딩(name-based binding)의 부분을 나타낼때는 괜찮은 표현이지만 그러나 바인딩을 모두 담아내지는 못하는 표현이다. 달봉이는 "어셈블리 바인딩"이라는 말과 그리고 원래의 용어 "퓨전"이라는 말을 그대로 사용하겠다. 그러나 이름 기반의 바인딩의 일부 과정을  나타낼 때는 때로 이름풀이라는 용어도 사용할 것이다.

이름 기반의 바인딩의 대표적인 메소드가 Assembly클래스의 Load()이고 위치기반의 대표적인 예가 LoadFrom() 메소드이다. 즉 Load() 메소드는 이름 정보를 인자로 받아서 그 물리적인 위치를 결정하는 단계를 밟고 LoadFrom()은 위치에 대한 정보를 바로 받아서 해당 어셈블리 파일을 결정하게 된다. 이름 기반의 바인딩을 Load타입 그리고 위치 기반의 바인딩을 LoadFrom타입이라는 말로도 표현한다. 상황에 따라서는 달봉이도 이 표현들을 사용하겠다.

이런 바인딩 타입에 따라 어셈블리가 로딩되는 과정이 달라진다. 그림은 바인딩 타입에 따라 달라지는 로딩 과정을 나타내고 있다.

이름 기반의 어셈블리 로딩

이름기반의 바인딩에 속하는 대표적인 예가 참조에 의한 어셈블리 로딩과 Assembly.Load(이름정보) 메소드에 의한 로딩이다. 참조에 의한 로딩이나 Asembly.Load()에 의한 로딩을 같은 타입으로 묶은 것은 둘 다 이름풀이 과정을 거치기 때문이다. 즉 두 방법 모두 부분적이거나 또는 완전한 이름이 주어지고 그 이름 정보를 통해서 해당 어셈블리의 물리적인 위치를 결정한다.
어셈블리의 위치가 결정되고 파일이 발견되면 주어진 이름 정보와 실제 발견된 어셈블리의 이름 정보가 모두 일치하는지를 확인한다. 예를 들어 약한 이름의 어셈블리의 경우, 주어지는 정보로 어셈블리 이름과 컬쳐가 있을 수 있다. 실제 파일 위치를 확인하는데는 어셈블리 이름만 이용된다. 그러나 위치가 확인되고 나서 비교 확인하는 단계에서는 나머지 정보도 모두 비교하게 되는 것이다. 이제 이후 과정부터서는 결정된 어셈블리 파일의 메너페스트에서 얻은 완전한 이름 정보를 이용하게 된다.
Assembly.Load(string assemblyName)의 인자 assemblyName은 어셈블리의 완전한 이름(fully qualified name)을 나타내는 문자열이다.

Assembly assm = Assembly.Load("myAssem,"+
  "version=1.0.0.0,publicKeyToken=a1690a5ea44bab32,"+
  "culture=neutral");
설정 파일 .config에 < qualifyAssembly >이라는 속성이 설정되어 있다면 간략한 이름을 사용할 수 있다.
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <qualifyAssembly
partialName="myAssembly"
              fullName="myAssembly,version=1.0.0.0,
           publicKeyToken=a1690a5ea44bab32,
    culture=neutral"/>
    </assemblyBinding>
  </runtime>
</configuration>

이런 설정이 있다면 다음처럼 메소드를 호출해도 된다.

Assembly assm = Assembly.Load("myAssembly");

<qualifyAssembly> 설정은 뒤의 "이름모으기"에서 설명한다.
이름풀이의 구체적인 과정은 중요한 문제이므로 뒤에서 자세히 다룬다. 하여튼 어셈블리 이름풀이를 통해서 파일의 물리적인 경로와 완전한 이름정보가 구해지게 된다.
어셈블리 이름풀이를 거치고 나서 바로 어셈블리를 로딩하는 것이 아니라 구해진 어셈블리의 아이덴터티 정보( 완전한 이름정보 )를 통해서 동일한 어셈블리가 이미 로딩 완료된 어셈블리 캐시에 있는지를 확인하는 과정을 거친다. 만약 이미 동일한 어셈블리가 로딩되어 있다면 기존의 로딩된 어셈블리를 사용하고 동일한 어셈블리가 발견되지 않은 경우는 새롭게 로딩하게 된다.

위치 기반의 어셈블리 로딩

Assembly.LoadFrom(경로)로 대표되는 위치기반의 바인딩에서는 어셈블리에 대한 위치가 인자로 주어진다고 했다. 위치 기반의 바인딩에서는 두 번의 바인딩이 일어난다. 첫번째는 인자로 주어진 경로값에 있는 어셈블리에서 먼저 완전한 이름 정보를 조회한다. 이제 이름 정보를 얻게 되었으니 이름 기반의 바인딩 과정이 가능해진다. 앞에서 봤던 이름 기반의 바인딩 과정을 수행한다. 만약 그래도 원하는 어셈블리가 없다면 경로로 주어진 어셈블리를 로딩하게 된다.

.NET 2.0에서 새로 도입된 방식이다. 이전 버전에서는 경로상의 어셈블리를 바로 로딩했다.

바인딩 과정을 거친다는 것은 예를 들어 LoadFrom()에 명확한 경로를 주더라도 바인딩 과정에서 버전 정책등에 의해서 다른 경로상에 있는 다른 버전의 어셈블리가 로딩될 수 도 있다는 것을 의미한다. 즉 특정 어셈블리에 대한 요청이 들어오면 다른 버전의 어셈블리 요청으로 리다이렉트시키는 일이 바인딩 과정중에 일어날 수 있다는 것인데, 바인딩의 구체적인 과정에 대해서는 뒤에서 다루겠다.

LoadFrom()처럼 경로를 인자로 받아서 로딩하는 메소드는 여러가지가 있다. LoadFrom()외에도 CreateInstanceFrom(), ExecuteAssembly()등도 LoadFrom() 타입에 속하는 방법이다(이런 메소드들에 대해서는 MSDN을 참고하기 바란다). 인자로 주어지는 어셈블리에 대한 경로 정보는 파일 시스템상의 경로(file://)도 될 수 있고, URL(http://)도 될 수 있다.

Assembly a = Assembly.LoadFrom("file://C:/usr/bin/xyzzy.dll");
Assembly a = Assembly.LoadFrom("http://myserver/myapp/xyzzy.dll");

다음 포스트에서는 구체적으로 어셈블리 바인딩하는 과정을 알아볼 것이다.

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

NTD 배포 및 어셈블리 로딩 그리고 IIS 설정  (1) 2009.04.23
어셈블리 바인딩 2  (0) 2009.04.23
어셈블리 구조  (0) 2009.04.23