스마트 클라이언트 시리즈 두 번째 글입니다. 필자가 처음 스마트 클라이언트를 접한 것은
MSDN
Magazine의 2002년 6월호에 실린 기사(
Return of the Rich Client: Code Access Security and Distribution Features in .NET Enhance Client-Side Apps)를
읽었을 때 였습니다. 기사를 읽고 예제 코드를 작성해 보니, 웹 UI의 한계를 극복할 수 있는 좋은 방법으로 생각하여,
Web Integrated Terminal 이란 이름으로 개발 프레임워크를 작성하는 회사(그 때 당시 재직 중이던 D 모 SI
회사) 내부 과제를 진행했었죠. 그러던 중 회사를 퇴사하게 되어 유야 무야
다른 일에 얽매어 살다가, 2004년 부터 실질적인 프로젝트로서 다양한 POS(Proof of Concept) 프로젝트나
실제 프로젝트를 하게 되었습니다. 저도 처음에는 스마트 클라이언트에 대해 자세히 모른 채로 달려들었다가 다양한 기술적인
이슈에 부딪히곤 했습니다. 그러나
대부분의 경우는 스마트 클라이언트이기 때문에
발생하는 이슈가 아닌 닷넷 프레임워크의 기본 행동을 제가 몰랐기 때문이었으며, 스마트 클라이언트 자체의 이슈는 많지
않았습니다. 앞으로 이 시리즈를 통해 차근차근 알아보도록 하겠습니다.
시리즈 목차
Smart Client (II) : Trivial Sample
지난
포스트에서는 스마트 클라이언트의 용어적인 정의를 해 보았다. 이제부터 스마트 클라이언트라 함은 닷넷 스마트
클라이언트를 말하는 것이며, "웹으로부터 어셈블리(DLL, EXE)를 다운로드 하여 수행하는 클라이언트"를 말하는 것으로
하겠다. 용어를 혼동하지 않도록 하자.
이번 포스트는 아주 기본적인 스마트 클라이언트를 작성해 보겠다. 스마트 클라이언트에 대해 전혀 백그라운드가 없는
독자도 있을 것이니, 간단한(?) 예제를 작성함으로써, 스마트 클라이언트가
무엇인지 감을 잡도록 하는 것이 이번 포스트의 목적이라 하겠다. 이 포스트는 Step-by-Step
스타일의 글로써(개인적으로는 그다지 좋아하지 않는 스타일이다) 내용이 상당히 길다(졸라 빡셌다... -_-). 내용은
길지만 깊은 부분을 다루지 않기 때문에 어렵지는 않을 것이다. 스마트 클라이언트의 상세한 기술적인 측면, 팁, 주의 사항
등의 상세한 내용은 이어지는 시리즈의 글에서 다루게 될 것이다.
Creating Client Module for Smart Client
스마트 클라이언트는 클라이언트 측에서 수행될 모듈(닷넷 용어로는
어셈블리)을 HTTP를 통해 웹으로부터 다운로드 받는다고 하였다. 그렇다면 클라이언트로 다운로드 될
닷넷 어셈블리를 작성하는 것이 시작점으로 볼 수 있다. 이제 클라이언트 모듈을 작성해 보자. 설명은 Visual
Studio .NET 2003 영문판을 기준으로 설명할 것이다. 아니꼬운 사람은 영문판을 깔던가, 잘 해석해서 한글판에
대응되는 부분을 찾던가 알아서 하길... 또한, Visual Studio 2005를 사용하는 독자라면 알아서 2005로
작성하기 바란다. 물론 여기서 작성한 코드는 모두 2005에서도 작동한다.
- 비주얼 스튜디오를 수행시키고 빈 솔루션(File > New >
Blank Solution 메뉴) 을 만든다. 나중에 테스트 및 Stand-alone 스마트
클라이언트 EXE 프로젝트를 추가할 것이기 때문에 이렇게 하는 것이며, 여러 프로젝트를 포함하는 솔루션을 작성할 때
파일 시스템 디렉터리 구조를 보기 좋게 구성할 수도 있다(필자는 이러한 구조를 즐겨 사용한다). 솔루션 이름은
꼴리는 대로 주면 된다. 필자는 SmartClient Basic 이란 이름을 사용했다.
- 클라이언트 모듈을 위한 Class Library 프로젝트를 추가한다(화면1).
Windows Control Library 프로젝트를 추가해도 되지만, 필자는 Class Library 프로젝트를
더 즐겨 사용한다(이런 개인적인 취향 가지고 태클 걸지 말자. 피곤하다...
사실 두 프로젝트의 차이는 기본으로 프로젝트에 추가되는 참조 어셈블리와 .cs 파일만이 다를 뿐이다). 프로젝트의
이름은 ClientModule로 한다. 이름은 가급적 필자가 지정한 대로 따라 하는 것이 나머지 문서를 읽는데도
도움이 될 것이며, "왜 나만 안되지?" 같은 상황을 피하도록 도와주곤 한다. Class Library 프로젝트를
사용한 독자라면 참조에 System.Windows.Form 과
System.Drawing 어셈블리를 추가하도록 하자. Windows Control
Library 프로젝트를 사용한 독자는 이미 참조가 추가되어 있을 것이다.

화면1. ClientModule 프로젝트 추가
- 프로젝트를 만들었으면, 기본으로 추가되어 있는 Class1.cs 파일을 지워버린다. 우리의 이번 예제에는
전혀 쓸모가 없다. 이제 클라이언트에 UI를 표시할 User
Control을 프로젝트에 추가한다. Project 메뉴를 사용하거나 프로젝트를 오른쪽 클릭하면
나타나는 컨텍스트 메뉴에서 Add User Control을 선택하고 이름을 UIControl로 지정한다.
- UIControl에 2개의 Label 컨트롤과 3개의
Button 컨트롤을 올려놓고 적당히 디자인을 한다. 필자는 두 개의 Label 컨트롤에
lblTitle, lblAssemblyVersion 이란 이름을 주었고, 버튼에는 각각 btnTest1,
btnTest2, btnTest3라고 이름 지었다. 적당히 폰트와 배경색을 지정하면
화면2와 같이 이뿌장한 컨트롤을 만들 수 있다.

화면2. UIControl 추가와 디자인
- 컨트롤의 Load 이벤트를 추가하여 현재 버전을 화면에
표시하도록 코딩을 한다. 현재 ClientModule의 버전을 알아내는 것은 어렵지 않다.
Assembly 클래스의 스태틱 메쏘드인 GetExecutingAssembly 메쏘드를 호출하면
ClientModule 어셈블리를 반환하게 되고, 이 Assembly 인스턴스에 대해 GetName() 메쏘드를
통해 AssemblyName 객체를 알아낸 후, 다시 AssemblyName.Version 속성을 액세스 하면
된다(리스트1). 그리고 테스트 버튼 1에 대해 이벤트 핸들러를 간단히 작성하자.
// 로드 이벤트 핸들러
private
void UIControl_Load(object
sender, System.EventArgs e)
{
// 현재
어셈블리 버전을 표시한다.
System.Reflection.Assembly assembly
= System.Reflection.Assembly.GetExecutingAssembly();
lblAssemblyVersion.Text = "Assembly
Version : " + assembly.GetName().Version.ToString();
}
// 테스트1 버튼
클릭 이벤트 핸들러
private
void btnTest1_Click(object
sender, System.EventArgs e)
{
MessageBox.Show("스마트 클라이언트 작동 테스트",
"테스트", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
리스트1. 로드 이벤트와 테스트 버튼 #1에 대한 이벤트 핸들러 작성
- 마지막으로 AssemblyInfo.cs를 열어 어셈블리의 버전
값을 고정 시키도록 한다. 나중에 버전을 바꾸어가며 테스트해야 할 것이므로 변하는 값을 사용하여
혼동을 일으키지 않도록 하자. 그리고, AssemblyInfo.cs 에서 다른 것은 수정하지 않는다.
특히, 서명을 하지 않았음에 유의하도록 한다.
// 어셈블리 버전
[assembly: AssemblyVersion("1.0.0.0")]
- 이제 솔루션을 빌드하여 컴파일이 제대로 되는지 확인한다. 졸라 잘 따라 했다면 컴파일 오류는 없어야 한다.
- 필자가 작성한 코드 이외의 기능은 가급적 포함시키지 않는 것이 좋다. 많은 기능들이 닷넷 보안 설정(CAS; Code Access
Security)을 위배하여 SecurityException이 발생할 수 있기 때문이다. 닷넷 보안 설정에 대한 상세한 내용은 추후
포스트에서 상세히 다루게 될 것이다.
Creating Smart Client Launcher Application
앞서 작성한 스마트 클라이언트 모듈이 제대로 동작하는 가를 테스트하고, 나중에 Stand-alone 으로 작동하는
스마트 클라이언트 시나리오를 테스트하기 위한 목적으로 윈폼 어플리케이션을 작성하도록 한다. 일단 이 테스트 어플리케이션으로
클라이언트 모듈을 다양한 스마트 클라이언트 시나리오에 의해 테스트 할 것이다. 나중에도 다시 언급하겠지만
스마트 클라이언트를 디버깅하는 것은 상당히 불편하다.
따라서 작성해 놓은 클라이언트 모듈을 테스트할 용도로 간단한 EXE를 작성하여 테스트를 수행하는 것이 좋다. 이렇게
로컬 컴퓨터에서 스마트 클라이언트 시나리오가 아닌 상황에서 테스트를 충분히
수행하고, 일반적으로 모듈이 잘 작동하는 경우에만 클라이언트 모듈을 웹 서버에 올려놓고 스마트 클라이언트 시나리오에 의해
최종 테스트를 수행하는 것이 생산성을 올리는 좋은 방법이므로 기억해 두기 바란다.
편의상 스마트 클라이언트 모듈을 로드 하는 어플리케이션을 런처(launcher)라고 명명할 것이다. 이 런처
프로그램을 솔루션에 추가해 보자.
- 솔루션에 Add > New Project...를 선택하고
Windows Application 프로젝트를 추가한다(프로젝트 이름은 Launcher로
하자). 이제 프로젝트에 추가된 기본 Form1.cs의 이름을 LauncherForm.cs 으로 수정하고, 클래스
이름 역시 LauncherForm 으로 바꾼다. 물론 이렇게 하지 않고 Form1 이란 이름을 써도 전혀 상관
없지만, 필자는 이런 꼴을 못 보는 이상한 결벽증 비스무레한 것이 있어서 그렇다. 이런 결벽증에는 이유가 있다. 몇
달 후에 작성한 예제를 다시 열어 볼 때, 파일 이름과 클래스 이름이 전혀 의미 없는 Form1 따위로 되어 있으면
소스를 까보지 않고는 당췌 이 녀석이 뭐하는 녀석인지 알 수 없는 경우가 종종 있기 때문이다. 그래서 이런 중병에
걸렸다는... 개인적으로 나쁜 병이라고 생각하지 않는데, 독자들은 어떤지 모르겠다...
- 폼에 앞서 작성한 클라이언트 모듈의 UIControl을 추가한다. 먼저 필요한 것은 작성해 놓은
클라이언트 모듈(어셈블리)을 '참조' 해야 한다.
Launcher 프로젝트의 참조 추가에서 ClientModule 프로젝트를 '참조'하도록 한다. 참조 후 툴박스(Toolbox)의
My User Controls 탭을 살펴보면 방금 작성해 놓은 UIControl 이 추가되어 있는 것을 알 수 있을
것이다. 이것을 이용해 폼위에 UIControl 을 올려 놓는다(화면3
참조).

화면3. 폼에 UIControl 추가
- 추가한 컨트롤의 이름을 적절히 주고(필자는 ucClient라는 이름을 사용했다), Dock 속성을 Fill로
설정하여 폼에 가득 차도록 설정한 후, 기타 다른 속성(폼 타이틀, 크기 등등)을 설정한다. 그리고
AssemblyInfo.cs를 열어 어셈블리 버전을 역시 1.0.0.0
으로 고정하는 작업도 수행한다. 버전을 이렇게 고정하는 이유는 이미 언급한 바 있다.
- 이제 컴파일을 수행하고, 컴파일 오류가 없다면 수행하여 작동을 테스트 해 보자. 별로 한 것이 없으므로
수행은 제대로 되어야만 한다.
IE Embedded Smart Client Setup & Test
Laucher가 잘 작동하면 스마트 클라이언트를 위한 코드 작성은 일단락 되었다. 이제 브라우저에 임베딩 되는 스마트
클라이언트 시나리오를 테스트 해보도록 하겠다. 브라우저가 클라이언트 모듈을 다운로드 받을 수 있도록
ClientModule.dll 을 웹 서버에 설정한다. 여기서 설명하는 기준은 단일 컴퓨터에서의 테스트이다. 만약 웹
서버가 별도에 존재한다면 해당 서버에 알맞은 설정을 해주면 된다. 스마트 클라이언트는 순수한 클라이언트 기술이므로 웹
서버는 IIS 가 아니어도 무방하다.
- SmartClientBasic 이란 폴더를 적당한 위치에
생성하고(필자는 솔루션 폴더 바로 하위에 만들었다), 웹 공유를 설정한다. 물론, 이렇게 하지
않더라도 C:\inetpub\wwwroot 디렉터리의 하위에 디렉터리를 작성하거나 직접 IIS 관리자를 통해 가상
디렉터리를 잡아주어도 무방하다.
- SmartClientBasic 폴더에
ClientModule.dll 를 복사한다.
- 이제 적당한 텍스트 에디터를 사용하여 스마트 클라이언트를 위한 웹 페이지(SmartClient.htm)를
작성한다. 이 웹 페이지는 IE에 임베딩 되는 스마트 클라이언트 시나리오를 위해 필요한 것이다.
Stand-alone 스마트 클라이언트의 경우, 이런 웹 페이지는 불필요 하다.
- SmartClient.htm을 리스트2와 같이 작성한다. 중요한 것은
<OBJECT> 태그의 classid 속성에 클라이언트 모듈의 이름(ClientModule.DLL)을
주는 것이다. 그리고 이 이름 뒤에 # 문자로
구분하여 표시할 User Control 클래스의 전체 이름(네임스페이스를 포함하는 이름)을 명시한다.
<html>
<head>
<title>스마트
클라이언트 따라 하기 예제</title>
</head>
<body>
<h1>Browser Embedded Smart Client Example</h1>
<p>다음은
브라우저에 <OBJECT> 태그를 사용하여 닷넷 윈폼 컨트롤을 호스팅 하는 스마트 클라이언트 예제
입니다.
</p>
<object classid="ClientModule.DLL#ClientModule.UIControl" width=100% height=240>
</object>
</body>
</html>
리스트2. 스마트 클라이언트를 호스팅하는 웹 페이지, SmartClient.htm
- 마지막으로,
C:\Windows\System32\Drivers\Etc 디렉터리의 hosts 파일에 다음 항목을 추가한다.
이렇게 하는 이유는, 스마트 클라이언트에 대한 CAS(Code Access Security) 설정을 테스트하기
위함이다. 그냥 localhost를 사용해도 되지만, 이미 스마트 클라이언트 접해본 독자는 localhost 에
대해 CAS 설정을 했을 가능성이 있기 때문이다. CAS에 대해서는 조금 후에 이야기를 하겠다.
# Copyright (c) 1993-1999 Microsoft Corp.
#
...... 중략 ......
127.0.0.1 localhost
127.0.0.1 localhost.com #
스마트 클라이언트 테스트용 호스트 이름
...... 생략 ......
- 이제 브라우저를 수행시키고
http://localhost.com/SmartClientBasic/SmartClient.htm 페이지를 브라우징
해본다. 약간의 지연 후에 화면 4와 같은 결과가 나타날 것이다!

화면4. 브라우저에 임베드 된 스마트 클라이언트
브라우저 임베딩 스마트 클라이언트가 작동하는 라이브 예제를 보고 싶다면, 다음 링크를 사용한다.
라이브 예제 수행
Stand-alone Smart Client Setup & Test
브라우저에 임베딩 되는 시나리오를 해 보았으니 이제 독립적으로 작동하는 스마트 클라이언트를 설정하고 테스트 해보자.
Stand-alone 스마트 클라이언트라 함은 독립적인 수행 파일(exe)이 브라우저와 독립적인 프로세스로 작동하는 것을
말한다. 대개의 경우 독립 스마트 클라이언트는 고유의 폼 윈도우를 갖으며, 브라우저 내부에 컨트롤을 표시하는 것은
불가능하다 (아주, 매우, 많이, 엄청나게 고생을 해서 Active Document 서버 형태로 구현하면 가능은 할 것
같다. 해보지는 않아서 잘 모르겠다... -_-).
독립 스마트 클라이언트는 수행할 exe를 필요로 한다. 우리는 이미 테스트를 위해 Launcher.exe를
작성했으므로 이것을 독립 스마트 클라이언트로서 사용해 보자.
- 앞서 작성했던 SmartClientBasic 가상 디렉터리에
Launcher.exe 파일을 복사한다.
- Launcher.exe 에 대한 링크를 포함하는 웹
페이지(SmartClient2.htm)를 SmartClientBasic 가상 디렉터리에 작성한다.
필요한 것은 URL 링크를 작성하기 위해 <A> 태그를 사용하고 href 속성에 Launcher.exe를 명시하는
것 뿐이다(리스트4 참조)
<html>
<head>
<title>스마트
클라이언트 따라 하기 예제
</title>
</head>
<body>
<h1>Stand-alone Smart Client Example</h1>
<p>다음은
URL 링크를 사용하여 닷넷 윈폼 어플리케이션을 수행하는 스마트 클라이언트 예제 입니다
.</p>
<a href="Launcher.exe">독립
스마트 클라이언트 수행 !
</a>
</object>
</body>
</html>
리스트4. 독립 스마트 클라이언트를 구동하기 위한 웹 페이지,
SmartClient2.htm
- 약간 허무하지만, 이게 설정의 전부이다. 브라우저를 새롭게 수행시키고
http://localhost.com/SmartClientBasic/SmartClient2.htm 페이지를 브라우징
한다. 그리고 표시된 독립 스마트 클라이언트 링크를 클릭해 보자. 독립 스마트 클라이언트가 멋지게 나타날 것이다!
(화면5)

화면5. 독립 스마트 클라이언트 수행 예제
- 인터넷 바로가기를 통해서도 독립 스마트 클라이언트를 구동할 수
있다. 바탕화면(혹은 다른 임의의 디렉터리)에 바로가기(short cut)를 만들고 "바로가기
항목"에 Launcher.exe의 URL 경로를 준다. 그리고 이 바로가기를 더블 클릭해 보자. 결과는 브라우저
링크를 클릭하는 것과 동일하게 스마트 클라이언트가 나타나는 것을 알 수 있을 것이다. 스마트 클라이언트가 나타나기
전에 잠시 브라우저가 나타났다가 사라질 수 있다. 이는 정상적인 행동이며 막을 수 없다. 공연히 버그 운운하거나
브라우저가 깜박이지 않도록 하기 위해 삽질을 하지 않길 바란다. 브라우저가 나타나지 않는 경우도 있는데, 이는
브라우저가 화면에 나타나기도 전에 스마트 클라이언트가 구동된 상황이다. 시스템에 메모리가 적을 수록, CPU가 느릴
수록, 지저분하게 설치된 프로그램이 많을 수록 브라우저가 나타날 가능성이 높다.
화면5를 잘 노려보면 처음 보는
풍선 팁이 나타나고, 심지어는 폼의 아이콘과 타이틀도 변경되었음을 알 수 있다. 이는 닷넷 CAS
에 의해 코드의 일부만이 신뢰되었음(partially trusted)을 알리는 도움말이며, 타이틀의 앞에는 EXE를
다운로드 한 인터넷 영역(zone)이 표시되고, 타이틀의 뒤에는 다운로드 한 사이트가 나타난다. 물론 완전히
신뢰된(full trusted) 어플리케이션에는 이러한 표시가 없다.
또 한가지 생각할 것은, URL 링크에는 Launcher.exe 만이 명기 되었지만, Launcher.exe가
참조하는 ClientModule.dll 역시 다운로드 되었음을 알 수 있다. 클라이언트 모듈이 없다면 UI를 표시하는
컨트롤이 나타날 리 없지 않은가? 스마트 클라이언트는 참조된 어셈블리를
자동으로 다운로드 하는 능력을 갖고 있다. 사실 이러한 능력은 스마트 클라이언트 만의 능력이 아니라
닷넷 프레임워크의 기본 행동이며 스마트 클라이언트도 이 능력이 적용될 뿐이다. 상세한 내용은 스마트 클라이언트와
어플리케이션 도메인의 관계를 설명하는 포스트에서 다시 하기로 하겠다.
마지막으로 독립 스마트 클라이언트에서 주목할 점은 이 폼을 수행하는 프로세스가 무엇인가 하는 것이다. 작업
관리자(task manager)로 수행 중인 프로세스를 살펴보면, 아무리 찾아
봐도 Launcher.exe 프로세스는 보이지 않는다. 대신 IEExec.exe 프로세스를 볼 수 있을
것이다(화면6). 이 프로세스가 독립 스마트 클라이언트를 호스팅 하는
프로세스이며, 이 프로세스에 의해 Laucher.exe가 로드 되고 수행된다. 이 점에 대해서는 스마트 클라이언트의 원리를
설명하는 포스트에서 다시 상세하게 이야기 하도록 하겠다.

화면6. 독립 스마트 클라이언트와 IEExec.exe 프로세스
독립 스마트 클라이언트가 작동하는 라이브 예제를 보고 싶다면, 다음 링크를 사용한다.
라이브 예제 수행
Basic Trouble Shooting
닷넷 프레임워크 2.0이 설치된 시스템의 경우, 독립
스마트 클라이언트가 나타나지 않고, EXE를 다운로드 하려는 대화상자가 나타날 수 있다. 닷넷 프레임워크 2.0에서 보안이
강화 되서 인지, 필자도 아직 정확하지 않다. 이러한 경우, "인터넷 등록
정보"의 보안 탭에 "신뢰할 수 있는 사이트" 혹은 "로컬 인트라넷"의 사이트에 스마트 클라이언트 사이트(이 예제의 경우,
localhost.com)를 추가하면 된다.
닷넷 프레임워크 버전에 관계 없이 독립 스마트 클라이언트나 브라우저 임베드 스마트 클라이언트를 기동하고자 할 때,
DLL 혹은 EXE를 수행하겠냐고 물어보는 다이얼로그가 나타나는 경우가 있다. 이는
Windows 2003의 보안 강화 설정이 적용되어 있거나 아니면
인터넷 보안 설정에서 "안전한 것으로 표시된 ActiveX 컨트롤 스크립트"
와 "ActiveX 컨트롤 및 플러그 인 실행" 이 모두 "사용"으로 설정되어 있지 않기 때문이다.
이외에도 클라이언트 측의 설정에 따라서 다양한 문제가 나타날 수 있다. 필자가 슈퍼, 울트라, 초특급 트러블 슈터가
아닌지라 이들 문제를 모두 알 수는 없다. 다만 필요할 때마다 이러한 클라이언트 측 설정 문제를 언급하기로 하겠다.
Code Access Security Limitation
CAS(Code Access Security)는
코드가 어디서부터 로드 되었는가(로컬 시스템, 인트라넷, 인터넷 등등), 누가 작성했는가(어셈블리 서명) 등등에 의해
권한을 다르게 설정하여 시스템 자원을 보호하는 것을 말한다. 모든 닷넷 프로그램은 CAS의 지배를
받는다. 다만 우리가 잘 인지하지 못하는 것은, 로컬 컴퓨터에서 로드 되는 어셈블리는 모든 권한을 갖고 있기 때문에 아무런
제약이 없어서 이다. 반면 스마트 클라이언트는 어셈블리를 웹에서 다운로드 받기
때문에, CAS에 의해 권한의 제약을 받게 되는 것이다. 스마트 클라이언트와 CAS에 대한 내용을 모두
설명하기에는 그 내용이 방대하므로 다음 포스트에 미루기로 하고, 여기서는 간단한 테스트 몇 개만 해보기로 한다.
먼저 ClientModule 프로젝트의 UIControl 에 만들어 두었던 btnTest2 버튼에 대한 핸들러를
작성한다(리스트4). 코드는 로컬 컴퓨터의 C:\Windows 디렉터리에 존재하는
ODBC.INI 파일의 내용을 표시하는 간단한 코드이다.
// 테스트2 버튼 클릭 이벤트 핸들러
private
void btnTest2_Click(object
sender, System.EventArgs e)
{
System.IO.StreamReader reader =
System.IO.File.OpenText("c:\\windows\\odbc.ini");
string
result = reader.ReadToEnd();
reader.Close();
MessageBox.Show(result, "ODBC.INI 파일
내용",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
리스트4. 파일 액세스를 수행하는 테스트 코드
로컬에서 Launcher.exe를 이용하여 테스트 해 본다. 아마 잘 작동할 것이다. 이제
수정된 ClientModule.dll을 SmartClientBasic 가상 디렉토리에 복사한다. 새로운 버전이 다운로드
되는가를 확인하기 위해 AssemblyInfo.cs 에서 버전 값을 수정하는 것도 좋다. 하지만 의미는 없다. 이에
대해서는 다른 포스트에서 상세히 설명하겠다(이에 대해서 닷넷의 버그이니 뭐니 하는 사람들도 있는데, 이런 사람을 보면
가까운 군부대나 경찰서 혹은 112에 신고하기 바란다).
새로운 ClientModule.dll 을 테스트 할 때 주의할 점이 있다.
브라우저 임베드된 스마트 클라이언트의 경우, 변경된 ClientModule.dll 이 다운로드 되고 브라우저 상에
나타나게 하기 위해서는 반드시 브라우저 프로세스(iexplorer.exe 프로세스)를 반드시 종료해야만 한다.
브라우저 임베딩 스마트 클라이언트는 다운로드 된 어셈블리가 브라우저 프로세스 내에 생성된 어플리케이션 도메인(AppDomain)에
로드 됨을 명심하자. 일단 어플리케이션 도메인에 로드 된 어셈블리는
어플리케이션 도메인이 Unload 되지 않는 한 메모리에서 제거되지 않는다. 따라서 웹 서버에서 아무리
ClientModule.dll을 갱신하더라도 단순히 웹 페이지를 "새로 고침" (F5 키) 하더라도 새로운 버전이
브라우저에 로드 되지 않는다(다운로드는 되지만 새 버전이 브라우저 프로세스에 갱신되지 않는다!). 따라서 브라우저
프로세스를 종료하여 다운로드 한 ClientModule.dll 이 프로세스에서 제거하고, 새로운 프로세스에서 테스트를 수행
해야 한다. 독립 스마트 클라이언트의 경우, 브라우저와 다른 프로세스를 사용하므로 해당 사항이 없다.
독립 스마트 클라이언트 이건 브라우저에 임베딩 된 스마트 클라이언트 이건 테스트 #2 버튼을 누르면 얄짤없이
화면7과 같은 예외가 발생한다. 이 예외 메시지를 잘 뜯어 보면
FileIOPermission 이 없어서 발생한 것으로 보이며, 예외가 발생한 위치는 FileStream 클래스의 생성자(constructor)이다.
뭔 말인고 하면, 닷넷 프레임워크의 클래스들(BCL; Base Class
Library)는 CAS에서 정의하는 다양한 권한 검사를 코드 곳곳에서 수행하고 있다는 것이다.

화면7. CAS에 의한 SecurityException
한가지 다행스러운 것은 CAS가 완전히 모든 것을 막아버리는 것이 아니라는 점이다.
보안 상 인터넷에서 다운로드 받은 닷넷 어셈블리(DLL 혹은 EXE)가 로컬
파일 시스템에 접근하려 한다 든가, 레지스트리에 접근하거나, Unmanaged 코드를 액세스하는 등의 것을 막고, 다른 웹
사이트로 정보를 전송하는 것을 막을 뿐이다. 기본적으로
CAS 설정은 안전한 UI 표시, 네트워크 액세스, 파일 시스템에 대한 액세스를 허용하고 있다. 한가지
재미있는 권한은 원래 사이트로 접속할 수 있는 권한(connect
back to the site of its origin)이 허용되어 있다는 것이다. 아주 이상한
닷넷의 한글 번역에 당황하지 말고(더 당황스런 번역이 아주 많다...), 예를 들어 설명하면 이해가 빠르다. 우리의
예제에서 Launcher.exe와 ClientModule.dll 은 localhost.com 사이트에서 다운로드
되었으므로, localhost.com 사이트에 HTTP 호출을 할 수 있지만 다른 사이트들에 대해서는 권한이 없다. 따라서
이들 사이트에 접속하려 하면 SecurityException을 얻어 맞게 된다.
웹 서비스 역시 HTTP 아닌가? 이는 곧, 어셈블리를 다운로드 받은 사이트에 대해 웹 서비스를 호출할 수 있음을
의미하기도 한다. 정말 될까? 간단하게 테스트 해보자(우씨 점점 길어진다... -_-). 웹 프로젝트를 만들고 자시고 하지
말고, SmartClientBasic 가상 디렉토리에 TestService.asmx 파일만 만들어 넣으면 된다(리스트5).
<%@ WebService Language="C#" Class="TestService" %>
using System;
using
System.Web.Services;
using
System.Data;
using
System.Data.SqlClient;
[WebService(Namespace="http://www.simpleisbest.net/apps/smartclientbasic/testwebservice")]
public
class TestService :
System.Web.Services.WebService
{
[WebMethod]
public
string TestWebMethod()
{
return "웹 서비스를 잘 호출 했습니다.";
}
[WebMethod]
public
DataSet GetDataSet()
{
SqlDataAdapter adapter =
new SqlDataAdapter("SELECT *
FROM Products",
"SERVER=(local);UID=Tester;PWD=test;Database=Northwind");
DataSet ds =
new DataSet();
adapter.Fill(ds);
return ds;
}
}
리스트5. 테스트용 웹 서비스
이제 ClientModule 프로젝트에 웹 참조를 수행하자(필자는 웹 참조의 네임스페이스는
WebService로 주었다). 이제 웹 서비스를 호출하고 그 결과를 표시할 QueryDataForm 폼을 추가하고,
데이터 그리드와 버튼을 적절히 올려 놓는다(화면8 참조). 글이 점점 길어지니
힘들고 귀찮아 진다. 설정은 대충 알아서 해라.

화면8. 웹 참조와 데이터 조회용 폼 작성
폼이 열리면서 웹 서비스를 수행하고 그 결과를 데이터그리드(dgProducts)에 표시하도록 코드를
리스트6과 같이
작성한다. 그리고 UIControl 의 테스트 버튼 #3 이 QueryDataForm을 표시할 수 있도록 코드를 추가하자(ShowDialog
호출하면 되는 코드이므로 상세 코드는 생략한다). 컴파일 후, 갱신된 ClientModule.dll 을 다시 웹 서버에 올리고 스마트 클라이언트를 구동해 본다.
결과는 화면9와 같이 아주 멋지게 데이터가 조회 될 것이다.
// 폼 로드 이벤트
private
void QueryDataForm_Load(object
sender, System.EventArgs e)
{
// 웹
서비스를 호출하고 그 결과를 데이터 그리드에 표시한다.
ClientModule.WebService.TestService
proxy = new
ClientModule.WebService.TestService();
System.Data.DataSet ds =
proxy.GetDataSet();
dgProducts.DataSource =
ds.Tables[0];
}
리스트6. 웹 서비스를 호출하는 코드

화면9. 웹 서비스를 통한 데이터 조회 결과
스마트 클라이언트가 기본 CAS 권한으로 간단하게 데이터를 조회하여 표시하는 것은 문제가 없지만,
조금이나마 복잡한
일들을 하려고 하면 항상 CAS 보안에 걸리기 마련이다. 예를 들어, 우리 나라 사용자들이 그렇게 좋아하는 엑셀 연동은 짤없이 SecurityException을 던져 버릴 것이다. 또한 그리드 역시 닷넷에 기본으로 포함된 그리드가 아닌, 기능
쫌 된다는 것을 쓰려고 해도 역시 SecurityException을 얻어 맞는다. 따라서
스마트 클라이언트를 SI
프로젝트에 적용하려면 클라이언트 PC에 스마트 클라이언트가 CAS 보안 오류 없이 작동할 수 있도록 CAS 설정을 해주어야
한다. 이 방법에 대해서는 다른 포스트에서 설명하기로 하자. 점점 힘이 빠진다... 으윽...
What's Next ?
구체적인 스마트 클라이언트 예제를 다루었으므로, 다음 포스트에서는 스마트
클라이언트의 원리에 대해 살펴보고자 한다. 어떻게 브라우저가 <OBJECT> 태그를 사용하여 마치
ActiveX 처럼 닷넷 User Control을 브라우저 내에 표시할 수 있는 것일까? 어떻게 URL 링크를 통해 독립
스마트 클라이언트 EXE가 수행되는 것일까? 그리고 왜 다운로드 받은 EXE가 아닌 IEExec.exe 프로세스에 의해
호스팅 되는 것일까? 다음 포스트에서는 이러한 질문에 대한 답을 제시할 것이다.
필자에게 이번 포스트의 예제 코드를 요청하지 말라. 이 정도로 자세히 해주었으면 따라 할 수 있어야 한다. 이렇게
설명해 줬는데도 못하겠으면 직업을 바꾸거나 프로그래머로서의 앞길을 심각하게 재고해보기 바란다. 필자가 이렇게 예제 코드에 인색한 이유가 있다.
예제 코드를 다운로드 받아 수행해 보는 것만으로는 그 기술을 자기 것으로
만드는데 큰 도움이 되지 않기 때문이다. 적어도 필자의 생각은 그렇다. 스스로 처음부터 코드를 작성해
보는 것은 특히나 개념을 잡는 부분에서는 매우 중요한 포인트라고 생각한다. 이렇게 필자가 고생을 해서 지식을 알려주니
독자도 예제를 따라 해보는 수고 정도는 해줘야 하는 것 아닌가? 달랑 예제 코드 받아서 돌려보려고 하는 것은 도둑 심보가
아닌가? 필자의 이런 생각에 적절한 반박을 제시한다면 예제 코드를 보내 주겠다. 상단
"Contact" 메뉴는
이럴 때 필자에게 메일 보내라고 만들어 놓은 것이다.
너무 긴 글에 지쳐서 글 내용이 부실하거나 오자가 많을 것 같다. 내용이 이상하거나 오자 등이 발견되면 곧바로
필자에게 꼰질러 주기 바란다.