어느덧 시리즈 네 번째 글이네요. 스마트 클라이언트를 이해하는데 얼마나 도움이 되는지 알 길이 없군요... 쯔읍... 이번 내용은 스마트 클라이언트 수행환경에서 생성된다는 AppDomain은 어떤 설정을 갖는지, 그리고 이 AppDomain은 어떤 configuration 파일을 사용하는지 알아 보도록 하겠습니다. 이번 포스트의 내용은
스마트 클라이언트 시나리오에서 DLL을 어디에서 다운로드 받는지를 결정하는데 중요한 역할을 수행하므로 잘 이해를 하셔야 성공적인 스마트 클라이언트 어플리케이션을 개발하실 수 있을 겁니다.
제가 단순 무식한 통 배짱으로 단언 하건 데, 이번 포스트의 내용을 잘 이해하지 않고서 성공적으로 스마트 클라이언트 어플리케이션을 구축하기 쉽지 않을 것입니다. 글의 처음 부분은 스마트 클라이언트 뿐 만 아니라 모든 닷넷 어플리케이션에도 적용되는 부분이므로 시간을 갖고 스터디 하시기 바랍니다.
시리즈 목차
Smart Client (IV) : App Base Directory & Configuration
닷넷의 모든 어플리케이션은 어플리케이션 도메인(application domain) 내부에서 작동하게 된다. 어플리케이션 도메인은 모든 닷넷 어플리케이션에 적용되는 개념이므로 여기서 간단하게 짚고 넘어가도록 하겠다. 그리고 스마트 클라이언트에서 상당히 중요하게 작용하는 어플리케이션 도메인의 어플리케이션 베이스 디렉터리가 무엇이고 스마트 클라이언트 시나리오에서 어떤 설정을 갖는지도 알아보도록 하겠다. 어플리케이션 도메인과 더불어 중요한 것은 configuration 파일이다. 닷넷 어플리케이션에 대한 다양한 설정을 수행할 수 있는 configuration 파일을 스마트 클라이언트에서는 어떻게 명시하고 사용하는지도 알아보도록 하겠다. 브라우저 임베디드 스마트 클라이언트에서 configuration 파일을 명시하는 것은 상당히 중요한 작업이며, 어플리케이션 베이스 디렉터리를 결정하는데도 아주 중요한 역할을 한다. 자... 기대하시라... -_-;
이번 포스트의 처음 부분은 좀 딱딱하게도 일반적인 닷넷 이야기가 주를 이룬다. 이들에 대한 이해 없이 스마트 클라이언트를 제대로 이해할 수 없기 때문에 지루하고 재미없는 부분이 먼저 나오게 되었다. 필자가 초기에 스마트 클라이언트에 대해 존나게 삽질할 때도 바로 닷넷 프레임워크의 기본적인 행동을 이해하지 못했기 때문에 삽질을 열나게 했던 것이다. 필자를 믿으라. 지루하더라도 읽어보라. 닷넷 CLR에 대한 기본적인 이해는 자욱한 안개와도 같은 스마트 클라이언트의 행동방식을 이해하는데 졸라 밝은 등대 역할을 해 줄 것이다. 싫음 말고... -_-;
What's Application Domain
어플리케이션 도메인은 닷넷 어플리케이션이 수행되는 가상의 수행 공간을 말한다. 어플리케이션 도메인은 고유의 어셈블리, 객체 힙, 쓰레드 공간을 갖으며, 서로 다른 어플리케이션 도메인은 엄격하게 격리되어 보호된다. 어플리케이션 도메인은 프로세스 개념과 매우 유사하다. 프로세스가 고유의 주소 공간(메모리), 운영체제 자원 등을 가지며 한 프로세스에서 수행되는 코드는 다른 프로세스의 메모리, 자원 등을 직접 액세스할 수 없다. 마찬가지로 한 어플리케이션 도메인의 코드는 다른 어플리케이션 도메인의 객체를 직접 액세스할 수 없으며, 한 어플리케이션 도메인의 오류(예외)가 다른 어플리케이션 도메인에 영향을 주지 않는 것 역시 프로세스와 비슷하다.
어플리케이션 도메인은 프로세스 내에 존재하며, 한 프로세스는 여러 어플리케이션 도메인을 가질 수 있다. 대개의 닷넷 프로그램이 수행되는 프로세스는 1개의 어플리케이션 도메인 만을 갖지만(그래서 개발자들이 소 닭보듯이 어플리케이션 도메인에 무관심한 것 같다), ASP.NET 작업 프로세스(aspnet_wp.exe 혹은 w3wp.exe 프로세스)나 스마트 클라이언트를 호스트하는 IE 브라우저(iexplore.exe 프로세스)는 2개 이상의 어플리케이션 도메인을 갖곤 한다. 닷넷 응용 프로그램이 수행되면 CLR이 구동되고 초기화 되면서 디폴트 어플리케이션 도메인(default application domain)을 생성한다. 디폴트 어플리케이션 도메인은 다른 어플리케이션 도메인과 다르게 CLR에 의해 특별하게 취급 받게 된다.
우리가 닷넷 어플리케이션을 개발할 때 매양 하는 짓이 뭣이더냐? 뭔가 좀 해보려면 항상 하는 것이 어셈블리 참조 아니던가? 어셈블리를 참조하고 그 어셈블리에 포함된 클래스를 사용하기 위해서는 그 어셈블리는 로드 되어야 한다. 어셈블리가 로드 되는 곳 역시 어플리케이션 도메인이다. Mscorlib.dll 과 같이 여러 어셈블리에 의해 공유되는 어셈블리가 있기도 하지만, 어플리케이션 도메인에 각각 따로 따로 로드 되는 경우도 있다(특히 스마트 클라이언트 시나리오에서 그렇다).

그림1. 브라우저 임베디드 스마트 클라이언트에서 다중 AppDomain 상황
그림1은 브라우저에 임베딩된 스마트 클라이언트 시나리오에서 iexplore.exe 프로세스 상에서 여러 어플리케이션 도메인이 생성될 수 있음을 보여주고 있으며 디폴트 어플리케이션 도메인 역시 자동으로 생성되었음을 보여주고 있다. 어플리케이션 도메인에 대해서 더 깊은 내용을 다루는 것은 이 글의 범위를 벗어나므로(글 쓸 때 정말 좋은 핑계다... 흐흐흐...), 다음 자료에서 MSDN에서 어플리케이션 도메인에 대한 설명을 찾을 수 있으므로 한 번쯤 읽어 보기 바란다. 한글 자료와 영문 자료를 모두 포함시켰다. 제목이 약간 다르지만 완전히 같은 내용이다. 한글을 읽다가 잘 이해가 안되면 영문 자료를 읽어보면 이해되는 경우(필자의 경우는 상당히 많았다)가 있기 때문에...
Application Base Directory in AppDomain
어플리케이션 도메인은 다양한 속성을 갖고 있다. 이들 속성들을 모두 설명할 순 없고, 관심을 갖는 부분이 바로 베이스 디렉터리(Base Directory) 속성이다. 어플리케이션 도메인의 베이스 디렉터리 속성은 닷넷 CLR(Common Language Runtime)이 어셈블리를 로드 할 때 참고하는 중요한 기준이 되기 때문에 매우 중요한 속성이라 할 수 있다.
Assembly Loading into Application Domain
어플리케이션 도메인이 어셈블리를 로드 하는 경우를 살펴보면 몇 가지로 나누어 볼 수 있다. 첫째로는 가장 많은 비율을 차지하는 것으로, 어셈블리 메니페스트(manifest)에 기록된 참조에 의한 암시적 로드이다. 졸라 어려워 보어지만 사실 간단하다. 우리가 비주얼 스튜디오에서 System.dll 이니 System.Web.Services.dll 등을 참조하면, 어셈블리 메니페스트에 이 참조 사항들이 기록되게 된다. 쫌 유식한 말로다가 정적 참조(static reference)라고 한다. 그리고 정작 이 참조된 어셈블리에 포함된 클래스가 최초로 사용하는 코드를 만나면 CLR은 암시적으로 참조된 어셈블리를 로드 하게 된다. 한가지 주의할 점은 어플리케이션이 시작될 때나 특정 시점에서 일거에 어셈블리들이 로드 되는 것이 아니라 참조된 어셈블리에 존재하는 클래스들 중 최초의 하나가 사용될 때에 어셈블리가 로드 된다는 점이다.
어플리케이션 도메인에 어셈블리가 로드 되는 두 번째 상황은 AppDomain 클래스의 Load 메쏘드나 Assembly 클래스의 Load 메쏘드 등 코드를 통해 명시적으로 특정 어셈블리를 로드 하는 경우이다. 코드에 의해 동적으로 로드 된다고 해서 동적 참조(dynamic reference)라고도 한다. Load 메쏘드는 어셈블리의 이름만을 명시할 뿐 어셈블리 파일(DLL, EXE)의 경로를 명시하지 않음에 유의해야 한다. 즉, Load 메쏘드(혹은 비슷한 메쏘드들)에 의해 어셈블리가 로드 되는 경우, CLR은 주어진 어셈블리 이름을 가지고 GAC, 로컬 디스크 등을 검색(?)하여 어셈블리를 찾는 Assembly Resolution 로직을 따르게 된다. 이 경우, Load 메쏘드에서 어떤 어셈블리를 로드 할 것인가를 명시적으로 밝히기 때문에 '참조'를 통해 메니페스트 어셈블리 참조를 기록할 필요는 없다. 즉, 비주얼 스튜디오에서 참조를 추가할 필요가 없다는 말이다.
세 번째 경우는 Assembly 클래스의 LoadFrom 메쏘드를 통해 어셈블리 DLL 파일을 명시하여 로드 하는 경우이다. Load와 LoadFrom은 그 의미가 매우 다르다. Load의 경우 어셈블리 이름을 명시하지만 LoadFrom의 경우, 어셈블리 파일 경로를 명시하는 것이다. LoadFrom이 호출되면 CLR은 주어진 경로의 어셈블리를 즉각 로드 하려 시도하고 이것이 실패하면(파일이 없다든가 등등) 곧바로 Exception을 발생한다. 이 말은 LoadFrom 호출 시 CLR은 어셈블리를 찾기 위해 Assembly Resolution 로직을 따르지 않는다는 말이다.
Assembly Resolving
암시적으로 CLR에 의해 어셈블리가 로드 되건 Load 류의 메쏘드가 호출되어 코드에 의해 명시적으로 어셈블리가 로드 되건, CLR은 주어진 어셈블리를 찾기 위해 Resolution 로직을 수행하게 된다(쓰바 Assembly Resolution 이란 용어는 한글화 하기 정말 까칠한 것 같다. 걍 원문 대로 쓰기로 해따... -_-; ). Assembly Resolution 로직이란, CLR이 주어진 어셈블리 이름(어셈블리 파일명이 아니다!!)을 통해 어셈블리를 찾는 과정을 말한다. 이 과정은 MSDN 에 아주 상세히 설명되어 있다. 한글 자료와 영문 자료를 모두 제시하는 이유는 앞서와 같다. 자신이 한 잉글리쉬 한다는 사람은 영문 자료를 보기를 바라며, 잉글리쉬에 핸디캡이 있는 사람이라도 영문자료를 보기를...
이러 저러한 과정이 아주 상세히 설명되어 있지만, 많이 적용되지 않은 상황들을 대충 제거하고 요약해 보면 이렇다. (제발 부탁이니, 이 요약 부분만 달랑 읽고 넘어가지 말고 꼭 위 자료를 읽어 보길 부탁한다. 닷넷 개발자라면 반드시 알고 있어야 할 필수 지식이니 말이다.)
- Configuration 파일의 바인딩 설정이 있다면 이 설정을 적용하여 로드할 어셈블리를 결정한다.
- 로드 하고자 하는 어셈블리의 이름이 Full Name 이면(어셈블리 이름, 버전, culture, public key), GAC을 먼저 찾는다. 주어진 어셈블리 이름이 Full Name 이 아니면 (달랑 어셈블리 이름만 주진 경우) GAC을 검색하지 않는다.
- GAC에서 어셈블리를 못 찾았거나, Full Name이 주어지지 않았다면, 어플리케이션 도메인의 베이스 디렉터리를 기준으로 어셈블리 파일을 찾아 가장 먼저 일치하는 어셈블리를 로드 한다.
먼저 어셈블리 이름에 대해 약간 이야기 하자면, System.dll 은 정확히 말해서 어셈블리 이름이 아니다. 닷넷 프레임워크 1.1에 포함된 System.dll 의 어셈블리 full name은? System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 이다. 그리고 이 어셈블리의 partial name은 System 이다. 이것에 주의할 필요가 있다. 예를 들어 Assembly.Load("System") 와 같은 코드가 수행되면 CLR은 GAC에서 System 어셈블리를 찾지 않는다. 왜냐하면 어셈블리의 full name이 주어지지 않았기 때문이다. 비슷하게 비주얼 스튜디오에서 어셈블리를 참조할 때, 참조된 어셈블리가 서명되어 있다면 암시적 어셈블리 로딩 과정에서 CLR은 GAC을 먼저 찾게 된다. 그러나, 참조한 어셈블리에 서명이 없다면 CLR은 GAC을 찾지 않는다.
어셈블리 전체 이름이 주어지지 않았거나, 주어졌더라도 GAC에서 어셈블리를 찾지 못한 경우, 현재 어플리케이션 도메인의 베이스 디렉터리를 기준으로 어셈블리 파일을 찾는 과정(probing 이라 한다)을 수행한다. 그렇다. 필자는 이 말이 하고 싶어서 지금까지 어플리케이션 도메인이 뭐니 어셈블리 로딩이니 이빨을 깠던 것이다.
Application Base Directory
어셈블리가 GAC을 통해 로드 되지 않은 상황에서 CLR은 어플리케이션 베이스 디렉터리에서 어셈블리를 로드 하려고 시도한다. 대개의 exe 응용 프로그램들의 베이스 디렉터리는 exe 파일이 존재하는 디렉터리로 설정된다. 주의할 점은 작업 디렉터리(working directory)와 베이스 디렉터리(base directory)는 서로 다른 개념이라는 것이다. 작업 디렉터리는 프로세스의 현재 디렉터리를 말하는 것으로, 상대 경로의 기준 위치가 되는 WIN32 의 개념이다. 반면 베이스 디렉터리는 닷넷 어플리케이션 도메인 별로 다르며 어셈블리를 찾는 과정(probing)에서 기준 위치로 사용되는 닷넷의 개념인 것이다. 작업 디렉터리는 WIN32 API 등을 이용하여 변경이 가능하지만 어플리케이션 베이스 디렉터리는 어플리케이션 도메인이 생성될 때 설정되고 변경되지 않는다.
어플리케이션 도메인의 베이스 디렉터리를 알아내는 방법은 AppDomain 클래스의 BaseDirectory 속성을 액세스 하면 된다. 다음 코드는 현재 어플리케이션 도메인의 베이스 디렉터리를 출력하는 코드 조각이다.
Console.WriteLine("AppDomain Base Directory : {0}", AppDomain.CurrentDomain.BaseDirectory);
일반적으로 닷넷 exe 프로그램이 수행되면 베이스 디렉터리는 exe 파일이 존재하는 디렉터리로 결정된다. 이것은 exe에 포함된 작은 스텁(stub) 코드에 의해 수행된다. 반면 ASP.NET 웹 어플리케이션은 ASP.NET 엔진에 의해 웹 어플리케이션의 가상 디렉터리의 물리 경로(파일 시스템 경로)가 베이스 디렉터리로 설정된다. 그렇다면 스마트 클라이언트는 어떠할까? 이제 알아보도록 하자.
Stand-Alone Smart Client : Base Directory & Configuration File
정말 잘 참고 여기까지 읽어 온 독자에게 심심한 치하를 하는 바이다. 독립 스마트 클라이언트가 보다 간단한 구조를 가지므로 먼저 설명하도록 하겠다.
AppDomain Base Directory
앞서 스마트 클라이언트, 그것을 알려주마 (III) : 작동 원리 포스트에서 독립 스마트 클라이언트는 IEExec.exe 에 의해 어플리케이션 도메인이 생성된다고 하였다. 스마트 클라이언트를 위한 어플리케이션 도메인을 생성할 때 IEExec.exe 는 어플리케이션 도메인의 베이스 디렉터리를 해당 EXE 파일을 다운로드 받은 디렉터리를 URL을 베이스 디렉터리로 설정한다. 그렇다. 베이스 디렉터리는 반드시 로컬 파일 시스템의 경로일 필요가 없다. URL도 베이스 디렉터리로서 설정이 가능한 것이다.
그렇다면 우리의 독립 스마트 클라이언트 예제(스마트 클라이언트, 그것을 알려주마 (II) : 맛보기 예제 참조)의 Launcher.exe를 다시 상기해 보자. Launcher.exe를 작성할 때 우리는 ClientModule.DLL을 '참조' 했었다. 소위 말하는 정적 참조(암시적인 참조)를 수행한 것이다. 그렇다면 Laucher.exe 가 다운로드 되고 IEExec.exe 프로세스에 호스팅 되어 수행될 때, 폼이 표시됨과 동시에 CLR은 정적 참조에 의해 ClientModule 어셈블리를 로드 하려고 할 것이다(폼에서 ClientModule 어셈블리의 UIControl 클래스를 참조 했기 때문이다). 물론 ClientModule 어셈블리는 서명하지 않은 어셈블리 이므로(적어도 필자의 기억으로는 그렇다), 어플리케이션 도메인의 베이스 디렉터리에서 ClientModule 어셈블리를 로드 하려고 시도할 것이다. 그리고 베이스 디렉터리는 exe를 다운로드 받은 URL이므로 그곳에 ClientModule 어셈블리가 존재해야만 한다. 물론 우리의 테스트에서 동일 디렉터리에 Launcher.EXE 와 ClientModule.DLL을 모두 복사해 놓았으므로 코드가 작동한 것이다.
보다 구체적인 예로 설명해 보자. 지난 예제에서 Launcher.EXE 를 올려놓은 URL은 http://localhost.com/SmartClientBasic/Launcher.EXE 이였다. 이 URL을 브라우저 주소 창에 입력하거나 링크를 만들어 클릭하면 독립 스마트 클라이언트가 구동될 것이며, 이 스마트 클라이언트 어플리케이션의 베이스 디렉터리는 http://localhost.com/SmartClientBasic/ 로 설정된다. 이제 CLR이 어셈블리를 GAC이 아닌 곳에서 찾게 된다면 기준이 되는 디렉터리는 http://localhost.com/SmartClientBasic/ 가 될 것이다.
마지막으로 한 마디만 더 하자면, 어플리케이션 베이스 디렉터리 설정과 더불어 어셈블리를 찾는 추가 경로로서 bin 디렉터리를 추가한다. 소위 PrivateBinPath 경로로서 추가되는 것인데, 더 이상 길어지는 걸 두고 볼 수 없어서 이만 입을 다물도록 하겠다.
Configuration File
독립 스마트 클라이언트의 configuration 파일은 일반 EXE의 상황과 동일하다. 즉, exe.config 파일을 웹에서 다운로드 한다는 것이다. 다운로드 위치는 EXE 파일을 다운로드 한 URL로 부터 계산된다. Launcher.exe 예제를 다시 이용하여 설명하면, Launcher.exe 스마트 클라이언트 어플리케이션의 configuration 파일은 http://localhost.com/SmartClientBasic/Launcher.exe.config 가 되겠다. 간단하지 않은가?
사실 어플리케이션 도메인을 생성할 때 configuration 파일까지 임의로 설정할 수 있지만, 일반적인 EXE 파일을 수행할 때와 일관성을 유지하기 위해 IEExec.exe 가 .exe.config 를 사용하도록 배려(?) 해주는 것이다(필자의 추측이다... -_-; ).
Browser Embedded Smart Client : Base Directory & Configuration File
독립 스마트 클라이언트는 반드시 EXE를 요구하기 때문에 베이스 디렉터리나 configuration 파일이 사용되는 패턴이 일반 EXE 를 수행하는 것과 동일하기 때문에 상당히 자연스럽지만, 브라우저 임베디드 스마트 클라이언트는 대략 난감하다 아니할 수 없다. 브라우저 임베디드 스마트 클라이언트는 configuration 파일을 HTML 태그를 통해 명시적으로 주도록 되어 있으며, 이 태그에 의해 사용되는 configuration 파일이 존재하는 웹 상의 디렉터리가 어플리케이션 베이스 디렉터리로 사용되게 된다. 조급해 하지 말고 상세히 살펴보도록 하자.
Specifying Configuration File
브라우저 임베디드 스마트 클라이언트 어플리케이션의 configuration 파일을 지정하는 방법은 태그를 사용하면 된다. 스마트 클라이언트를 위한 OBJECT 태그를 포함하는 HTML (.htm이 사용되건 aspx를 쓰건 jsp를 쓰건 결과물은 HTML 이다)에 다음과 같이 configuration 파일을 명시할 수 있다.
<link href="ClientModule.dll.config" rel="Configuration">
태그를 구사할 때는 rel 속성에 반드시 Configuration을 명시하고 파일을 href 속성에 주면 된다(절대 경로건 상대 경로건 상관 없지만 다른 사이트에 대한 URL은 불가하다).
뭐 몰라도 되지만 원리는 이렇다. 지난 스마트 클라이언트, 그것을 알려주마 (III) : 작동 원리 포스트를 다시 떠올려 보자. 브라우저 임베디드 스마트 클라이언트는 IEHost.dll 모듈에 의해 스마트 클라이언트를 위한 어플리케이션 도메인이 만들어진다고 했다. 이 때 IEHost.dll 은 재미있는 행동을 한다. IEHost.dll은 OBJECT 태그가 포함된 HTML 에서 ref 속성이 "Configuration" 인 태그를 찾는다. 만약 이것을 찾으면 IEHost.dll은 어플리케이션 도메인을 생성할 때, 어플리케이션 도메인의 configuration 파일로서 찾은 태그의 href 속성이 표시하는 URL을 사용해 버린다. 재미있지 않은가? 아니면 말고... -_-;
브라우저 임베디드 스마트 클라이언트의 경우 Configuration 파일이 명시되지 않으면 IEHost.dll 은 쌩뚱맞게도 iexplore.exe.config 파일을 configuration 으로 사용하려고 시도 한다. 뭐 IE (iexplore.exe) 가 사용되므로 그렇다고 치부할 수 있지만 iexplore.exe.config 파일을 다운로드 하려고 하는 URL 은 더욱 쌩뚱맞게 사이트의 루트 URL(예: http://www.simpleisbest.net/iexplore.exe.config)을 사용한다는 점이 더욱 요상스럽다. 하지만 이러한 행동은 곧이어 설명할 어플리케이션 도메인의 베이스 디렉터리를 설정하는 IEHost.dll 의 행동을 생각 하면 또 자연스럽기도 하다.
Determining AppDomain Base Directory
IEHost.dll 은 configuration 파일을 설정함과 동시에 생성되는 어플리케이션 도메인의 베이스 디렉터리로서 configuration 파일을 다운로드 받은 웹 상의 디렉터리를 설정하는 만행(?)까지 서슴지 않는다. 독립 스마트 클라이언트와는 전혀 다르게도 브라우저 임베디드 스마트 클라이언트는 태그에 의해 configuration 파일을 설정하고 이 configuration 파일이 존재하는 웹 상의 디렉터리가 베이스 디렉터리로 설정됨을 잘 알아 두자.
스마트 클라이언트, 그것을 알려주마 (II) : 맛보기 예제 에서 ClientModule.DLL은 어떤 어셈블리도 참조하지 않았지만 만약 ClientModule.DLL이 ClientModule2.DLL을 Assembly.Load 메쏘드 호출과 같은 코드로 명시적으로 참조하거나 비주얼 스튜디오의 '참조'를 통해 암시적으로 참조했다면, CLR은 ClientModule2.DLL을 configuration 파일을 다운로드 한 웹 상의 디렉터리에서 찾게 될 것이다.
그렇다면, 지난 맛보기 예제에서 처럼 configuration 파일을 명시하지 않았다면, 즉 태그를 명시하지 않는 경우에는 어떻게 되는가? 이 경우에는 물론 적용되는 configuration 파일은 없다. 그리고 어플리케이션 베이스 디렉터리는 DLL 을 다운로드 한 사이트 루트 경로(URL)로 설정된다. 예를 들어, http://www.simpleisbest.net/Apps/SmartClientBasic/SmartClient.htm 페이지에 스마트 클라이언트를 위한 OBJECT 태그가 존재하고, 이 명시되지 않는다면 어플리케이션의 베이스 디렉터리는 http://www.simpleisbest.net/ 이 된다는 얘기 이다. Configuration 파일이 명시되지 않은 상황에서 ClientModule.dll 이 ClientModule2.dll 을 참조한다면, CLR은 http://www.simpleisbest.net/ 에서 ClientModule2.dll 을 로드하려고 시도할 것이다(물론 ClientModule2.dll이 GAC에 없을 경우이다. 일일이 이런 조건 다 얘기 하자면 졸라 빡시므로 대충 알아 듣기 바란다... -_-). 이러한 행동은 Configuration 파일이 명시되지 않으면 사이트의 루트 경로에서 iexplore.exe.config을 다운로드 하여 configuration 으로 설정하려는 IEHost.dll 의 기본 행동과도 맞아 떨어진다.
마지막으로 독립 스마트 클라이언트와 동일하게 IEHost.dll 은 어플리케이션 도메인을 생성할 때 PrivateBinPath로서 bin 디렉터리를 추가한다. 고로, 어셈블리를 한번에 찾지 못하면 추가적인 웹 서버 액세스를 수행함에 유의 하자(사실은 어셈블리를 한 번에 찾지 못한다면 찾기 위해 더 많은 웹 서버 액세스를 수행한다. 이로 인해 스마트 클라이언트의 구동시간이 느려지기도 한다)
IIS Server Setting For Configuration File
스마트 클라이언트는 웹 서버와 무관하게 작동하는 클라이언트 기술이다. 웹 서버로 IIS를 사용하건 아파치를 사용하건 아무런 관계가 없다. 다만 EXE 혹은 DLL을 웹 서버가 다운로드 해 줄 때 MIME 타입이 application/octet-stream, application/x-msdownload, application/x-complus 중 하나이기만 하면 된다(스마트 클라이언트, 그것을 알려주마 (III) : 작동 원리 참조).
스마트 클라이언트 어플리케이션이 Configuration 파일을 사용하면 IIS를 웹 서버로 사용할 때 약간(?)의 주의를 기울여야만 한다. IIS를 사용하는 웹 서버에 닷넷 프레임워크가 설치되어 있다면 .config 파일의 다운로드는 제한되곤 한다. 그 이유는 ASP.NET 웹 어플리케이션의 설정을 담는 web.config가 클라이언트에게 다운로드 되는 것을 막기 위해 ISAPI Extension 확장자 매핑이 이루어져 있기 때문이다. 이러한 설정은 IIS 관리자 MMC를 통해 손쉽게 확인할 수 있다(화면1 참조).

화면1. .config 파일의 ISAPI 매핑
.config 파일 확장자는 다시 ASP.NET의 HTTP 핸들러에 매핑 되어 있는데, 이 핸들러는 System.Web.HttpForbiddenHandler 로서 다운로드를 금지하는 핸들러이다(HTTP 403 Forbidden 오류를 유발한다). 때문에, configuration 파일을 지정할 때는 약간의 주의를 해야만 한다. 참고로, ASP.NET의 HTTP 핸들러 매핑 설정은 닷넷 프레임워크 1.1의 경우 machine.config 에 기록되어 있으며, 닷넷 프레임워크 2.0의 경우 시스템 수준의 web.config (machine.config 파일 존재하는 디렉터리에 존재한다)에 설정되어 있으므로 한 번 살펴보는 것도 좋을 것이다. 다음은 machine.config 혹은 web.config 파일에 설정된 .config 파일에 대한 HTTP 핸들러 매핑이다.
<configuration>
<system.web>
<httpHandlers>
<!--
다른 부분 생략 -->
<add
verb="GET,HEAD"
path="*.dll.config"
type="System.Web.StaticFileHandler"
/>
<add
verb="GET,HEAD"
path="*.exe.config"
type="System.Web.StaticFileHandler"
/>
<add
verb="*"
path="*.config"
type="System.Web.HttpForbiddenHandler"
/>
<!--
다른 부분 생략 -->
</httpHandlers>
</system.web>
</configuration>
리스트1. .config 파일에 대한 ASP.NET HTTP 핸들러 기본 매핑
독립 스마트 클라이언트를 사용하는 경우, configuration 파일은 .exe.config 이다. 이 경우, 친절한 마이크로 소프트 닷넷 팀이 .exe.config 파일이 클라이언트로 다운로드 될 수 있도록 이 확장자를 System.Web.HttpStaticFileHandler 로 연결해 놓았다. 고로 별다른 문제 없이 configuration 파일이 다운로드 되고 설정 파일의 역할을 수행할 것이다.
브라우저 임베디드 스마트 클라이언트의 경우, configuration 파일을 태그를 통해 임의의 파일로 명시할 수 있기 때문에 문제가 발생할 수 있다. 만약 SmartClient.config 같은 파일 이름을 사용한다면 이 파일은 HttpForbiddenHandler와 매핑되어 HTTP 403 Forbidden 오류가 발생한다. Configuration 이 다운로드 되지 않고 사용되지 않는 것은 둘째치고 스마트 클라이언트의 구동조차 되지 않으므로 주의를 기울여야 한다. 그렇다면 configuration 파일로서 Temp.txt 와 같은 쌩뚱맞은 파일명을 사용해도 될까? 그렇다 된다. 하지만 configuration 로서 확장자가 .config 가 아닌 파일을 사용한다면 대략 혼동될 가능성이 높으므로 가급적 .config로 끝나는 파일을 사용하는 것이 좋다. 좋은 권고 사항은 .dll.config 확장자를 사용하는 것이다. .dll.config 확장자는 .exe.config 확장자와 더불어 StaticFileHandler와 연결되어 있어서 다운로드가 잘 될 것이다(리스트1 참조).
StaticFileHandler를 통해 configuration 파일을 다운로드 하는 것은 그다지 좋지 않다. StaticFileHandler의 기본 행동 방식이 좀 괴짜스럽기 때문이다. 이 녀석은 파일을 다운로드 해주면서 콘텐트의 만료 시간을 24시간으로 설정하는데, 이러한 설정은 브라우저 측에 캐시 된 콘텐트가 24시간 동안 재사용된다는 말이 된다. 즉, 서버에서 콘텐트가 변경되더라도 브라우저는 24시간 동안 이 콘텐트의 다운로드를 요구하지 않는다는 얘기가 되겠다. 해결방법은 독자들이 잘 짱구를 굴려보면 될 것이다. -_-;
What's Next
이번 포스트도 기대를 져버리지 않고 졸라 빡신 포스트 였다. 스마트 클라이언트 시나리오에서 프로세스와 어플리케이션 도메인의 관계 그리고 베이스 디렉터리 설정은 상당히 중요한 개념이다. 지난 스마트 클라이언트, 그것을 알려주마 (III) : 작동 원리 포스트와 더불어 이번 포스트를 시간을 갖고 천천히 정독하여(사실 정독할 만큼 글을 잘 썼다고 생각하진 않는다) 잘 이해하기 바란다.
다음 포스트에서는 스마트 클라이언트를 구동할 때 발생하는 문제들을 해결하는 방법과 도구 그리고 팁들을 살펴보도록 하겠다. 브라우저 임베디드 스마트 클라이언트 시나리오에서 이상하게 엑박만 나타난다거나 뜬금없이 SecurityException 이 발생한다거나 DLL을 찾을 수 없다거나 하는 둥의 문제 상황에서 이들을 해결하기 위한 단서를 제공하는 여러 가지 툴, 설정 등을 살펴보도록 하겠다. 다음 포스트 역시 너무 많이 기대는 하지 않는 것이 좋다. 요즘 필자를 휘감는 엄청난 포스의 귀차니즘으로 인해 언제나 스마트 클라이언트에 대한 다음 글이 올라올지 필자도 모르겠다.