login register Sysop! about ME  
qrcode
    최초 작성일 :    2004년 04월 15일
  최종 수정일 :    2005년 04월 04일
  작성자 :    kevin (정성태)
  편집자 :    Taeyo (김 태영)
  읽음수 :    20,558

강좌 목록으로 돌아가기

필자의 잡담~

..

이 강좌는 2004년 2월부터 4월까지 월간마소에 연재되었던 스마트 클라이언트에 대한 컬럼이구요. 닷넥엑스퍼트의 기술 컨설턴트이신 정성태님이 제공해주셨습니다. 스마트 클라이언트에 대한 자료를 구하기 힘든 상황에서 이렇게 선뜻 좋은 자료를 공개해주신 성태님께 다시 한번 감사드립니다.

참고로, 성태님의 개인 사이트는 바로 www.sysnet.pe.kr입니다.
강좌보고 감동을 먹어서 한마디 격려를 해 주고 싶거나, 강좌에 대해 질문은 요기로!!!


스마트 클라이언트의 경우 제작하는 방법만큼 배포 또한 더욱 쉽게 되어있다. 이번 회에서는 배포하기 전 어셈블리에 대한 추가적인 작업을 먼저 다룬 후, 정상적인 클라이언트 측 동작을 위한 보안설정과 함께 마지막으로 디버깅 방법에 대해 언급하고 마무리를 하겠다

정성태 kevin@dotnetxpert.com

현재 (주) 닷넷 엑스퍼트 에서 기술컨설턴트로 일하고 있으며, 주로 COM-ActiveX, COM+, .NET Framework 관련한 작업을 하고 있다.


연재 순서
1회 : Smart Client 의 개요
2회 : Internet Explorer 와의 연동
3회 : 배포

연재 가이드
운영체제 : IIS 가 설치된 Windows 운영체제 - 필자의 경우 Windows 2003
개발도구 : Visual Studio .NET 2003, .NET Framework v1.1.4322
기초지식 : COM, C#, ASP.NET 기초
응용분야 : 현재로서는 기업 내부의 인트라넷 환경에서의 ActiveX 컨트롤 대체.
이후, 닷넷 프레임워크가 일반화되면 외부 웹사이트 에서의 ActiveX 컨트롤 대체

[강좌의 소스를 다운로드하고 싶다면 클릭하세요!!]


연재 1회에서도 말씀드렸지만, 스마트 클라이언트의 경우 아직은 "인터넷" 상으로의 배포에는 다소 무리가 있어 보인다. 왜냐하면 모든 클라이언트 컴퓨터에 ".NET Framework" 이 설치되어 있지는 않기 때문이다. 현재 닷넷이 기본 포함된 운영체제는 Windows 2003 하나뿐이며 그 외의 운영체제들은 "Windows Update" 웹 사이트를 통하거나 "MSDN 온라인 다운로드" 웹사이트를 통해서 선택적으로 설치하는 것이 가능한 정도이다. 환경자체로만 본다면 이미 모든 Windows 사용자들이 "닷넷 프레임워크" 를 설치할 수 있지만, 사용자들의 인식이라는 측면에서 본다면 그러한 설치방법은 의미가 없다고 봐야 한다. 일반 사용자들이 Windows Update 사이트를 방문해서 ".NET Framework" 설치를 기대하기 보다는 오히려 자사의 웹사이트에서 "닷넷 재배포 가능모듈" 을 포함한 셋업 파일을 제공해 주는 것이 더욱 현실적인 방안이 될 수 있다. 참고로 기사를 쓰고 있는 시점을 기준으로 버전 1.1 은 MSDN 온라인 다운로드 웹사이트인 경우 http://www.microsoft.com/korea/msdn/msdn-files/027/001/829/msdncompositedoc.asp에서 다운로드 받을 수 있으며 모듈의 크기는 20MB 정도이다. 앞으로 인터넷으로의 배포는 시간이 지나면 해결될 것이고, 지금 적용가능한 분야라면 단연 인트라넷을 들 수 있겠다. 특정 단체의 내부라면 "닷넷 프레임워크" 의 설치를 의무화할 수 있을 텐데 요즘의 사내 네트워크 속도가 대개 100Mbps 인 것을 감안해 본다면 20MB 정도의 런타임 환경을 설치하는 것은 그다지 부담되는 수준은 아니다. 오히려 한번 설치로 인해서 이후에 개발되는 인트라넷 애플리케이션의 생산성이 높아진다면 오히려 설치는 부담이 아닌 투자라고도 볼 수 있을 것이다.

닷넷 프레임워크 환경에 대한 설치는 기본적인 사항이니 더 언급할 것이 없고, 필자 입장에서는 그보다는 개발에 관계된 사항들을 정리하는 것으로 글을 써나가도록 하겠다. 그럼, 이제부터 배포를 위한 현실적인 문제들을 하나씩 해결해 보자. 우선, 여러분은 다음과 같은 구문을 이용해서 HTML 페이지에서 여러분이 만든 컨트롤을 위치시킬 것이다.

<OBJECT id="_Control1" style="WIDTH: 272px; HEIGHT: 193px" classid="SmartClient.dll#SmartClient.TreeControl"
    VIEWASTEXT>
        <PARAM Name="DebugEnable" VALUE="True">
</OBJECT>

직접적인 배포문제는 아니지만, 우선 눈에 띄는 것은 <PARAM/> 태그를 들 수 있다. 필자는 처음에 <PARAM/> 설정을 생각했을 때 우선 IPersistPropertyBag 을 떠올렸다. ActiveX 에서는 IPersistPropertyBag 인터페이스를 COM 개체에 구현해 두면, Internet Explorer 가 해당 인터페이스가 COM 개체에 구현되어 있는지 QueryInterface 를 해보고 구현되어 있다면, IPersistPropertyBag::Load 메서드를 호출해 주기 때문이다. 인터넷 익스플로러가 활성화시키는 스마트 클라이언트의 경우도 그와 크게 다르지 않다. 하지만, 다행히도 C# 컨트롤의 경우에는 그런 복잡한 과정을 IE 용 .NET 런타임 호스트 ( 이하 IEHost.dll ) 가 숨겨주고 있다. 위에 예시한 경우대로라면 "DebugEnable" 이라는 이름과 동일한 "공용속성" 을 정의해 두면 나머지 매핑과정은 자동으로 이루어지게 되어 있다. 참고로, 공용 속성은 반드시 get/set 이 모두 구현되어 있어야 한다. <PARAM/> 의 특성상 set 접근자는 필요없는 데도, 구현을 하지 않게 되면 동작하지 않는다.

bool _debugEnable = false;
public string DebugEnable
{
    get
    {
        return _debugEnable.ToString();
    }
    set
    {
        _debugEnable = Boolean.Parse( value );
    }
}

.NET Windows Forms 로 구현된 컨트롤의 경우에는 내부적으로 숨겨진 체로 IPersistPropertyBag 이 기본 구현되어져 있고, IEHost.dll 은 내부적으로 Reflection 을 이용해서 <PARAM/> 태그의 NAME 속성으로 있는 이름과 컨트롤에 구현된 공용 속성중에서 동일한 이름을 찾아서 값을 설정해 준다.

다시 위의 <OBJECT/> 구문으로 돌아가서, 이번에는 CLASSID 속성을 살펴보자. 1회연재에서 부터 당연하게 써왔던 것이지만, 배포를 알아보는 것이니 만큼 좀더 자세히 알아봐야 할 필요가 있다. Microsoft 에 의해서 정의된 의미로는 다음과 같이 구성이 된다.

컴포넌트 경로#네임스페이스경로.컨트롤 이름

즉, CLASSID="SmartClient.dll#SmartClient.TreeControl" 이라고 되어 있다면,

컴포넌트 경로: SmartClient.dll
네임스페이스 경로 : SmartClient
컨트롤 이름: TreeControl

이 되는 것이다. 1회 연재에서 필자는 무조건 개발된 컨트롤 DLL 의 위치를 컨트롤이 포함될 웹폼 페이지와 동일한 폴더에 있도록 했다. 이제는 좀 바꿔보자. 관리적인 편의를 위해 이제 웹 사이트의 루트 디렉토리에 "netcontrols" 라는 폴더를 만들고 SmartClient.dll 을 복사해 놓자. 이를 반영하기 위해 CLASSID 는 다음과 같이 변경되어져야 한다.

CLASSID="/netcontrols/SmartClient.dll#SmartClient.TreeControl"

그럼, 인터넷 익스플로러는 "GET /netcontrols/SmartClient.dll" 라고 HTTP GET 호출을 하게 된다. 여기서 실제 HTTP 소켓 통신이 발생하는 과정을 살펴볼 필요가 있다. 여러분들도 "네트워크 모니터" 등의 툴을 이용해서 보면 알 수 있겠지만, IEHost.dll 은 다소 이해할 수 없는 오버헤드 를 야기시키고 있다. 다음은 네트워크 모니터를 통해서 순서대로 나열해 본 GET 호출이다.

GET /netcontrols/SmartClient.dll
------- DLL 전송 ----------
GET /ko-KR/mscorlib.resources.dll ( File Not Found )
GET /ko-KR/mscorlib.resources.mscorlib.resources.dll ( File Not Found )
GET /bin/ko-KR/mscorlib.resources.dll ( File Not Found )
GET /bin/ko-KR/mscorlib.resources/mscorlib.resources.dll ( File Not Found )
GET /ko-KR/mscorlib.resources.EXE ( File Not Found )
GET /ko-KR/mscorlib.resources.mscorlib.resources.EXE ( File Not Found )
GET /bin/ko-KR/mscorlib.resources.EXE ( File Not Found )
GET /bin/ko-KR/mscorlib.resources/mscorlib.resources.EXE ( File Not Found )

GET /SmartClient.DLL ( File Not Found )
GET /SmartClient/SmartClient.DLL ( File Not Found )
GET /bin/SmartClient.DLL ( File Not Found )
GET /bin/SmartClient/SmartClient.DLL ( File Not Found )
GET /SmartClient.EXE ( File Not Found )
GET /SmartClient/SmartClient.EXE ( File Not Found )
GET /bin/SmartClient.EXE ( File Not Found )
GET /bin/SmartClient/SmartClient.EXE ( File Not Found )

GET /ko-KR/System.resources.DLL ( File Not Found )
GET /ko-KR/System.resources/System.resources.DLL ( File Not Found )
GET /bin/ko-KR/System.resources.DLL ( File Not Found )
GET /bin/ko-KR/System.resources/System.resources/DLL ( File Not Found )
GET /ko-KR/System.resources.EXE ( File Not Found )
GET /ko-KR/System.resources/System.resources.EXE ( File Not Found )
GET /bin/ko-KR/System.resources.EXE ( File Not Found )
GET /bin/ko-KR/System.resources/System.resources.EXE ( File Not Found )

처음에 한번 /netcontrols/SmartClient.dll 에서 컴포넌트 DLL 이 실제로 전송이 이루어지고 이후 mscorlib.resources.dll/exe, smartclient.dll/exe, system.resources.dll/exe 에 대한 GET 호출이 연이어서 호출이 되어진다. 리소스 DLL 들에 대해서 찾는 과정이 발생하는 것은 다국어 지원 DLL 들의 경로 풀이 과정으로 미뤄볼때 당연한 호출들이다. 그런데 어째서 SmartClient.DLL 이 처음의 호출에서 발견되었는데도 그 이후에 또 다시 찾으려고 GET 호출을 발생시키는 가 하는 점이 의문으로 남는다. 한마디로, 쓸모 없는 네트워크 트래픽을 발생시키는 것일 뿐이다. 그리고 좀더 생각해 본다면 리소스 DLL 들에 대한 GET 호출까지도 스마트 클라이언트의 경우라면 제어를 할 수 있어야 하지 않았을까 라는 아쉬움이 남는다. 테스트 한 결과로는 마지막 "GET" 호출이 끝나야만 스마트 클라이언트 DLL 이 <OBJECT/> 내에서 정상적으로 활성화되는 것을 볼 수 있다. 24번의 HTTP GET 통신은 인터넷 환경에서는 속도 저하라는 결과를 낳을 수 있다. SmartClient.DLL 의 경로풀이는 한가지 더 의문을 남기고 있다. 필자는 처음에 위의 목록을 보고, 만약 CLASSID 에 기본 지정된 폴더에서 SmartClient.dll 이 발견되지 않는 다면, 위의 HTTP GET 리스트에서 보여지는 것처럼 차례대로 해당 DLL 을 발견하기 위한 호출이 일어나는 것이 아니냐고 생각을 했었다. 하지만, 실제 테스트를 해보면, CLASSID 에 명시된 폴더에 해당 DLL 이 없다면 이후의 호출은 아예 발생도 하지 않는다. 아직 초기 기술이라 그런지 이러한 미숙한 문제들이 아직 남아 있다는 것을 알아두자.

이제 <OBJECT/> 에 대한 사항은 끝마쳤고, 서버측 코드로 넘어가 보자. 여러분은 자신이 만든 컨트롤에 대해서 "강력한 이름서명" 을 해서 배포하기를 원할 것이다. 기존 독립 애플리케이션만을 만들어 본 독자라면 우선 AssemblyKey 특성만을 지정하여 SNK 파일과 연결할 텐데, 스마트 클라이언트에서는 그것만으로는 부족하다. 스마트 클라이언트에서는 "강력한 이름서명" 이 된 어셈블리의 경우 다음과 같이 반드시 "AllowPartiallyTrustedCallers" ( 이하, APTC ) 특성을 포함시켜야만 한다.

[assembly: AllowPartiallyTrustedCallers]
[assembly: AssemblyKeyFile("rich.snk")]

우선, 동작결과만을 놓고 본다면 APTC 특성을 제외시키는 경우, 정상적인 <OBJECT/> 활성화가 이루어지지 않는다. 원래, "강력한 이름서명" 이 된 어셈블리의 경우 "부분신뢰를 받고 있는 어셈블리" 에 의한 호출이 원칙적으로 허용이 안된다. 이 규칙은 상식적으로 생각해 보면 쉽게 이해가 된다. "강력한 이름서명" 이 된 어셈블리라면 분명한 자격증명을 가진 것이고 구현된 동작 역시 중요한 역할을 한다고 볼 수 있다. 그러한 어셈블리를 신뢰할 수 없는 또 다른 어셈블리가 자유롭게 인스턴스화해서 호출한다는 것은 분명 문제가 아닐 수 없다. 예를 든다면, 시스템에 중요한 기능을 하는 "DxType" 이라는 어셈블리가 있다고 가정할 때, 어느 사이트에 방문해서 다운로드 받아 실행한 "AttackType" 이라는 어셈블리가 "DxType" 을 생성해서 실행하는 경우일 텐데, 이런 경우는 절대 있어선 안된다. ( 사실, 그 동안은 그것이 자연스러웠다. ) .NET 환경에서는 그러한 접근까지도 막겠다는 것인데 개발자로서는 번거롭겠지만 찬성할 만한 개념이다. 일단, 그러한 APTC 특성으로 인해 주의해야 할 사항이 몇가지 있다. 첫번째로, 독립 어플리케이션에서 제공되는 "강력한 이름서명" 된 어셈블리에 APTC 특성을 설정하는 것은 위험하니 특별한 사유가 아니라면 아예 쓰지 말라는 것이고, 두번째는 그 어셈블리가 GAC 에 등록될지의 여부에 따라 더욱 더 여러분의 코드에 대한 주의를 기울여야 한다는 것이다. 현실적으로 스마트 클라이언트에 APTC 특성을 사용한다고 해도 외부 어셈블리에서 스마트 클라이언트 DLL 에 있는 Type 을 생성하는 것은 거의 불가능하다. 그런데, 독립 애플리케이션에서의 GAC 에 등록되는 어셈블리라면 사정이 달라진다. 그런 경우 외부 프로그램에서 자유롭게 Type 을 생성할 수 있으므로, APTC 특성을 명시했다는 것은 모든 어셈블리로 하여금 접근을 허용하도록 한다고 볼 수 있기 때문이다.

배포로 인해 발생하는 또 하나의 고민은 애플리케이션 설정 파일의 위치 문제이다. 이 위치에 대한 문제해결은 "네트워크 모니터" 등의 툴을 통해서 보면 쉽게 알 수 있다. 우선, TreeControl.cs 파일의 TreeControl_Load 이벤트 핸들러에서 다음과 같은 코드를 넣어보자.

AppSettingsReader reader = new AppSettingsReader();
bool bDebugEnable = (bool)reader.GetValue( "DebugEnable", typeof( bool ) );

이제 컴파일을 하고, DLL 파일을 웹에 배포한 후 웹 폼 페이지를 인터넷 익스플로러를 이용해서 방문하고 그 사이 오고 가는 네트워크 패킷을 "네트워크 모니터" 를 통해서 살펴보자. 그럼, 다음과 같은 유형의 GET 패킷을 볼 수 있다.

GET /IEXPLORE.EXE.config HTTP/1.1
Connection: Keep-Alive
Host: 192.168.100.19

위치를 알았으니, 웹 사이트의 루트 폴더에 IExplore.exe.config 파일을 생성하고 다음과 같이 내용을 넣어두자.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="DebugEnable" value="True" />
    </appSettings>
</configuration>

그럼, AppSettingsReader 클래스로 위의 Config 파일에 담긴 설정을 액세스 할 수가 있다. 참고로 /iexplorer.exe.config 의 이름과 경로에서 알 수 있듯이, 여러개의 스마트 클라이언트 DLL 들이 있다고 해도 각각에 대한 설정파일을 나눌 수 없고 오직 루트 경로에 있는 IEXPLORE.EXE.config 파일을 공유해야 한다는 것이다. 아울러, 여기서도 IEHost.dll 의 이해할 수 없는 현상이 하나 있는데, 네트워크 모니터를 주의깊게 살펴보신 분은 아시겠지만 IExplorer.exe.config 에 대한 GET 호출이 연이어 두번 발생해서 똑같은 내용의 파일이 중복되어 전송되어지는 것을 확인할 수 있다.

일단, 이 정도로 배포에 관한 HTML 과 컨트롤측의 코드에 대해서 알아보는 것은 끝난 것 같다. 이제 그 동안의 1~2회 연재에서 수작업으로 기본설정하고 넘어갔던 스마트 클라이언트의 동작에 필요한 보안 설정에 관해서 살펴보도록 하자. 여러분은 지금까지 만들었던 ActiveX 컨트롤들을 생각해 볼 때, 그와 동일한 기능의 스마트 클라이언트를 만들고자 한다면 대개의 경우 필히 기본 "Internet" 권한 집합에서 제공하는 권한목록 이외의 것들을 필요로 하게 된다는 것을 알게 될 것이다. 물론, 그런 경우에 사용자로 하여금 직접 권한 설정을 하도록 유도하는 것이 현실적으로 어려운 일이라는 것을 이미 알고 있을 것이다. 여기서, 스마트 클라이언트에 대한 한가지 "아이러니컬" 한 상황이 발생하게 된다. 필자는 연재 1회에서 ActiveX 컨트롤은 클라이언트에 대해서 무제한적인 액세스가 가능하다는 문제를 지적했었다. 그래서 가능한 ActiveX 를 자제하고 이후부터는 제한된 보안환경속에서 .NET 스마트 클라이언트를 써야 한다고 이야기했다. 그런데, "보안설정" 에 와서는 어쩔 수 없이 다시 ActiveX 의 힘을 빌려야 하는 상황으로 오게 된다.

생각해 보자. 보안 설정을 추가하기 위해서는 그러한 권한을 가진 어셈블리만이 가능할것이고, 스마트 클라이언트 어셈블리는 기본 "Internet" 권한집합만 가지고 있기 때문에, 스스로 자신에 대한 보안사항을 허용할 수 없으며 또한 보안설정을 전담하는 스마트 클라이언트 조차도 불가능한 시나리오일 뿐이다. 중요한 것은, 어쨌든 최초의 "신뢰받은" 모듈이 "스마트 클라이언트" 를 위한 보안 설정을 반드시 해주어야만 하는데, 지금의 윈도우즈 환경에서 그 "신뢰받는" 모듈에 대한 선택으로 우리가 취할 수 있는 현실적인 방안은 "ActiveX" 뿐이다. 물론, 조금 덜 현실적인 방법도 몇가지가 있다. 예를 들어, 보안설정을 해주는 .NET 독립 실행파일을 만들어서 사용자로 하여금 다운로드 받게 하고 직접 실행해 주는 것도 방법이다. 물론, 그렇게 사용자로 하여금 단계를 더 거치도록 하는 것을 웹 사이트 운영자입장에서는 원하지 않을 것이다. 사실 사용자들이 그런 단계를 싫어하기 때문에 운영자 역시 원하지 않게 된다. 사용자들은 모든 것이 "원클릭" 방식으로 이루어지기를 바라고 가능하면 어떠한 질문도, 어떠한 팝업도 뜨는 것을 원치 않는다. 그렇기 때문에 결국 현실적인 방안으로, 인증서를 포함한 ActiveX 가 실행이 되어서 사용자는 단지 "확인" 버튼만 눌러주면 보안설정이 끝나게 되도록 개발되어야 할 수 밖에 없는 것이다.

그러니, 결국 다시 "ActiveX" 로 돌아온 것이다. 물론, 이전상황과는 많이 다르다. 단지 설정하는 것에만 "ActiveX" 를 사용할 뿐 나머지 모든 컨트롤에 대해서는 .NET 스마트 클라이언트로 제작될 것이므로 생산성 측면에서는 여전히 우위를 차지하게 된다. 그리고, 한가지 다를 수 있는 점이 하나 더 있다. 고급 사용자를 위한 "보안설정" 방법을 설명하는 도움말을 해당 웹사이트에 올려 놓는 다면, 사용자들은 한결 마음 편하게 ActiveX 를 설치할 수 있을 것이다. 즉, 해당 ActiveX 는 그렇게 공개한 "보안설정" 이외의 것은 아무것도 하지 않는 다는 무언의 약속을 사용자들에게 해줄 수 있기 때문이다. 그런 선택이 주어진다면 내려받는 ActiveX 컨트롤에 대해서 사용자들은 이전보다 훨씬 신뢰를 할 수 있을 것이다.

이미 보안에 관계되었던 APTC 특성을 살펴보면서 느끼는 거지만, 보안은 역시 어려운 것으로 다가온다. 사실 들춰보면 그다지 어렵지 않지만 그 동안 배워온 다른 것보다 상대적인 기준으로 어렵기 때문에 다가서는 것이 가장 마지막 단계로 밀려나기 마련이다. 필자도 아직 .NET Framework 에서 제공되는 Code Access Security 에 대한 깊은 지식이 없으므로 스마트 클라이언트 분야에 해당하는 보안설정 부분을 위주로 설명해 나가겠다. 그 이외의 좀더 자세한 .NET 보안에 대한 자료는 "참고 URL" 에 있는 보안 관련 자료를 읽어주기 바란다. 여기서는, 연재 1회에서 다룬 수작업 보안설정을 프로그램으로 자동화해주는 애플리케이션을 제작해 볼 것이다. 그리고, 그 방법에는 2가지가 있는데, 첫번째는 Caspol.exe 를 실행시켜 설정하는 방법이 있고, 두번째는 .NET BCL 에서 제공되는 System.Security 를 이용한 프로그래밍으로 직접 제어하는 방법이 있다.

2가지 방법을 소개하기에 앞서 기본적인 사항부터 짚고 넘어가자. 먼저, 우리가 지난 1회에서 설정했던 "SmartClientSet" 권한집합과 "InternetSmartClient_Zone" 코드그룹에 대한 변경이 어디에 저장되는지 아래의 표를 참조하자.

Enterprise%SystemRoot%\Microsoft.NET\Framework\v1.0.3512\config\enterprisesec.config
Machine%SystemRoot%\Microsoft.NET\Framework\v1.0.3512\config\security.config
User%APPDATA%\Microsoft\CLR Security Config\v1.0.3512\security.config
* SystemRoot : C:\Windows와 같이 OS 가 설치된 폴더
* APPDATA : C:\Documents and Settings\Administrator\Application Data와 같이 현재 로그인한 계정의 응용 프로그램 데이터를 저장하는 폴더.

연재 1회에서 살펴본 것처럼, "Microsoft .NET Framework 1.1 구성" MMC 콘솔에 보면, "내컴퓨터 / 런타임 보안 정책" 하위에 3가지 보안 정책유형이 있는 것을 볼 수있다. 그 3가지 보안정책 내에서 변경된 내용들은 위의 표에 소개된 CONFIG 파일에 저장되어진다. 즉, 우리가 만들었던 "SmartClientSet" 과 "InternetSmartClient_Zone" 에 대한 설정은 "Security.config" 파일에서 찾아 볼 수 있다. 왜 이것이 필요하게 되는 지는 잠시 후에 살펴보겠다.

1. 보안설정 첫번째 : Caspol.exe 를 이용한 방법을 살펴보자. 현재로서는 ActiveX 로 보안설정을 할 수 있는 유일한 방법이다.

Caspol.exe 는 단순히 System.Security 에 있는 클래스를 이용한 하나의 EXE 실행파일에 지나지 않는다. 따라서, 깊게 살펴볼 사항은 없지만 필요한 사용법과 이 프로그램을 이용해서 어떻게 보안 설정을 하는지에 대한 설명을 하겠다. 우선, 우리의 목적대로, "SmartClientSet" 권한집합과 "InternetSmartClient_Zone" 코드 그룹을 차례대로 다뤄보자. 권한집합을 추가하기 위한 Caspol.exe 의 사용법은 다음과 같다.

caspol { -enterprise|-machine|-user } -ap <xml_file>
caspol { -enterprise|-machine|-user } -addpset <xml_file>

SmartClientSet 의 경우 "컴퓨터" 하위의 권한집합에 추가될 것이므로, 첫번째 인자는 "-machine" 임을 알 수 있다. 문제는 <xml_file> 인데, 해당 권한집합을 표현하는 XML 구성을 수작업으로 하는 것은 너무 번거롭다. 그렇기 때문에 위에 소개한 .config 파일들을 잘 이용하면 이런 경우 손쉽게 처리할 수 있다. "Microsoft .NET Framework 1.1 구성" MMC 콘솔에서 새로운 권한집합을 생성하고 그것에 원하는 권한들을 추가한 다음 저장을 하게 되면 해당 권한내용을 담은 XML 노드가 지정된 정책설정 파일에 담겨지게 된다. 연재 1회에서 소개했던 "SmartClientSet" 권한 집합의 경우에는 "security.config" 파일에 있으니, 메모장으로 열어보면 다음과 같은 XML 노드를 발견할 수 있다.

<PermissionSet class="NamedPermissionSet" version="1"
        Name="SmartClientSet" Description="Default rights given to internet applications">
    <IPermission class="FileDialogPermission" version="1" Access="Open"/>
    <IPermission class="IsolatedStorageFilePermission" version="1"
        Allowed="DomainIsolationByUser" UserQuota="10240"/>
    <IPermission class="SecurityPermission" version="1" Flags="UnmanagedCode, Execution"/>
    <IPermission class="UIPermission" version="1" Window="SafeTopLevelWindows"
        Clipboard="OwnClipboard"/>
    <IPermission class="PrintingPermission" version="1" Level="SafePrinting"/>
</PermissionSet>

아쉽게도 위의 내용만으로는 부족하다. 권한을 나타내는 IPermission 에서 class 에 대해 완전한 어셈블리 명을 지정해 줘야 한다. 예를 들어서, "FileDialogPermission" 에 대해서는 "System.Security.Permissions.FileDialogPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 라고 설정해 주어야 한다. 물론 이 부분에 대해서도 "security.config" 파일에 보면 "SecurityClass" 노드셋안에서 각각의 권한에 대한 어셈블리 명을 구할 수 있다. 일일이 모두 찾아서 치환하는 것이 귀찮을 것 같은데, 그런 분들을 위해서 좀더 편한 방법을 마저 설명해 보겠다. "Caspol.exe" 의 경우 정책 수준을 지정하면 모든 코드 그룹을 표시해 주는 기능도 가지고 있는 데 그것을 이용하면 좀더 쉽게 "SmartClientSet" 권한 집합에 대한 완전한 XML 을 추출할 수가 있다. 다음과 같이 명령행에서 실행해서 권한집합 내역이 담긴 텍스트 파일을 생성하자

caspol -machine -listpset >> machinesecurity.txt

생성된 MacineSecurity.txt 파일에서 "SmartClientSet" 만을 추출해서 XML 파일 ( 예를 들어서, SmartClientSet.xml ) 로 별도로 저장해 두면 이후 Caspol.exe 에 의해서 사용될 수 있는 완전한 형식의 권한집합 XML 로 재사용될 수 있다. 이제, 다른 컴퓨터에서 해당 권한 집합을 생성해 보자.

caspol -machine -addpset SmartClientSet.xml

위의 명령을 실행 후 적용된 것을 확인하려면, 반드시 "Microsoft .NET Framework 1.1 구성" MMC 콘솔을 재시작 해야 된다. 기존 실행되어 있는 MMC 콘솔에서는 "새로 고침" 을 해도 나타나지 않는다.

권한 집합에 대한 추가방법은 그걸로 끝이고, 이젠 그 권한집합을 갖는 "InternetSmartClient_Zone" 코드그룹 을 추가해 보자. 권한집합의 경우에는 하위 권한에 대한 설정을 추가해야 하는 것으로 인해 다소 복잡한 반면, 코드그룹의 경우에는 보다 더 간단하게 설정된다.

caspol -machine -addgroup Internet_Zone -url http://192.168.100.19/webapp/* SmartClientSet -n InternetSmartClient_Zone

위와 같은 명령을 내리게 되면, Internet_Zone 하위에 URL 멤버 조건으로 선택된 "InternetSmartClient_Zone" 코드 그룹을 생성하게 됩니다. 실제적인 URL 경로는 여러분의 환경에 맞게 설정한다.

정리해 보면,Caspol.exe 를 이용해서 보안설정을 자동화하는 프로그램을 개발하는 단계는 다음과 같다.

1.ActiveX 프로젝트 생성.
2.Resource 파일로 caspol.exe 의 -listpset 옵션으로 구한 SmartClientSet.xml 파일을 담아둔다.
3.ActiveX 의 초기화 함수나 별도의 메서드를 정의하여 caspol -addposet 과 caspol -addgroup 을 실행하는 CreateProcess / ShellExecute Win32 API 를 실행

위의 내용으로 구현한 ActiveX DLL 의 소스파일은 [소스 다운로드]에 "AxForSmartClient" 프로젝트로 첨부해 놓았으니 참고하자.

2. 보안 설정 두번째 : System.Security 를 이용한 보안설정 프로그래밍. ActiveX 에서는 이용할 수 없고 .NET 개발언어로 만든 경우에만 이용가능하다.

Caspol.exe 에 대한 사용법을 아는 것도 유용하지만, 그 내부에서 어떻게 돌아가는지를 아는 것도 그에 못지 않게 중요하다. 프로그래밍으로 구현하는 순서도 caspol.exe 로 할때와 동일하다. 우선 새로운 권한집합 "SmartClientSet" 을 생성한다.

NamedPermissionSet permSet = new NamedPermissionSet( "SmartClientSet", PermissionState.None );

위에서처럼 권한 집합 생성은 단순히 클래스 인스턴스 하나를 정의하는 것으로 끝난다. 참고로 두번째 인자에 대해서 좀더 알아본다면, NamedPermissionSet 클래스의 생성자에서 PermissionState 인자를 지정하지 않으면 기본적으로 "무제한" 리소스 접근인 "Full Trust" 와 동일한 권한집합이 된다. 기본적으로 권한 허용은 PermissionState.None 으로 시작해서 원하는 권한을 추가하는 방식으로만 가능하며 그 반대로 PermissionState.Unrestricted 로 시작해서 권한을 제거하는 것은 허용되지 않는다. 이제 우리가 원하는 각각의 권한이 어떻게 클래스로 구현되는지 알아볼 차례이다.

<IPermission class="FileDialogPermission" version="1" Access="Open"/>
FileDialogPermission fileDialogPerm = new FileDialogPermission( FileDialogPermissionAccess.Open );
<IPermission class="IsolatedStorageFilePermission" version="1"Allowed="DomainIsolationByUser" UserQuota="10240"/>
IsolatedStorageFilePermission isolatedStorageFilePerm = new IsolatedStorageFilePermission( PermissionState.None );
isolatedStorageFilePerm.UsageAllowed = IsolatedStorageContainment.DomainIsolationByUser;
isolatedStorageFilePerm.UserQuota = 10240;
<IPermission class="SecurityPermission" version="1" Flags="UnmanagedCode, Execution"/>
SecurityPermission securityPerm = new SecurityPermission( SecurityPermissionFlag.UnmanagedCode | SecurityPermissionFlag.Execution );
<IPermission class="UIPermission" version="1" Window="SafeTopLevelWindows" Clipboard="OwnClipboard"/>
UIPermission uiPerm = new UIPermission( UIPermissionWindow.SafeTopLevelWindows, UIPermissionClipboard.OwnClipboard );
<IPermission class="PrintingPermission" version="1" Level="SafePrinting"/>
PrintingPermission printingPerm = new PrintingPermission(PrintingPermissionLevel.SafePrinting);

Security.Config 파일에 설정된 <IPermission/> 노드 하나당 동일한 이름의 권한 클래스를 만들어서 정의한후, <IPermission/> 속성에 지정된 값들을 권한 클래스의 속성에 그대로 지정하면 된다. 위와 같이 권한 클래스들을 생성했다면 권한집합에 NamedPermissionSet.AddPermission 을 이용해서 추가할 수 있다. 이렇게 구성된 권한집합 인스턴스를 "Machine" 정책에 추가한 후 수정사항을 반영하도록 하면 된다. 해당 Machine 정책에 대한 클래스화된 인스턴스를 찾기 위해서는 System.Security.SecurityManager 클래스의 static 으로 정의된 PolicyHierarchy 메서드를 사용하여 다음과 같이 구현될 수 있다.

PolicyLevel machinePolicyLevel = null;
System.Collections.IEnumerator ph = SecurityManager.PolicyHierarchy();

// 아래의 코드를 통해서 "Enterprise", "Machine", "User" 정책을 열람
while( ph.MoveNext() ) {
    PolicyLevel pl = (PolicyLevel)ph.Current;
    if( pl.Label == "Machine" ) {
        machinePolicyLevel = pl;
        break;
    }
}

NamedPermissionSet permSet = new NamedPermissionSet( "SmartClientSet", PermissionState.None );
///////// permSet 구성 부분 생략 /////////////
machinePolicyLevel.AddNamedPermissionSet( permSet );

// 변경된 사항을 security.config 파일에 저장
SecurityManager.SavePolicy();

이와 같이 새로운 권한 집합을 생성하게 되는데, 사실 단순 작업의 반복일 뿐 그다지 어려운 사항은 없다. 다음으로, 위에서 생성한 "SmartClientSet" 권한집합을 갖는 "InternetSmartClient_Zone" 코드 그룹을 생성해 보겠다. 코드 그룹의 경우 동일한 이름으로 CodeGroup 이라는 클래스가 존재하지만 추상 클래스인 관계로 직접 생성할 수 는 없고 파생클래스인 UnionCodeGroup 을 이용해서 인스턴스화 해야 한다. UnionCodeGroup 의 생성자에서 해당 코드 그룹에 적용될 "멤버조건" 과 "권한집합"을 지정하게 된다. 권한집합의 경우 앞에서 생성한 permSet 인스턴스를 넣어주면 되지만, 멤버조건의 경우는 우리가 원하는 상황을 반영하는 클래스를 만들어야 한다. 현재 구현된 멤버조건은 총 8 개 가 있다.

클래스관련 멤버 조건
AllMembershipCondition모든 어셈블리
ApplicationDirectoryMembershipCondition어셈블리가 설치된 디렉토리
HashMembershipCondition어셈블리의 해시 값
PublisherMembershipCondition어셈블리의 소프트웨어 게시자의 Authenticode X.509v3 인증서
SiteMembershipCondition어셈블리의 원본 사이트
StrongNameMembershipCondition어셈블리의 강력한 이름
UrlMembershipCondition어셈블리의 URL
ZoneMembershipCondition어셈블리의 원본 영역

스마트 클라이언트의 경우 웹에서 배포가 되므로 "UrlMembershipCondition" 을 선택해서 다음과 같이 인스턴스를 생성해야 한다.

IMembershipCondition memberShipCondition = new UrlMembershipCondition( "http://127.0.0.1/webapp/*" );

물론, 위에 지정된 URL 은 각자 자신의 상황에 맞게 수정을 해야 한다. 이렇게 코드 그룹에 대한 멤버 조건이 결정되었으면 UnionCodeGroup 을 생성할 수 있다.

// 이전에 생성해 두었던 SmartClientSet 권한집합을 갖는 PolicyStatement 클래스 생성
PolicyStatement policyStatement = new PolicyStatement( permSet );

// 권한집합과 멤버조건을 갖춘 코드 그룹을 생성
CodeGroup smartClientGroup = new UnionCodeGroup( memberShipCondition, policyStatement );
smartClientGroup.Name = "InternetSmartClient_Zone";

// Machine 정책의 코드 그룹에 추가
level.RootCodeGroup.AddChild( smartClientGroup );

생성자에서 멤버조건과 권한집합을 지정하고, 이후 Name 속성에 생성될 코드 그룹에 대한 이름을 지정하면 된다. 그런 후, Code Group 을 이미 정책에 포함되어 있는 기존 코드그룹의 자식에 추가하고 SecurityManager.SavePolicy() 메서드를 호출하면 모든 설정이 끝난다. 완전한 실행 코드는 [소스 다운로드]에 "SmartClientSecuritySetup" 이라는 C# 콘솔 프로젝트를 추가했으니 참고하자.

비록 개발자인 여러분들은 프로그래밍을 통한 보안설정을 아는 것도 중요하지만, 현실적인 이유로 인해 필자의 경우에는 "Caspol.exe" 를 통한 보안 설정을 추천한다. C/C++ 로 만든 ActiveX 에서도 사용할 수 있다는 장점도 있지만, 무엇보다도 복잡한 권한 설정을 쉽게 마무리 할 수 있기 때문이다. 만약 하나의 권한 집합에 권한이 30 개라면 그 권한 하나하나에 해당하는 클래스를 일일이 프로그래밍으로 구현해야 할 텐데, 여간 귀찮은 일이 아닐 수 없다. 대신에 그러한 권한설정을 MMC 에서 제공하는 손쉬운 UI 로 처리하고 Caspol.exe -listgroups 옵션으로 해당 권한집합을 XML 파일로 별도 저장한 후 Caspol.exe -addpset 으로 추가하는 것이 시간적인 면으로도 유리할 수 있다.

그런데, 이쯤에서 하나 더 짚고 넘어가야 되겠다. 바로 속도 문제이다. 앞에서 "네트워크 모니터" 를 통한 패킷이 오고 가는 것을 살펴보았는데, 보셨던 것처럼 아무 의미없는 "GET" 호출의 결과로 스마트 클라이언트의 활성화는 더욱 늦어지게 된다. 비록 완전한 해결책을 제시할 수는 없지만, <OBJECT/> 태그의 classid 속성에 대한 또다른 사용법을 같이 설명하면서 특정한 상황의 컨트롤인 경우에 그러한 연속적인 GET 명령을 배제할 수 있는 방법을 소개하겠다. 원래의 ActiveX 컨트롤의 경우와 비교해서 Smart Client 는 사실 classid 속성에 대해서 2가지 방식으로 사용할 수 있다.

ActiveX 컨트롤Classid="CLSID: 108C3529-A6FF-42b4-912A-4C4754DC0273"
Smart ClientClassid="SmartClient.dll#SmartClient.TreeControl"
Classid="CLSID: 108C3529-A6FF-42b4-912A-4C4754DC0273"

물론, <OBJECT/> 의 Classid 속성에 CLSID 를 지정하기 위해서는 몇 가지 선행과정이 필요하다. 우선, 해당 사용자 컨트롤 클래스에 GuidAttribute 특성을 사용해서 CLSID 를 지정해야 한다.

[ClassInterface(ClassInterfaceType.None), ComSourceInterfaces( typeof( ITreeEvent ) )]
[Guid("108C3529-A6FF-42b4-912A-4C4754DC0272")]
public class TreeControl : System.Windows.Forms.UserControl, ......

그리고, 기존 스마트 클라이언트의 배포방식과는 달리 해당 DLL 이 클라이언트 측에 미리 다운로드 되어서 아래와 같은 등록을 거쳐야 한다.

gacutil /i SmartClient.dll
regasm SmartClient.dll

위의 2 단계를 거쳐서 각각 Global Assembly Cache 와 레지스트리에 COM 개체로 등록을 시켜야 한다. 하지만 문제가 하나 있다. 아무래도 IEHost.dll 은 Smart Client 에 대한 클라이언트 측 활성화를 제대로 지원못하는 듯 한데, 컨트롤 자체는 <OBJECT/> 안에서 정상적으로 활성화가 되는데 HTML 스크립트상에서 해당 컨트롤에 대한 메서드, 속성 호출이 막혀버린다. 그러니까, 특정한 상황 즉, HTML 스크립트와 상호작용하지 않는 컨트롤이라면 위와 같은 방법으로 활성화를 하게 되면 HTTP GET 호출에 대한 오버헤드를 줄일 수 있다. 게다가 클라이언트 측 활성화이므로 보안에 대한 한계가 없어지므로 특별한 보안 설정이 필요가 없어진다. 결국, 일반 ActiveX 와 동일한 권한을 갖는다고 보면 된다. 물론, 그러한 경우가 그다지 많지는 않겠으나 그러한 가능성이 있다는 것을 아는 것만으로도 나중에 도움이 될 때가 있을 것이다.

여기까지 스마트 클라이언트에 관해서는 얘기할 것을 모두 한 것 같다. 이제 마지막으로 디버깅하는 방법에 대해서만 언급하고 이글을 마칠까 한다. 기존 통합환경에 익숙한 개발자들의 경우 무조건 "F5" 단축키를 눌러서 실행하면 디버그 모드로 진행할 것이라고 생각할 텐데, 스마트 클라이언트의 경우에는 그것이 적용되지 않는다. 실행과정을 생각해보면 그 이유를 알 수 있을 텐데 우선, IE 의 주소창에서 http://127.0.0.1/webapp/webform1.aspx 라고 입력해서 웹폼을 방문하게 되면 <OBJECT/> 로 포함되어 있던 SmartClient.DLL 은 클라이언트 측으로 다운로드 되어져서 캐쉬폴더에 담기게 된다. 캐쉬폴더의 위치는 탐색기를 통해서 확인하는 경우 GAC 폴더 하위에 "Download" 라는 폴더로 되어 있다. 통합환경에 로딩되어서 디버깅하려고 하는 대상은 WebApp 웹 가상 디렉토리에 담긴 SmartClient.DLL 인데 정작 Internet Explorer 에 로딩되는 것은 Cache 폴더에 있는 SmartClient.DLL 이니 디버깅이 안되는 것이다. 이런 상황에서는 디버깅을 "F5" 로는 불가능하고, VS.NET 통합환경의 "도구메뉴" 에 있는 "프로세스 디버그" 기능을 이용해야 한다.

    1. Internet Explorer 를 실행한다.
    2. VS.NET 통합환경에서 "도구" / "프로세스 디버그" 메뉴를 선택.
    3. 아래 화면에서와 같이 IEXPLORE.EXE 를 선택한 후, 우측의 "연결" 버튼 클릭

    4. 아래와 같은 화면이 나오게 되는데 반드시 "Common Language Runtime" 과 "Native" 를 포함시켜야 한다. CLR 코드만 디버깅할 뿐이라고 해서 Native 를 체크하지 않으면 IE 내부에서 활성화된 스마트 클라이언트의 디버깅이 불가능하다

    5. 그렇게 되면 VS.NET 통합환경은 "F5" 로 시작한 디버깅상황과 동일한 환경으로 전환이 되고 중단점이 위치한 곳마다 정상적으로 멈추게 된다.

그리고, 필자의 경험상으로 얻은 팁을 하나 더 말하자면 해당 컨트롤이 클라이언트 측 GAC\Download 폴더에 캐쉬된다고 언급했었는데, 가끔 Internet Explorer 가 서버에 있는 최신 DLL 을 로드하지 않고 캐쉬폴더에 있는 DLL 을 로드하는 상황이 발생하게 된다. 캐쉬폴더가 GAC 하위에 있는 것으로 짐작할 수 있겠지만 스마트 클라이언트 캐쉬 폴더 역시 Gacutil.exe 에 의해서 관리되어진다. 앞서 말씀드렸던 DLL 재사용으로 인한 문제가 발생하는 경우에는 명령행에서 "gacutil /cdl" 하게 되면 Download 폴더에 있는 모든 캐쉬된 DLL 들을 비워버리게 된다. 참고로, 명령행에서 Download 폴더경로를 찾아가려면 C:\Windows\assembly\temp 폴더이지만, 캐쉬된 DLL 은 "/ldl" 옵션을 주면 gacutil.exe 가 보다 더 편리하게 보여준다.

연재 3회를 마치며

여기까지, 스마트 클라이언트를 Internet Explorer 에서 사용하기 위한 내용을 다뤄봤다. 필자로서는 기존에 출판된 책들에서 흔히 볼 수 있는 내용을 종합해서 다루기 보다는 어디서도 쉽게 찾아 볼 수 없는 것을 위주로 글을 써나갔기 때문에 오히려 스마트 클라이언트와 관계되어 잘 알려진 사항들은 거의 다루지를 않았다. 오히려 그와 관계된 일반적인 것들에 대해서는 다른 책을 한번쯤 읽어보는 것도 좋을 것 같다. 필자 나름대로는 스마트 클라이언트의 활성화는 시간적인 문제만 있을 뿐 그 활용성은 무궁무진하다고 볼 수 있다. 또한 개인적인 바램이라면, 스마트 클라이언트와 기존 Legacy COM 구현관계에 대한 좀더 많은 기술 공개와 협력이 있었으면 하는 것이다. 그런 정보가 많이 공개될수록 기존의 많은 ActiveX 개체들이 보다 더 쉽고 빠르게 스마트 클라이언트로 변경될 수 있기 때문이다.


참고 URL

.NET Zero Deployment : Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients
http://msdn.microsoft.com/msdnmag/issues/02/07/NetSmartClients/

.NET CAS 보안 : Code Access Security in Practice
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/thcmch08.asp

Security in .NET : The Security Infrastructure of the CLR Provides Evidence, Policy, Permissions, and Enforcement Services
http://msdn.microsoft.com/msdnmag/issues/02/09/SecurityinNET/default.aspx

VS.NET 2003 도움말 : Increasing Permissions for Web-Deployed Windows Forms Applications
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1042/dnforms/html/winforms11122002.htm


authored by


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})