삽질의 현장/- .NET

#073_닷넷(.NET)_.Net Framework 기본 - 동적 어셈블리 로딩

shovelman 2015. 11. 11. 18:46


안녕하세요 삽잡이입니다.


이번 시간에는 동적으로 어셈블링을 하고,

객체를 생성해보고, 메서드를 바인딩해보려고합니다.


머나먼 여정이 될것 같습니다... 같이 힘내서 달려봅시다!



동적이라는 뜻은 '런타임'시간을 의미하고,

정적이라는 뜻은 '컴파일' 시간을 의미합니다.


그렇다면 동적 어셈블리 로딩이란 무엇일까요?

바로, 실행시간에 어셈블리를 로드하는 것이지요.


도대체 동적, 정적을 왜 나눠놓은 것일까요?

각기 필요가 있으니 나눠놓은 것이지요...



ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ  



정적으로 어셈블리를 로드 할 경우 '고정 불변'입니다.

즉, 컴파일할 때에 어셈블리를 참조하게 되면 변경이 불가능해지는 것입니다.


그런데 동적으로 어셈블리를 로드할 경우에는 '변화가 가능'합니다.

원하는 시점에 Load/UnLoad가 가능하다는 소리입니다.


System.Reflection의 멤버로써

Assembly에서는 어셈블리를 로드할 수 있는 기능을 제공해줍니다.



정적 메서드에 어셈블리의 이름을 넘기게 되면 어셈블리 객체를 반환해줍니다.

(물론, string 형식의 이름을 뜻하는 것입니다.)



공유 어셈블리는 강력한 이름을 지정해주지 않으면 열리지 않습니다.

따라서 강력한 이름을 넣고 load하여 실행하면 타입을 리플렉션해줍니다.


'@'는 문자열 그대로의 문자가 되는 것입니다.

즉, 문자열 표식에는 \(역슬래시)를 사용했었지요.

이를 그대로 역슬래시 필요없이 문자열 그대로 문자로 취급하기 위해서 사용한 것입니다.


실행시간일 경우, 위의 코드의 SapLibrary는 어셈블리에 참조되어있지 않습니다.

그 전에는 컴파일 시간때 참조를 해뒀기 때문에 메타데이터에 포함되어있었습니다.

즉, 메니페스트에 참조되있는 것을 확인할 수 있었다 이겁니다.


그런데, 지금은 다릅니다.

전혀 상관없는 어셈블리였다가 

실행시간에 load해서 내 프로그램에 load된다는 것입니다.


뭔 x 소리야^^...  



실행시간에 load함으로써 즉, 내 프로그램에 받아들인다는 것입니다.

공유 어셈블리도 load 할 수 있는 것을 확인했습니다.


이로써 어셈블리 안에 형식들을 확인할 수 있었습니다.

또한 어셈블리를 런타임 시간 때에 즉, 동적으로 load도 했습니다.


동적으로 로드한다? 정적으로 로드한다?

Load한다는 것이 오른쪽 마우스를 눌러서 '참조'하는 것과 같습니다.

그런데 이건 컴파일 시간일 때에 이야기고

지금은 런타임 시간에 어셈블리 로딩을 한것이지요.



지금까지는 어셈블리를 load한 것입니다.

그런데 지금부터는 

어셈블리를 만들고 호출하는 과정에 대해서 따라가보려고합니다.

이를 통해 내가 알지 못했던 어셈블리를 load해서 메모리에 띄우고,

객체를 생성할 수 있게 된다 이겁니다.


늦은 바인딩이란,

컴파일 시 알지 못했던 타입의 객체를 생성하고 '런타임 시 호출하는 기술'을 의미합니다.


그렇다면, 이른 바인딩은?

지금까지 우리가 사용했던 방식들이 모두 이른 바인딩입니다.

우리는 기존에 알고 있던 형식들에 대해서 

객체를 생성하고 메서드를 호출하지 않았습니까?



여기서 바인딩이란,

'객체와 인터페이스 메서드를 연결하는 작업'을 뜻합니다.

따라서 객체를 통해 인터페이스를 호출할 수 있지요.


 

타입에 대한 정보를 알고 있다면, 이는 이른 바인딩이라고할 수 있습니다.

SapZil() 이라는 메서드가 a의 객체에 연결되었다는 것을 컴파일 시에 결정하기 때문입니다.


그런데, 우측과 같은 경우는 늦은 바인딩을 말합니다.

우선, 런타임 바인딩을 하기 위해서는 객체를 생성할 수 있어야합니다.

객체 없이 호출할 수 없기 때문입니다.



따라서, 객체를 생성하기 위해서 문자열을 넘겨 타입을 얻고 (동적 로딩)

해당 객체를 생성합니다.


모든 형식에는 Object의 GetType() 메서드가 있습니다.

그런데 이것을 말하는게 아니라,

어셈블리 내에도 정적 메서드로 GetType() 메서드가 있습니다.


따라서, 어셈블리의 이름을 주고 타입 객체를 뽑아낼 수 있지요.

어셈블리라는 형식의 정적 메서드인 GetType() 메서드를 호출함으로써

타입 객체를 받을 수 있다는 것입니다.


Activator라는 클래스 형식에는 CreateInstance()라는 메서드가 있습니다.

해당 메서드는 '내가 객체를 생성하고자하는 타입 객체'를 인자로 받습니다.


뭐 이리 힘들게 쓰나요...

이른 바인딩은 컴파일 시 객체를 만들고,

늦은 바인딩은 런타임 시 객체를 만들고... 왜 이렇게 써요 ㅠㅠ


둘을 한번 비교해보겠습니다.


 

좌측은 늦은 바인딩, 우측은 이른 바인딩입니다.

늦은 바인딩이 참 복잡하군요...


좌측의 경우 

Sap이라는 타입을 모릅니다. 

문자열로 받지요. 따라서 사용자에게 Input을 받는 것과 같습니다.

사용자가 뭘 입력할지 예상하고 구현합니까... 뭘 입력할지 모르지 않겠습니까?

그리고 입력하는 형식 정보를 어디에서도 찾을 수 없습니다.


리플렉션이란 내가 알지 못하는 정보를 실행시간에 얻는 과정이라고 했습니다.


그래서 s라는 타입을 가지고 있는 어셈블리를 동적으로 로딩을 하고,

타입 객체를 생성하고,

객체를 생성하는데 까지 성공한 것입니다.


위의 예제에서도 혹여나 뭐지... 하는 부분이 있으실 것입니다.

맞습니다... 아직 메서드 호출에 대한 언급을 드리지 않았습니다.


객체 생성하면 어떠한 서비스도 못받습니다. 따라서 의미가 없지요.

그래서 이 서비스를 위한 바인딩 즉, 호출을 할 수 있도록 해야합니다.


물론, 'obj에서 인스턴스 객체를 생성할 때 형식변환을 하면 되지 않느냐'라고 

명쾌하게 생각하시는 독자분들에게 말씀드리자면, 못.합.니.다..

형식을 모르는데, 어떻게 형식 변환을 합니까... 허허...

형식 메타데이터에 타입 정보가 들어있지 않다면,

형식 변환을 못해준다고 하지 않았습니까?


우선 계속 진행해봅시다.



우리는 Sap이라는 타입의 객체 s를 얻었습니다.

그리고 이제, GetMethod() 메서드를 사용해서 메서드의 이름을 호출할 수 있습니다.

그래서 MethodInfo 형식에 객체를 얻을 수 있지요.


우리는 direct로 호출을 할 수 는 없고,

타입 객체 s는 Sap 클래스에 대한 설명을 가지고 있는 완벽한 타입 객체이기 때문에,

타입 객체에게 얘기를 하는것입니다.



타입 객체 s야! GetMethod() 를 통해 객체를 넘겨줘!  



이렇게 하면 삽잡이의 노력이 통했는지,

해당 메서드의 이름을 넘겨줍니다.


그리고 해당 메서드의 이름을 MethodInfo[]에 받습니다.

즉, Method에 대한 Info형식에 받는 것입니다.


Type에는 인스턴스, 객체 다 있습니다.

이제 연결 즉, 바인딩만 하면 됩니다.

이를 바로 InVoke() 메서드를 통해서 수행하는 것입니다.



객체를 넘기는 것입니다.

InVoke 에는 '불러 일으키는'이라는 뜻이 있습니다.

호출하고자하는 객체와 매개 변수를 넘기게 되지요.


해당 코드가 바로

이른 바인딩에서 s.SapZil(); 과 같은 코드가 되는 것입니다.


그런데 다시 말씀드리지만, 좌측은 늦은 바인딩으로써 

객체가 메서드를 연결하는 작업을 말하는 것이고,

우측은 이른 바인딩으로써

InVoke() 메서드로 SapZil() 메서드가 호출되는 것입니다.


그런데, 아무리 봐도 복잡합니다.

따라서 .NET Framework 4.0 버전부터 다이나믹 형식을 만들어놨습니다.

즉, 동적 형식을 말하는 것입니다.



실행시간에 함수를 매핑 시키고 호출시켜주는 메커니즘을 가지고 있습니다.



그런데 신기한 것은 obj .(쩜)! 을 하는 순간 아무것도 나오지 않습니다.

그래도 우직하게 메서드를 써내려갑니다.



늦은 바인딩 즉, 메서드 호출을 할 때에

다이나믹 형식은 컴파일을 할 때에 타입 검사를 한다는 것입니다.

기존에는 컴파일을 할 때에 타입 검사를 했습니다.

런타임때 검사를 한다 이겁니다.



그런데, 없는 메서드를 써도 에러가 나지않습니다.

타입 검사를 런타임 때에 실행하기 때문에 그렇습니다.


var와 비교를 할 것은 없습니다. 

var는 컴타일 타임 때 

왼쪽 타입을 보고 오른쪽 타입을 결정하는 것이기 때문입니다.

다이나믹 타입은 

컴파일 때 아무것도 하지 않습니다. 즉, 형식 검사 자체를 안한다 이겁니다.


동적 타입이 정말로 편리합니다.



이와 같은 기존의 과정을 어마무시하게 짧게 바꿔버렸습니다.



다이나믹으로 쉽게! 가능해졌습니다.

단, 런타임때에 실행되는 단점이 있습니다.


기본적으로 닷넷은 '강력한 타입'이라는 것을 제공해줍니다.

컴파일 시간 때 정학한 타입 외에는 

그 기능을 수행할 수도, 형식을 사용할 수도 없다는 것입니다.

이게 바로 강력한 형식에 대한 개념입니다.


강력한 타입이라는 것은 컴파일러가 엄격하게 형 검사를 한다는 것이고,

기본적으로 닷넷은 강력한 타입의 언어라고 할 수 잇습니다.


그런데 dynamic 타입은 예외라는 것입니다.


이번 시간은 여기까지 하도록 하겠습니다!


이상 삽잡이였습니다!