지난 회 차에 여러 가지 문제로 .NET 스마트클라이언트가 가진 문제점을 살펴 보았습니다. 그 중, 주된 이슈는 이미 로드된 어셈블리는 업데이트/갱신이 불가능하다는 것과, 메모리의 사용률이 지속적으로 증가한다는 문제입니다. 이러한 문제는 사내 정책적인 서버를 도입하여 해결 가능하지만, 대부분의 조직과 기업은 이러한 정책 서버를 도입하지 않는 것으로 알고 있습니다.    

이미 얘기 했다시피, 평소에도 .NET 에서 이러한 문제를 가지고 고민을 했었지만, 최근 이러한 문제가 이슈가 되었을 때 더 이상 필자 또한 방관할 수 없었습니다. 왜냐하면 "안된다!" 라는 것 자체가 .NET 의 많은 매리트를 배제한다는 의미가 될 수 있기 때문입니다. 이러한 문제로 '목숨거는' 고객이라면 차라리 '지금은 곤란하다. 조금만 기다려달라' 라는 답이 훨씬 나아 보입니다. 물론 이런 문제가 가능하다는 전제 조건으로 말입니다.

   

문제 해결 방안

일단, 몇 날 몇 일을 고민하며 생각한 끝에 아래와 같은 아키텍처링을 하게 되었습니다. 물론 최선의 방법도, 최적의 방법도 아니지만, 문제가 된다면 저에게 피드백을 주시기 바랍니다. 저 또한 짧은 지식으로 이러한 고민을 하게 되었으니 저도 많이 답답하네요^^;    

   

위의 아키텍처링은 논리적인 아키텍처입니다. 이 방법을 통해 이전 아티클을 통해 골치 아픈 .NET 어플리케이션의 메모리 릭(Memory Leak) 을 해결할 수 있을 것으로 기대합니다.

   

어플리케이션과 AppDomain 의 분리

.NET 어플리케이션은 기본적으로 하나의 프로세스(Process) 를 차지하게 됩니다. 그것이 독립 프로세스든, IEHost.DLL 또는 IEExec.EXE 든 간에 말이죠. 이 독립 프로세스는 독립적인 하나의 어셈블리에서 관장하게 됩니다. 기본적인 이 부분의 컨셉은 어플리케이션의 재시작을 방지하기 위한 방법이기도 합니다.    

기존의 어플리케이션의 프로세스와 AppDomain 을 분리함으로써 최소한으로 AppDomain 이 안전하게 언로드될 수 있는 환경을 제공하는 것입니다. 그리고 위해 AppDomain Manager 는 이것을 관장하는 최상위 Manager Layer 가 됩니다.

   

MVVM 으로 구현부와의 분리

MVVM(Model View ViewModel) 패턴의 가장 큰 특징은 View 와 ViewModel 을 분리한 것입니다. 이것을 분리함으로써 View 와 ViewModel 의 종속 관계를 완전히 해결하고, ViewModel 은 격리된 AppDomain 으로 제한함으로써 언제든지 AppDomain 이 언로드될 수 있게 합니다.    

이 부분을 구현하기 가장 이상적인 환경은 바로 WPF(Windows Presentation Foundation) 이 되겠네요.

   

Views 의 교체

MVVM 으로 구현부와의 분리를 통해 당연히 Views 는 언제든지 교체가 가능합니다. 서버/로컬에서 Views 가 교체된다면 ViewModels 을 언로드하고 새로운 Isolated AppDomain 을 생성하여 View 와 ViewModel 간에 연결하는 방법입니다.    

특히 이 통신 구간은 View 와 ViewModel 간의 Interface Contract 를 통해 크리티컬한 자원의 관리를 최소화하는 것에 있습니다. 이로써 이미 로드된 사용자 화면과 어셈블리라도 서버/로컬의 갱신이 있다면 언제든지 갈아치울 수 있는 구간입니다. 이 부분이 앞서 얘기한 .NET 스마트클라이언트의 문제를 해결할 수 있는 핵심 구간입니다.

   

업데이트 기능을 재작성

이 아키텍처링의 가장 큰 문제지만, .NET 스마트클라이언트의 NTD(No Touch Deployment) 기능을 그대로 사용할 수 없습니다. .NET 의 NTD 는 이미 실행되는 AppDomain 에 어셈블리를 로드하기 때문에 .NET 의 NTD 를 그대로 사용한다면 이 아키텍처링을 적용할 수 없습니다.

NTD 기능 뿐만 아니라, ClickOnce 의 자동 버전 감지 기능도 사용할 수 없습니다. ClickOnce 는 주기적으로 서버의 Application Manifest 를 확인하는 과정으로 새로운 버전을 감지하고 업데이트하는데, 이 기능을 그대로 사용한다면 위의 아키텍처링은 사실상 무의미하고, 결국 메모리 사용 증가는 해결할 수 없기도 합니다.

   

제한 사항

하지만 필자가 제안한 .NET 스마트클라이언트의 문제를 해결하기 위한 방법은 제한적인 방법으로 수행이 가능합니다. 물론 모든 경우라도 제안이 가능한 방법이라면 좋겠지만, .NET 의 기본 아키텍처가 해결하지 못한 이상, 필자 또한 제한적인 방법으로 .NET 스마트클라이언트의 문제점을 해결할 수 있습니다.

그 제한적인 방법의 한계는 아래와 같습니다.

  • 개발 표준을 완벽하게 MVVM 기반으로 개발되어야 한다.
  • MVVM 패턴으로 완벽하게 분리가 되어야 한다.
    • WPF 를 사용할 경우 MVVM 패턴으로만 작성되어야 한다.
    • 윈도우 폼(Windows Forms) 또는 ActiveX 컨트롤 일 경우, MVVM 로 작성할 수 없다.
    • 이 경우, View와 ViewModels 를 분리하도록 별도 프레임워크 개발이 필요하다.
  • Marshaling 을 통한 통신
    • Marshaling 은 AppDomain 간의 원격 통신을 해야 한다.
    • 원격 통신으로 인한 성능 저하
  • WPF 개발
    • Binding Expression 을 확장한 Binding Marshaling Expression(단지, 예임) 으로 바인딩을 해야 한다.
    • 원격 바인딩으로 성능 저하 예상

   

결론

필자는 .NET 어플리케이션이 업데이트될 경우 왜 반드시 최적의 방법이 어플리케이션 쉘을 재시작하느냐에 시작한 고민으로부터 시작됩니다. .NET 아키텍처를 이해못하는 것은 아니지만, 고객은 언제나 더 향상된 방법을 제안합니다. 그리고 필자는 그런 고민을 극복하고자 제안한 방법입니다.

물론 위의 아키텍처링을 효율적인 면과 성능적인 면을 더 자세히 테스트해 보아야 하겠지만, 분명한 것은 끊임없이 고객의 요청은 진화하지 퇴화하지는 않을 거라고 생각합니다.

예전에 필자는 위와 같은 문제를 문의할 때, ".NET 에서는 안된다" 라고 답했습니다. 맞아요. 안됩니다. 하지만 문득, '안되면 되게 해야지!' 라는 생각이 들더군요. 짧은 소견이지만 잘못된 부분이 있으면 언제든지 피드백 주시기 바랍니다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 남정현 2010.07.20 14:12 Address Modify/Delete Reply

    좋은 글 잘 읽었습니다. 늘 그렇지만 "안되는 것을 가능하게 만드는 것"은 쉽지만, 그것을 다른 사람들에게 전달하고 공유하도록 만드는 것 (문서를 쓰는것부터 설명에 이르기까지)이 사실은 훨씬 더 어려운 작업이지 않나 생각하게 되네요. :-)

    • 땡초 POWERUMC 2010.07.20 23:26 신고 Address Modify/Delete

      오늘 뵙게 되어 너무 반가웠습니다.
      저는 사실 항상 정현님을 모티브로 본받으려고 노력하고 있답니다.^^
      언제나 겸손하시고 솔선수범해 주시는 모습..
      제 곁에 있어 주실꺼죠? ^^

    • 남정현 2010.07.28 23:53 Address Modify/Delete

      오우.. 댓글을 늦게 봤네요. =_=

      저야 말로 여러모로 앞으로 엄준일 MVP님께 많은것을 배워야 할것 같아요. 언젠가 인사드렸었지만 그래도 다시한번 잘 부탁드립니다. ㅎㅎ

  2. lancers 2010.07.21 09:07 Address Modify/Delete Reply

    어차피 별도 AppDomain을 사용하는 방법이 유일한 해결책인데,
    이 아키텍처의 착상은 나쁘지 않으나 실제로 진행하다 보면 많은 문제에 부딪칠 거야.
    지금 상황에선 예상하지 못한 문제들을 많이 겪게 될 거고..

    이론 상으로 볼 때는 이런 방향으로 가는 게 맞는데,
    실질적으로 사용자가 원하는 건 굳이 이런 방식이 아니어도 된다는 것도 알게 될 거고..

    회사 내부에서 이미 진행하고 있는 내용의 결론이 조만간 나올 듯..

    • 땡초 POWERUMC 2010.07.22 04:52 신고 Address Modify/Delete

      저도 그 많은 문제가 제가 생각하는 이상으로 많을 것 같아요.
      말씀하신 ShadowCopy 가 좀 더 현실적으로 가까운 것 같습니다.^^
      말씀해주신 힌트가 큰 도움이 되었습니다.
      ---------- Updated ----------
      다만, AppDomain 의 ShadowCopy 는 AppDomain 당 1회성이 아닐까요?
      테스트 해 보지는 않았지만, 논리적으로 본다면 AppDomain 당 ShadowCopy 폴더를 둔다는 것은 1회에 한한 제한적인 방법인 것 같습니다.
      ------------------------------

      DX 의 멋진 솔루션이 기대됩니다용^^

개요

.NET 에서 윈도우 어플리케이션을 개발해 본 독자라면 한번 쯤은 .NET 스마트클라이언트라는 용어를 많이 들어보았을 것입니다. 스마트클라이언트는 배포(Deployment), 플랫폼 독립 모델을 제공함으로써 다양한 클라이언트를 지원하는 것이 특징입니다.

예전에 필자가 UX 라는 주제로 쓴 포스트 중 "당신이 생각하는 UX 란?" 에서도 언급하였듯이, .NET 스마트클라이언트는 X-Internet 이라는 트랜드로 기술적인 부분을 초점으로 마케팅한 용어로 발전하였습니다. 이와 반대로 RIA(Rich Internet Application) 는 UX(User eXperience) 초점에서 마케팅한 용어라고 보셔도 좋습니다.

   

사전 지식

하지만 .NET 스마트클라이언트는 사실상 매번 나오는 이슈가 있습니다. 아니, 이것은 .NET 스마트클라이언트의 문제라기 보다는 .NET 자체의 아키텍처와 관련된 문제이기도 합니다.

결혼부터 말하자면, .NET 어플리케이션은 로드된 어셈블리(Loaded Assemblies) 는 언로드(Unload) 가 되지 않습니다. 간단하게 아래와 같이 .NET 어플리케이션의 모델을 보면 알 수 있습니다. .NET 어플리케이션은 하나의 AppDomain(Application Domain) 을 갖는 것을 알 수 있습니다.

   

AppDomain 은 어플리케이션 간의 CAS(Code Access Security) 라는 임계 영역에 존재하게 됩니다. 말 그대로 CAS(Code Access Security) 이 CAS는 어플리케이션간의 엑세스를 제한함으로써 신뢰할 수 없는 코드나 어플리케이션은 사용자의 컴퓨터에서 실행할 수 없도록 한 보안 모델입니다.    

즉, 이메일이나 인터넷, 사용자 그룹 및 권한 등 신원이 확인되지 않은 어플리케이션을 실행했을 때, 악의적인 목적으로 사용자의 로컬 자원을 엑세스할 수 없도록 제한하는 모델이라고 보시면 됩니다.    

이 코드 보안 모델은 .NET 의 어떤 어플리케이션이든 모두 이 보안 정책 안에 있다고 보시면 됩니다. ASP.NET 도 마찬가지로 아래와 같이 AppDomain 의 임계 영역 안에서 어플리케이션이 동작하게 됩니다. AppDomain 이 하나의 웹 어플리케이션을 동작하게 하고, HttpRuntime 에 의해 HttpContext 가 관리됩니다. 그리고 각각의 요청에 의해 HttpContext 는 별도의 스레드(Thread) 로 사용자의 요청을 응답하게 되는 구조라고 보시면 됩니다.

 예를 들어, 아래와 같은 코드 보안을 위한 선언적인 방법을 이용하여 악의적으로 사용될 수 있는 코드 쓰기, 수정 등을 할 수 없도록 합니다. 어셈블리, 클래스, 구조체, 생성자에서 사용할 수 있습니다. 물론 사용자가 이 보안 수준을 변경할 수 도 있지요.

문제 1

여태까지 이것을 말하기 위해 설명을 한 것입니다. 바로 .NET 어플리케이션은 어셈블리를 로드할 수 는 있지만, 언로드할 수 는 없습니다.

그러니까 더 자세하게 얘기하면, 아무리 가비지 컬렉션(Garbage Collection) 을 호출하고 CLR Runtime(Common Language Runtime) 이 이것을 대신 수행해 준다고 해도, 로드된 어셈블리 자체는 이 대상에서 예외라는 것입니다. 결론은 .NET 어플리케이션을 오래 쓰면 쓸 수록 메모리 사용이 증가할 가능성이 있습니다.

플러그인 모델(Plugin Model) 기반의 어플리케이션도 확장 기능이 많아지면 많아질 수록 메모리 점유율이 높아지고, 특히 엔터프라이즈 기업용 어플리케이션은 반드시 피해갈 수 없는 문제이기도 합니다.    

개인적으로 플러그인 모델과 엔터프라이즈 어플리케이션의 중간 영역이라고 생각되는 Visual Studio 를 한 1주일 정도 닫지 않고 써보셨나요? 쓰지 못할 정도는 아니지만, 괜히 버벅되고 느려지는 현상이 나타나게 된답니다.^^; 이런 현상은 Visual Studio 뿐만이 아니라 .NET 으로 작성된 모든 어플리케이션은 모두 영향을 받게 됩니다.

   

그 이유는, .NET 은 로드된 AppDomain 의 어셈블리를 언로드할 수 있는 방법을 제공해 주지 않습니다. AppDomain 이 참조하는 관계는 기본적으로 로컬 자원의 어셈블리를 참조하겠지만, 코드 베이스(Code Base-코드의 출처) 가 인트라넷이나 인터넷이라면 그 코드 베이스로부터 어셈블리를 다운로드 하게 됩니다.    

문제 2

결론부터 말하면, .NET 어플리케이션은 참조 또는 다운로드한 어셈블리는 다운로드 캐시(Download Cache) 에 보관하게 됩니다. 어셈블리를 참조 또는 다운로드하는 판정 조건은 어셈블리의 버전, 토큰 등 복잡한 과정을 거치기 때문에, 제대로 된 정책을 갖고 있지 않는다면, 이미 다운로드된 어셈블리는 다운로드 캐시로부터 어셈블리를 재사용합니다.    

그렇기 때문에, 다운로드된 어셈블리는 File Lock(파일 잠김)이 발생하므로, 동일한 파일 이름의 어셈블리를 다운로드 받는 것은 불가능 합니다. 하지만 해결책이 없는 것은 아닙니다. Assembly.Load 시리즈의 메서드에는 byte[] 로 읽을 수 있는 오버로드된 메서드가 존재하기 때문입니다.    

즉, 아래와 같이 File Lock 을 방지할 수 있습니다. 하지만 어셈블리는 로드할 수 있으나, 기존의 로드된 어셈블리를 갈아치우지는 못합니다.

 

결국, 하나의 어플리케이션을 오래 사용하면 할수록 메모리의 점유율을 증가할 수 있게 될 가능성이 큽니다. 특히 엔터프라이즈 기업용 어플리케이션은 단위 업무별로 적절한 파일 크기, 업무간의 연간 관계 등을 고려하여 어셈블리를 모듈화하는데, 사실상 메모리 사용률 증가의 문제는 여전히 해결할 수 없는 문제입니다. 그 이유는, 앞서 말했듯이 어셈블리를 언로드할 수 있는 방법은 AppDomain 을 언로드하는 것이고, AppDomain 을 언로드하면 메인 어플리케이션을 재시작해야 된다는 문제입니다.

   

문제 3

이 섹션은 문제 2와 연관된 정책적인 문제입니다. 다운로드된 어셈블리는 다시 다운로드 받을 수 없기 때문에 선행적으로 몇 가지 정책적인 강제가 필요할 수 밖에 없습니다.

  • 어플리케이션 쉘(Shell)
    • 어플리케이션 쉘이 업데이트되면 어플리케이션을 재시작 해야 한다.
  • 어플리케이션 실행 중 단위 어셈블리
    • 단위 어셈블리가 한 번 다운로드되면 서버/로컬의 어셈블리가 갱신되도 다운로드 받지 못한다.
    • 단위 어셈블리가 다운로드 되고 서버/로컬 어셈블리가 갱신되어도 알림 받을 수 없다.
    • 이럴 경우, 어플리케이션 쉘을 서버에서 갱신하여 업데이트 알림을 받을 수 있고, 어플리케이션을 재시작 해야한다.

즉, 어떠한 경우라도 갱신된 어플리케이션을 적용하기 위해서는 메인 어플리케이션 쉘을 재시작해야 한다는 결론을 얻을 수 있습니다.

   

문제 4

더욱 문제인 것은 .NET Framework 4.0 기반의 일부 스마트클라이언트는 이 문제와 상관없이 불가능합니다. 그 이유는 이미 닷넷엑스퍼트의 안재우 수석님의 블로그 중 "[.NET 4.0] IE Embedded WinForm(Smart Client) 지원 중단" 를 참고하세요.

이유의 요지는, IEHost.DLL 과 IEExec.EXE 파일이 .NET Framework 2.0 으로 강력한 이름의 서명이 되었다는 것입니다. 이것은 즉, IEHost.DLL 과 IEExec.EXE 를 통하는 .NET 스마트클라이언트의 경우 GAC(Global Assembly Cache) 를 통해 활성화가 되는데, .NET Framework 4.0 의 스마트클라이언트 어플리케이션은 어셈블리 리디렉트(Assembly Redirect)를 통하지 않고서는 이것을 활성화할 수 있는 방법이 없습니다. 어셈블리 리디렉트를 통한다고 하더라도 Dependency Assemblies 는 .NET Framework 2.0 을 바라보기 때문에 .NET Framework 4.0 의 기능을 사용한다면 절대 불가능하기도 합니다.

하지만 .NET 어셈블리의 바이트 코드 조작을 통해서 가능하긴 합니다.

  • IEHost.DLL, IEExec.exe 의 바이트 코드를 수정하여 강력한 서명을 지운다
  • IEHost.DLL, IEExec.exe 의 바이트 코드를 수정하여 .NET 4.0 으로 저장한다
  • GAC(Global Assembly Cache) 에서 IEHost.DLL 과 IEExec.EXE 를 제거한다.

어셈블리의 바이트 코드 조작은 Mono 프레임워크를 통해서 아주 쉽게 할 수 있습니다. 하지만 IEHost.DLL 과 IEExec.EXE 를 사용하는 모든 사용자 클라이언트를 해킹하는 무자비한 방법입니다. 하지만 된다는 것만으로도 만족한다면 이 방법이 최선의 방법이 될 것 같네요.

   

.NET 스마트클라이언트의 고찰

.NET 스마트클라이언트는 .NET 엔터프라이즈 어플리케이션에 많은 기여를 하였습니다. 그리고 .NET 스마트클라이언트를 사용하는 기업 또는 인트라넷 환경은 매우 많기도 합니다.    

필자 또한 얼마 전에 이러한 고민으로 Microsoft 의 의뢰를 받은 적이 있습니다. 그리고 개인적으로 아주 많이 고민했습니다.    

왜냐하면 자바의 클래스 로드(Class Loader) 는 .NET 의 스마트클라이언트와 유사한 점이 굉장히 많습니다. 하지만 다른 점이 하나 있다면, 자바의 클래스 로더는 GC(Garbage Collection) 의 대상이 된다는 것이죠. 다시 말하면, 어플리케이션의 재시작 없이 마음만 먹으면 메모리 사용률이 증가하지 않도록 아키텍처링이 가능하다는 것입니다.    

필자가 결론적으로, .NET 의 AppDomain 과 자바의 클래스 로더는 각기 특색은 있지만, 어느 것이 정답인지는 모르겠습니다. 다만, 고객이 어플리케이션의 재시작 없이 어플리케이션 업데이트/갱신이 가능해야 한다는 전제 조건이라면 자바의 클래스 로더가 장점이긴 합니다.    

하지만, 필자는 이 문제로 몇 일 동안 고민했습니다. 왜냐하면 세상에는 불가능한 것이 없다라는 것이 필자의 신념이기도 하며, 어떤 문제든 최선의 방법이라는 것이 존재한다고 믿습니다. 그리고 결국 "빙고" 를 찾았습니다. ^^

다음 회 차에서는 .NET 스마트클라이언트의 이러한 문제를 개선할 수 있는 방법을 알아보도록 하겠습니다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. lancers 2010.07.21 09:08 Address Modify/Delete Reply

    문제2의 File Locking 문제는 굳이 저렇게 하지 않고 Shadow Copy 기능을 쓰면 됨..

Fuslogvw.exe
 
Fuslogvw.exe 는 닷넷 어셈블리(EXE,DLL) 의 바인딩 정보를 관찰할 수 있는 유틸리티이다. 닷넷 어셈블리가 어떤 버전의 참조를 하고 있는지, GAC 으로부터 바인딩 되는지 잘 살펴볼 수 있는 유용한 도구이다. 
 
이 도구를 이용하여 다음과 같은 특정 오류에 대한 자세한 내용을 볼 수 있다. (MSDN 도움말 참조)
 
"파일을 찾을 수 없습니다." 또는 "버전이 일치하지 않습니다." 등 바인드가 실패한 특정 원인
이름, 응용 프로그램의 루트 디렉터리(AppBase) 및 개인 검색 경로(있는 경우)의 설명 등을 포함하여 바인드를 시작한 응용 프로그램에 대한 정보
도구에서 찾고 있는 어셈블리의 ID
적용된 응용 프로그램, 게시자 또는 관리자 버전 정책 설명
전역 어셈블리 캐시에 어셈블리가 있는지 여부
검색하는 모든 URL 목록
 
물론 이 도구에 대한 설명은 MSDN 도움말에 잘 나와 있지만, MSDN 을 즐겨하지 않는 분에겐 스크린샷과 함께 설명이 잘 된 다음의 URL 을 참고 하면 될 것이다.
 
Loner’s .NET Blog - Fusion Log Viewer: Fuslogvw.exe
http://www.simpleisbest.net/archive/2006/05/10/627.aspx
 
MSDN - 어셈블리 바인딩 로그 뷰어 ( fuslogvw.exe )
http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cptutorials/html/assembly_binding_log_viewer_deployment.asp
 
특히 스마트클라이언트에서 참조된 어셈블리가 제대로 다운로드 되었는지, Cache 에서 참조되었는지, 혹은 GAC 에서 참조되었는지 잘 알 수 있다.
 
이 부분에 대해선 이미 본 블로그의
 
스마트클라이언트 다운로드 비교 ( Infragistics 컴포넌트 ) 아티클에서 잘 보여주고 있다.
http://umc.pe.kr/article/2007/09/20/SmartClient-Download-With-Infragistics-Component.aspx
 
 
또 다른 활용 방안
 
위에서 살펴 보았듯이, 어셈블리의 바인딩 정보를 볼 수 있는 도구가 fuslogvw.exe 라고 하였다. 만약 하나의 어셈블리가 여러 개의 어셈블리를 참조하고 있게 되면, 순차적으로 log 폴더에 각 어셈블리별로 바인딩 되는 정보를 볼 수 있다. 더불어, 해당 어셈블리가 참조하는 .NET Framework 어셈블리의 바인딩 정보마저 로그에 기록된다. 때문에, 바인딩 될 .NET Framework 2.0 의 어셈블리가 .NET Framework 1.1, 1.0 어셈블리를 참조하게 되어, 스마트클라이언트가 제대로 동작하지 않게 되는 경우도 이 도구로 원인을 알 수 있다.
 
 
[그림1] Fiddler 다운로드 스크린샷
 
위 그림은 현재 본인이 투입되어 있는 프로젝트의 스마트클라이언트 다운로드 캡춰이다. ( 보안상 모자이크 처리 하였으니 양해 바랍니다 ) 쓸대없는 리소스를 찾아헤메는 보기싫은 404 오류는 이미지를 캡춰하기 위해 제거한 상태이다.
 
지금 위 [그림1] 을 보여주는 이유는 fustion log viewer 가 바인딩 시점의 시간을 알려주기 때문에, 그것에 아이디어를 얻어 어셈블리가 다운로드 순차적으로 다운로드 하며, 웹페이지에 엠베디드 되기까지의 시간을 재기 위한 것이다.
 
방법은 무척 간단하다. Fuslogvw.exe 의 설정에서 모든 바인딩을 디스크에 기록하기만 하면 된다. Iexplore.exe 에 의해 바인딩 되는 여러 어셈블리는 iexplore.exe 라는 폴더에 참조된 .NET Framework 와 어셈블리의 모든 로그가 기록 될 것이다. 모든 어셈블리의 로그가 html 파일로 기록이 되었다면, 폴더의 파일들을 시간순으로 정렬해보자. 그럼, 가장 늦은 시간의 어셈블리 바인딩 시간과 가장 빠른 시간의 어셈블리 바인딩의 시간차를 구하게 되면 바인딩 총 시간을 구할 수가 있다. 모든 Download Cache 와 인터넷 임시 파일을 삭제한 후, 최초 어셈블리 다운로드의 시간과 Download Cache 로부터의 바인딩 시간 등 비교할 수도 있다.
 
 
마치며…
 
본 블로그의 아티클을 보게 된다면, 반드시 Loner’s .NET Blog 와 MSDN 의 링크를 꼭 확인하여, 좀 더 정확한 정보를 얻길 바란다. 위 링크에는 레지스트리를 수정하여 공용 언어 런타임의 바인딩 로그를 기록하는 방법 또한 수록되어 있다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요

오늘도 어김없이 뎁질좀 하다가 스마트클라이언트 관련 질문이 올라왔다.
질문인 즉, Infragistic 컴포넌트를 사용하니 로컬에선 잘 되는데 원격접속시 다운로드가 제대로 되지 않는 것 같다는 것이다.
 
데브피아 Q&A 원문 :
http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=7&MAEULNO=5&no=70422&ref=70422&page=1
 
아래의 샘플 경로는 접속할 수 없습니다.
샘플 :
우선 예시 상황을 만들어 보았다.
아래는 버추얼PC 로 Infragistic 컴포넌트가 설치가 되지 않는 Windows XP 환경이다.
 
 
Fiddler 를 이용하여 어셈블리 다운로드 상태를 보도록 하자.
 
 
예상대로 어셈블리를 제대로 다운받지 못하여 발생하는 엑박 이라는 것을 알 수 있다.
 
Infragistic 은 최초 설치 시 GAC 에 등록되어 컴포넌트 참조시에 GAC 에 등록된 어셈블리를 참조하게 된다. 때문에, 참조된 어셈블리는 로컬 복사가 이루어 지지 않는다. 더 쉽게 말해, bin 폴더에 Infragistic 의 DLL이 복사되지 않는다.
 
그럼 다음과 같이 참조된 어셈블리를 하나의 DLL 로 떨어질 수 있도록 참조 속성을 변경해주자.
 
 
그럼 다음과 같이 Infragistic 컴포넌트가 설치 되지 않은 PC 에서 정상적으로 작동이 될 것이다.
 
 
C:\Visual Studio 2005 명령 프롬프트>GACUTIL /L
 
위와 같이 비쥬얼 스튜디오 명령 프롬프트를 통해 GAC 에 등록된 어셈블리를 확인할 수 있다.
 
 
만약 예리한 사람이라면 여기에서 굉장히 비효율성을 느낄 것이다.
DLL 모듈의 업데이트가 필요없는 구성요소에 대해 매번 GAC 과 로컬 Cache 를 뒤져가며, 버전체크 하려 할 것이다.
 
Fiddler 를 통해 GAC 에 등록된 어셈블리와 그렇지 않은 어셈블리를 찾아 헤메는 과정이다.
 
#
Result
Protocol
Host
URL
Body
Caching
Content-Type
User-defined
4
404
HTTP
umc.pe.kr
/iexplore.exe.config
1,466
text/html
5
304
HTTP
umc.pe.kr
/sample/Umc.PaintSmartClient.DLL
0
6
404
HTTP
umc.pe.kr
/iexplore.exe.config
1,466
text/html
7
404
HTTP
umc.pe.kr
/ko-KR/System.resources.DLL
1,466
text/html
8
404
HTTP
umc.pe.kr
/ko-KR/System.resources/System.resources.DLL
1,466
text/html
9
404
HTTP
umc.pe.kr
/bin/ko-KR/System.resources.DLL
1,466
text/html
10
404
HTTP
umc.pe.kr
/bin/ko-KR/System.resources/System.resources.DLL
1,466
text/html
11
404
HTTP
umc.pe.kr
/ko-KR/System.resources.EXE
1,466
text/html
12
404
HTTP
umc.pe.kr
/ko-KR/System.resources/System.resources.EXE
1,466
text/html
13
404
HTTP
umc.pe.kr
/bin/ko-KR/System.resources.EXE
1,466
text/html
14
404
HTTP
umc.pe.kr
/bin/ko-KR/System.resources/System.resources.EXE
1,466
text/html
15
404
HTTP
umc.pe.kr
/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
16
200
HTTP
CONNECT
urs.microsoft.com:443
0
17
404
HTTP
umc.pe.kr
/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
18
404
HTTP
umc.pe.kr
/bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
19
404
HTTP
umc.pe.kr
/bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
20
404
HTTP
umc.pe.kr
/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
21
404
HTTP
umc.pe.kr
/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
22
404
HTTP
umc.pe.kr
/bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
23
404
HTTP
umc.pe.kr
/bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
24
404
HTTP
umc.pe.kr
/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
25
404
HTTP
umc.pe.kr
/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
26
404
HTTP
umc.pe.kr
/bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
27
404
HTTP
umc.pe.kr
/bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL
1,466
text/html
28
404
HTTP
umc.pe.kr
/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
29
404
HTTP
umc.pe.kr
/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
30
404
HTTP
umc.pe.kr
/bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
31
404
HTTP
umc.pe.kr
/bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE
1,466
text/html
[Fiddler 1] Infragistics 컴포넌트가 GAC 에 등록 되었을 때 Fiddler 다운로드 상태
 
 
 
#                         Result                 Protocol              Host                    URL                     Body                   Caching              Content-Type      User-defined     
5                         404                     HTTP                  umc.pe.kr           /IEXPLORE.EXE.config                   1,466                                            text/html                                      
6                         404                     HTTP                  umc.pe.kr           /IEXPLORE.EXE.config                   1,466                                            text/html                                      
7                         404                     HTTP                  umc.pe.kr           /ko-KR/System.resources.DLL        1,466                                            text/html                                      
8                         404                     HTTP                  umc.pe.kr           /ko-KR/System.resources/System.resources.DLL      1,466                                            text/html                                      
9                         404                     HTTP                  umc.pe.kr           /bin/ko-KR/System.resources.DLL 1,466                                            text/html                                      
10                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/System.resources/System.resources.DLL                          1,466                                            text/html                                       
11                       404                     HTTP                  umc.pe.kr           /ko-KR/System.resources.EXE       1,466                                            text/html                                      
12                       404                     HTTP                  umc.pe.kr           /ko-KR/System.resources/System.resources.EXE      1,466                                            text/html                                      
13                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/System.resources.EXE                           1,466                                            text/html                                      
14                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/System.resources/System.resources.EXE                          1,466                                            text/html                                       
15                       404                     HTTP                  umc.pe.kr           /ko/System.resources.DLL             1,466                                            text/html                                      
16                       404                     HTTP                  umc.pe.kr           /ko/System.resources/System.resources.DLL            1,466                                            text/html                                      
17                       404                     HTTP                  umc.pe.kr           /bin/ko/System.resources.DLL       1,466                                            text/html                                      
18                       404                     HTTP                  umc.pe.kr           /bin/ko/System.resources/System.resources.DLL     1,466                                            text/html                                      
19                       404                     HTTP                  umc.pe.kr           /ko/System.resources.EXE             1,466                                            text/html                                      
20                       404                     HTTP                  umc.pe.kr           /ko/System.resources/System.resources.EXE           1,466                                            text/html                                      
21                       404                     HTTP                  umc.pe.kr           /bin/ko/System.resources.EXE      1,466                                            text/html                                      
22                       404                     HTTP                  umc.pe.kr           /bin/ko/System.resources/System.resources.EXE     1,466                                            text/html                                      
23                       200                     HTTP                  CONNECT           urs.microsoft.com:443                   0                                                                                                       
24                       404                     HTTP                  umc.pe.kr           /ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                 1,466                                            text/html                                       
25                       404                     HTTP                  umc.pe.kr           /ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                        1,466                                                     text/html                                      
26                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL          1,466                                            text/html                                       
27                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                  1,466                                                     text/html                                      
28                       404                     HTTP                  umc.pe.kr           /ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE                1,466                                            text/html                                       
29                       404                     HTTP                  umc.pe.kr           /ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE                        1,466                                                     text/html                                      
30                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE          1,466                                            text/html                                       
31                       404                     HTTP                  umc.pe.kr           /bin/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE                 1,466                                                     text/html                                      
32                       404                     HTTP                  umc.pe.kr           /sample/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL    1,466                                            text/html                                       
33                       404                     HTTP                  umc.pe.kr           /sample/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL           1,466                                                     text/html                                      
34                       404                     HTTP                  umc.pe.kr           /sample/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE   1,466                                            text/html                                       
35                       404                     HTTP                  umc.pe.kr           /sample/ko-KR/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE           1,466                                                     text/html                                      
36                       404                     HTTP                  umc.pe.kr           /ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                      1,466                                            text/html                                       
37                       404                     HTTP                  umc.pe.kr           /ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL   1,466                                             text/html                                      
38                       404                     HTTP                  umc.pe.kr           /bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                1,466                                            text/html                                       
39                       404                     HTTP                  umc.pe.kr           /bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                       1,466                                                     text/html                                      
40                       404                     HTTP                  umc.pe.kr           /ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE                      1,466                                            text/html                                       
41                       404                     HTTP                  umc.pe.kr           /ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE   1,466                                             text/html                                      
42                       404                     HTTP                  umc.pe.kr           /bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE               1,466                                            text/html                                       
43                       404                     HTTP                  umc.pe.kr           /bin/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE                       1,466                                                     text/html                                      
44                       404                     HTTP                  umc.pe.kr           /sample/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL         1,466                                            text/html                                       
45                       404                     HTTP                  umc.pe.kr           /sample/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.DLL                 1,466                                                     text/html                                      
46                       404                     HTTP                  umc.pe.kr           /sample/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE         1,466                                            text/html                                       
47                       404                     HTTP                  umc.pe.kr           /sample/ko/Infragistics2.Win.UltraWinGrid.v7.1.resources/Infragistics2.Win.UltraWinGrid.v7.1.resources.EXE                1,466                                                     text/html                                      
[Fiddler 2] Infragistics 컴포넌트가 GAC 에 등록되지 않았을 때 Fiddler 다운로드 상태
 
URL 등이 중요한 것이 아니라 어셈블리 다운로드를 위해 얼마만큼의 요청이 있었는지의 첫번째 컬럼을 통해 확인해 보자.
GAC 에 등록되지 않은 어셈블리 다운로드 요청 횟수가 GAC 에 등록된 어셈블리 다운로드 요청 횟수에 비해 16회 정도 더 많은 오버헤드를 유발하고 있다.
 
Infragistic 컴포넌트가 어떻게 바인딩 되었는지 로그를 통해 좀더 자세한 정보를 얻을 수 있다.
 
*** 어셈블리 바인더 로그 엔트리 (2007-09-19 @ 오후 11:43:43) ***
 
작업이 완료되었습니다.
바인딩 결과: hr = 0x0. 작업을 완료했습니다.
 
다음 위치에서 어셈블리 관리자 로드: C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
다음 실행 파일에서 실행: C:\Program Files\Internet Explorer\iexplore.exe
--- 자세한 오류 로그가 아래에 표시됩니다.
 
=== 미리 바인딩된 상태 정보 ===
로그: 사용자 = PowerUmc\엄준일
로그: DisplayName = Infragistics2.Win.v7.1, Version=7.1.20071.40, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb
 (Fully-specified)
로그: Appbase = http://umc.pe.kr/
로그: 초기 PrivatePath = bin
로그: 동적 베이스 = NULL
로그: 캐시 베이스 = NULL
로그: AppName = iexplore.exe
호출 어셈블리: Infragistics2.Win.UltraWinGrid.v7.1, Version=7.1.20071.40, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb.
===
로그: 이 바인딩은 default 로드 컨텍스트에서 시작됩니다.
로그: 응용 프로그램 구성 파일이 없습니다.
로그: C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config에서 컴퓨터 구성 파일을 사용합니다.
로그: 바인딩했습니다. C:\Windows\assembly\GAC_MSIL\Infragistics2.Win.v7.1\7.1.20071.40__7dd5c3163f2cd0cb\Infragistics2.Win.v7.1.dll에서 어셈블리를 반환합니다.
오류: 어셈블리가 default 로드 컨텍스트에 로드되었습니다.
[바인딩 로그 1] Infragistic 컴포넌트가 GAC 에 등록되었을 때 바인딩 로그
 
*** Assembly Binder Log Entry (2007-09-19 @ 오후 11:57:54) ***
 
The operation was successful.
Bind result: hr = 0x0. 작업을 완료했습니다.
 
Assembly manager loaded from: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable C:\Program Files\Internet Explorer\IEXPLORE.EXE
--- A detailed error log follows.
 
=== Pre-bind state information ===
LOG: User = VPOWERUMCXP\엄준일
LOG: DisplayName = Infragistics2.Win.v7.1, Version=7.1.20071.40, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb
 (Fully-specified)
LOG: Appbase = http://umc.pe.kr/
LOG: Initial PrivatePath = bin
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = IEXPLORE.EXE
Calling assembly : Umc.PaintSmartClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Post-policy reference: Infragistics2.Win.v7.1, Version=7.1.20071.40, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb
LOG: GAC Lookup was unsuccessful.
LOG: Where-ref bind Codebase does not match what is found in default context. Keep the result in LoadFrom context.
LOG: Found assembly by looking in the download cache.
[바인딩 로그 2] Infragistic 컴포넌트가 GAC 에 등록되지 않았을 때 바인딩 로그
 
[바인딩 로그 1] 에서는 GAC 등록된 어셈블리를 로드 하였다. 참고로 GAC 에 등록된 어셈블리는 Windows폴더의 Assembly 안에서 확인할 수 있다. ( GACUTIL /L 과 동일 )
하지만, [바인딩 로그 2] 에서는 GAC 에서 찾지 못했다는 통보와 함께 다운로드 캐시에서 어셈블리를 찾았다는 로그를 남겨 놓는다.
 
그렇다면, 스마트클라이언트가 화면에 나타나기까지 동적인 업데이트(웹으로부터 업데이터)가 필요없는 어셈블리에 대해서 GAC 에 등록하는 것이 다운로드 속도를 증가하는 것을 알 수 있다.
 
그럼 이런 어셈블리는 어떻게 GAC 에 등록을 하면 될까~?
방법은 ClickOnce 를 통한 배포로 가능하다. ClickOnce 의 부트스트래퍼를 통해 필수구성요소로써 Infragistics 어셈블리를 배포하는 것이다. 하지만 이 내용은 이번 아티클에 포함하기엔 약간은 방대한 분량이라 다음 기회에 다시 다루어 볼 예정이다.
 
우연히 뎁질(데브피아 놀이) 하다가 팁으로 올리려다 이렇게 글이 길어지게 되었기에, 내일 아침도 뻐근한 목을 부등켜 잡고 출근해야 할 것 같다. 아훔 +_+
Posted by 땡초 POWERUMC

댓글을 달아 주세요

다음은 스마트클라이언트 디버깅 방법이다.
웹 엠베디드 스마트클라이언트를 기준으로 설명드리지만, 독립 스마트클라이언트와 큰 차이가
없기 때문에 동일한 방법으로 디버깅을 수행하시면 된다.
 
여기에서 사용할 예제는 본 블래그의 실전 예제로 준비된 “다중파일 업로드 스마트클라이언트”
를 사용할 것이다.
 
다음의 아티클을 참고하시면, 예제 소스도 함께 제공됩니다.
[.NET/Smart Client] - 실전 다중파일업로드 스마트클라이언트 - [1]
[.NET/Smart Client] - 실전 다중파일업로드 스마트클라이언트 - [2]
 
 
1. 먼저 디버깅할 프로젝트를 연 후에, 원하는 지점에 브레이크 포인트를 건다.
 
 
 
 
2. 스마트클라이언트가 엠베디드 되는 페이지를 연다.
 
 
좀 특이한 점이 있는데 혹시 눈치 채셨을라나..
VS 2005 에 로드한 프로젝트는 로컬 프로젝트이고, 웹페이지는 원격 서버에 있는 DLL 이 엠베디드 되었다.
하지만, 로컬이든 원격이든 동일한 어셈블리일 경우 문제없이 디버깅이 가능하다는 것.
 
 
 
3. VS 2005 의 디버그 메뉴의 “프로세서 연결…” 을 클릭한다.
 
 
 
 
4. 스마트클라이언트가 엠베디드 된 브라우져를 클릭하여 연결을 클릭한다.
 
 
그럼, VS 2005 는 편집 모드가 아닌, 디버깅 모드로 전환될 것이다.
 
 
 
5. 전송 버튼 내부에 브레이크 포인트를 걸었기 때문에, 웹페이지에서 전송 버튼을 눌러보자.
 
 
전송 버튼을 클릭하면 아래와 같이 브레이크 포인트 위치에 정지하면서 디버깅이 가능하다.
 
 
 
 
6. 이제부터 F10 과 F11 을 이용하여 웹 페이지에 엠베디드 된 스마트클라이언트를 마음껏 디버깅 해보자.
 
 
 
알고 보니 무척 간단하다.
만약, 동일한 프로젝트더라도 어셈블리가 일치 하지 않게 되면 디버깅이 불가능하다는 것을 염두 해 두자.
또한, 제대로 바인딩 되지 않는 스마트클라이언트는 Iehost 로그파일이나,
fuslogvw 를 이용하여 예외를 감시하면 보다 많은 정보를 얻을 수 있을 것이다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요


2009/06/19 - [.NET/Smart Client] - 실전 다중파일업로드 스마트클라이언트 - [2]



상태 변경 작업에 대한 이벤트
 
샘플의 UserControl 에서 업로드 진행 상태에 대한 5가지의 이벤트를 제공한다.
 
UploadStart;                       // 업로드가 시작할때 발생
UploadCompleted;                    // 업로드가 완료되면 발생
UploadFileChanged;                 // 파일별 업로드되면 발생
UploadFilePercentChanged;          // 업로드 진행율이 변경되면 발생
UploadOneFileCompleted;             // 파일 하나가 업로드 완료되면 발생
 
주석에서 보듯이, 업로드 상태의 변경에 따른 이벤트가 발생하도록 하고 있다.
 
이벤트의 발생은 외부에서 다음과 같이 발생하도록 한다.
 
private void OnUploadStart()
{
         if( UploadStart != null )
                  Invoke( UploadStart, null );
}
 
외부 코드에서 += 연산을 통해 이벤트가 구현이 되었을 경우 Invoke 를 통해 이벤트의 Delegate 가 실행되도록 한다.
나머지 Riase 이벤트도 동일하므로 생략한다.
특히 인자를 받는 이벤트도 주의깊게 보도록 하자.
 
 
클래스와 이벤트를 COM 에 노출하기
 
COM 에 노출하기 위한 Attribute 을 선언한다. 이 부분에 대해 실전 스마트클라이언트 그림판 을 참고 하도록 하자.
 
[ClassInterface( ClassInterfaceType.AutoDispatch )]
[ComSourceInterfaces( typeof( IFileUploadComEvents ) )]
[ComVisible(true)]
public partial class FileUploadCtrl : UserControl
{
       …
}
FileUploadCtrl 클래스의 속성 선언
 
[Guid("1DAC6C2D-24C3-4965-B2B6-B7B611C1639C")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IFileUploadComEvents
{
         [DispId(1)]
         void UploadStart();
      …
}
Interface 를 통한 COM 에 이벤트 노출
 
 
 
스마트클라이언트 엠베디드 페이지
 
드디어 UserControl 이 완성되었다.
알다시피 스마트클라이언트의 어셈블리(*.DLL) 은 브라우져의 object 태그로 엠베디드할 수 있다.
파일이 전송될 때 frmProgress 폼이 뜨게 되고, 업로드 진행상황이 표시되도록 하였다.
하지만, 클래스와 이벤트를 COM 에 노출하였기 때문에, frmProgress 없이도 브라우져의 HTML 로도 진행 상황을 표시할 수 있다.
 
스마트클라이언트, HTML 이 각각 표시하는 업로드 진행율
 
위와 같이 스마트클라이언트가 내뱉는 이벤트를 받을 수 있다.
 
<%@ Page Language="C#" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>다중업로드 스마트클라이언트</title>
         <script type="text/javascript">
                  var cnt = 0;
         </script>
         <script event="UploadStart" for="ctrl">
                  document.getElementById("divUploadStatus").innerText = "업로드 시작";
         </script>
         <script event="UploadCompleted" for="ctrl">
                  document.getElementById("divUploadStatus").innerText = "업로드 완료";
         </script>
         <script event="UploadFileChanged(filename)" for="ctrl">
                  document.getElementById("divFilename").innerText = filename;
         </script>
         <script event="UploadFilePercentChanged(percent)" for="ctrl">
                  document.getElementById("divProgress").style.width = percent + "%";
                  document.getElementById("divProgress").innerText = percent + "%";
         </script>
         <script event="UploadOneFileCompleted" for="ctrl">
                 
         </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
                  <object id="ctrl" classid="Umc.FileuploadCtrl.DLL#Umc.FileUploadCtrl.FileUploadCtrl">
                           <param name="UploadUrl" value="http://umc.pe.kr/Sample/FileUploadSmartClient/FileUpload.aspx" />
                  </object>
                  <p />
                  업로드 상태 : <div id="divUploadStatus"></div>
                  업로드 파일명 : <div id="divFilename"></div>
                  <p />
                  진행율 : <br />
                  <div id="divProgressPanel" style="width:500px;height:20px;">
                           <div id="divProgress" style="width:0%;height:100%;text-align:center;background-color:Red;"></div>
                  </div>
    </div>
    </form>
</body>
</html>
 
FileUploadSmartClient.aspx
 
<script event="UploadFileChanged(filename)" for="ctrl">
         document.getElementById("divFilename").innerText = filename;
</script>
 
C# 코드에서 += 연산을 통해 이벤트를 등록하지만,
자바스크립트로 위와같이 C# 코드처럼 이벤트를 등록할 수 있다.
 
샘플의 WebClient 가 제공하는 이벤트를 통해
 
OnUploadFilePercentChanged(e.ProgressPercentage);
 
InterfaceType Attribute 과 Raise Event 를 호출하였기 때문에, 자바스크립트로 이벤트를 전달 받을 수 있다.
 
 
HTTP 파일 데이터를 받는 페이지
 
아마 샘플의 소스중에 가장 간단한 부분이 될 것 같다.
한번 코드를 보도록 하자.
 
<%@ Page Language="C#" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
         private void Page_Load(object sender, EventArgs e)
         {
                  if (Request.Files.Count > 0)
                  {
                           string absPath = Server.MapPath(".");
                           string uploadPath = System.IO.Path.Combine( absPath, "Upload" );
                          
                           if( !System.IO.Directory.Exists( uploadPath ) )
                                   System.IO.Directory.CreateDirectory( uploadPath );
                          
                           Request.SaveAs( System.IO.Path.Combine( uploadPath, Request["filename"].ToString()), false);
                  }
         }
         protected override void OnInit(EventArgs e)
         {
                  base.OnInit(e);
                  Server.ScriptTimeout       = 60*1;
         }
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>제목 없음</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>   
    </div>
    </form>
</body>
</html>
 
HTTP 파일 데이터를 받는 FileUpload.aspx
 
현재 폴더로부터 Upload 폴더에 업로드된 파일을 저장하도록 하였다.
저장될 파일명을 위해 GET 방식으로 저장될 파일명을 넘기도록 하였다.
즉,
 
UploadFile.aspx?filename=a.exe
 
라면 전송된 파일 데이터는 a.exe 로 저장되도록 하였다.
 
아무래도 샘플 페이지이고 외부 업체에 호스팅을 받는 나에게 있어, 트래픽 과부하(아직 그런일은 없다 -_-;) 는 치명적이므로
 
Server.ScriptTimeout   = 60*1;
 
구문을 통해 업로드 되는 파일당 요청제한 시간을 60초로 설정하였다.
때문에, 요청 제한 시간이 초과하게 된다면 “페이지를 찾을 수 없습니다” 또는 “페이지를 요청할 수 없습니다” 라는 오류 또는 메시지가 뜨게 될 것이다.
 
 
마치며
 
아티클이 조금 길어진 감이 없지 않아 있는 것 같다.. 좀더 많은 부분을 전달해 주고 싶었기에, 짧은설명이 아쉬운 부분도 있겠지만, 나름대로 강조한 부분이 잘 전달 되었길 바랄뿐이다 >.,<
언제나 그렇듯, 아티클 하나로 모든 부분을 긁어주진 못할거라고 생각한다.
나조차 아직 많은 부분을 미스하고 넘어가는 경우가 많다. 좋은 정보는 함께 공유했으면 하는 바램이다^^
Posted by 땡초 POWERUMC

댓글을 달아 주세요



실전 스마트클라이언트 그림판 을 끝으로 관련 단원을 마무리 하려다가, 그림판 샘플도 많이 아쉽고, 허접하여 한가지 샘플을 더 작성해 보았다.
이번에 소개할 샘플도 그림판 샘플과 거의 같은 맥락이지만, 좀 더 난이도가 있는 샘플을 만들어 보았다.
 
ActiveX 로만 보아왔던 업로드/다운로드 컨트롤을 스마트클라이언트로 만들어 볼 것이다.
업로드 스마트클라이언트 구성
 
샘플을 업로드/다운로드가 모두 가능한 컨트롤로 만들어 볼까 해봤었는데, 시간 관계상, 샘플이 비대해 질 것 같아서 업로드만 가능하도록 구성해 보았다.
데이터는 HTTP 를 통해 전송될 것이고, 서버측에서 데이터를 받도록 하였다.
소켓을 통해 사용자별 전송속도를 제한하려 하였으나, 본 주제와 많이 벗어나게 되므로, 단순히 HTTP 로 구성하게 되었다.
 
 
보안 설정 및 샘플 미리보기
 
CAS 보안설정 배포
http://umc.pe.kr/umccas/UmcCas.application ( CAS 보안설정 후 브라우져를 재시작 하세요 )
 
다중파일 업로드 스마트클라이언트 샘플
 
독립 스마트클라이언트로 작동하는 샘플 ( 작동오류시, 신뢰 할 수 있는 사이트로 등록 )
http://umc.pe.kr/sample/fileuploadsmartclient/umc.fileuploadctrl.win.exe

파일업로드 스크린샷


UploadFileInfo 클래스
 
UploadFileInfo 클래스는 업로드 하기위한 파일의 간략한 정보를 저장하는 클래스이다.
그렇게 중요한 부분은 아니므로 소스만 대충 훓어보면 될 것 같다.
 

public class UploadFileInfo
{
         private string filename;
         private string fullname;
         private long filesize;
 
         public UploadFileInfo(string filename, string fullname, long filesize)
         {
                  this.filename = filename;
                  this.fullname = fullname;
                  this.filesize = filesize;
         }
 
         public string FileName
         {
                  get { return filename; }
                  set { filename = value; }
         }
 
         public string FullName
         {
                  get { return fullname; }
                  set { fullname = value; }
         }
 
         public long FileSize
         {
                  get { return filesize; }
                  set { filesize = value; }
         }
}
UploadFileInfo 클래스

 
 
파일 추가/삭제
 
UserControl 에 ListView 컨트롤을 올리자. 파일추가/삭제 의 버튼의 액션에 따라, OpenFileDialog 를 띄워 파일을 추가하고, 삭제버튼을 통해 ListView에서 추가된 파일을 삭제할 것이다.
이 부분은 중요한 부분이 아니므로, 첨부된 소스를 참고하기 바란다.
 
 
작업 진행율을 위한 frmProgress 폼 디자인
 
frmProgress 에서 단 한줄의 코드도 추가하지 않았다. 개인적으로 샘플로 제공되는 소스가 3개이상의 클래스로 분리되어 있는 것을 좋아하지 않아서, 모든 이벤트는 UserControl 에 구현하였다.
 

frmProgress 폼 디자인

 
 
스레드를 통한 비동기 업로드 시작
 
업로드가 진행되는 동안, UI 의 변화가 지속적으로 변화됨과 사용자의 취소 작업이 이루어 질 수 있다. 때문에 스레드를 통해 업로드 작업이 진행되지 않으면, 사용자는 UI, 즉, 취소 버튼을 클릭할 수 없게 된다.
 
다음은 “전송” 버튼에서 호출하는 Upload() 메서드이다.
 

public void Upload()
{
         …
         fileProgressBytes = progressBytes = uploadingFilesCount = 0;
         frmProgress = new frmFileUploadProcess();
         frmProgress.btn.Click += new EventHandler(btn_Click);
         frmProgress.FormClosing += new FormClosingEventHandler(frmProgress_FormClosing);
         frmProgress.Show();
 
         // 스레드로 Upload 를 진행한다.
         OnUploadStart();
         thread = new System.Threading.Thread(new System.Threading.ThreadStart(UploadThread));
         thread.Start();
}
Upload() 메서드

 
 
진행율을 표시하는 UploadThread 메서드
 
아마 다중파일 업로드의 가장 중요한 부분의 메서드가 UploadThread 가 아닌가 싶다.
WebClient 클래스를 이용해 비동기로 데이터를 전송하는 방법과 전송중 대기하는 방법이 구현되어 있다.
 

private void UploadThread()
{
         WebClient wc = new WebClient();
         wc.UploadProgressChanged += new UploadProgressChangedEventHandler(wc_UploadProgressChanged);
         wc.UploadFileCompleted += new UploadFileCompletedEventHandler(wc_UploadFileCompleted);
 
         int cnt = 0;
         foreach (UploadFileInfo info in filesInfo)
         {
                  try
                  {
                           isBusy = true;
                           OnUploadFileChanged(info.FileName);
                           SetFileTitle(string.Format("{0} {1}/{2}", info.FileName, ++cnt, filesInfo.Count));
                           wc.UploadFileAsync(new Uri(UploadUrl + "?filename=" + info.FileName), "POST", info.FullName);
 
                           // 파일이 전송중이라면 끝날때 까지 다음파일을 전송하지 않는다.
                           while (isBusy) { }
                  }
                  catch (System.Threading.ThreadAbortException)
                  {
                  }
                  catch (WebException ex)
                  {
                           MessageBox.Show("서버에 연결할 수 없습니다\n" + ex.Message);
                  }
                  catch (Exception ex)
                  {
                           MessageBox.Show(ex.Message);
                  }
         }
 
         SetFileProgress(100);
         SetTotalProgress(100);
 
         SetBtnText("완료");
 
         OnUploadCompleted();
 
         if (thread.IsAlive)
         {
             thread.Abort();
             thread.Join();
         }
 
}
비동기로 파일을 전송하는 UploadThread() 메서드

 
가장 우선적으로 보아야 할 부분 아래의 코드이다.
 
wc.UploadFileAsync(new Uri(UploadUrl + "?filename=" + info.FileName), "POST", info.FullName);
 
WebClient 는 비동기적으로 데이터를 전송 또는 요청하는 메서드는 ~Async 가 붙는다.
UploadFileAsync, UploadDataAsync, UploadValueAsync, DownloadFileAsync, DownloadDataAsync…
 
위의 나열한 ~Async 메서드는 다른 스레드와 독립적으로 비동기 요청이 발생한다.
 
MSDN 왈~

~Async 메서드는 호출 스레드를 차단하지 않습니다.

 
무슨말일까? 일반적으로 스레드는 다른 스레드와 교차점, 즉 크로스 스레드를 전혀 신경 쓰지 않아도 된다.
스레드와 스레드가 너무나도 평범하게(?) 교차하게 되면 Cross Thread 관련 Exception 이 발생하지만, ~Async 는 메인스레드(Upload 메서드가 생성하는 스레드)가 종료 되어도 아무런 Exception 이 발생하지 않는다.
 
즉, WebClient 의 ~Aysnc 메서드는 통제할 수 없는 스레드라는 것 정도만 염두해 두도록 하자.
 
~Async 메서드가 호출됨으로써, 드디어 우리가 원하는 이벤트를 발생할 수 있다.
 
가령, UploadFileAsync 메서드가 호출되었다면 비로서 WebClient 는
 
wc.UploadFileCompleted
wc.UploadProgressChanged
 
두가지 이벤트를 발생한다.
네이밍에서 암시하듯, 전송완료, 전송율변경 이벤트가 발생한다.
( 위에서도 말했듯이, UploadFile 메서드가 호출되면 절대로 발생하지 않는다 )
 
 
WebClient 의 비동기 작업 이벤트
 
그럼 위의 두 이벤트를 어떻게 구현했는지 코드를 살펴보자.
 

void wc_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
         OnUploadOneFileCompleted();
         progressBytes += fileProgressBytes;
         isBusy = false;
         uploadingFilesCount++;
}
 
void wc_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
         int percent = (int)(((float)e.BytesSent / (float)e.TotalBytesToSend) * (float)100);
         OnUploadFilePercentChanged( percent );
         //SetFileProgress(e.ProgressPercentage);
         SetFileProgress( percent );
 
         fileProgressBytes = e.BytesSent;
         SetTotalProgress((int)(((float)progressBytes + e.BytesSent )/ (float)totalBytes * (float)100));
}
UploadFileAsync 가 발생하는 두가지 이벤트 구현

UploadProgressChanged 이벤트에서 UploadProgressChangedEventArgse 이벤트 인자는
ProgressPercentage 프로퍼티가 제공이 된다.
현재 업로드중인 파일의 진행율을 퍼센테이지 단위로 값을 알 수 있지만,
이 역시 액면상 UI 와 안맞는 점이 있기 때문에, 전송된 용량으로 퍼센테이지를 구했다. 
wc_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
 
분명, 위에서 WebClient 의 ~Async 메서드는 비동기 전송 메서드라고 말한 바 있다.
즉, 모든 파일이 비동기적으로 전송되는 것을 방지하기 위해 isBusy 라는 맴버를 구현하였다.
 
UploadThread() 메서드에서
 
while (isBusy) { }
 
코드로 인해, 파일 전송이 완료 되면, 다음 파일을 전송하도록 하였다. 만약, 위 코드가 존재하지 않는다면, 전송될 모든 파일이 한꺼번에 전송되고 만다.
 
아마 눈치빠른 분이라면 WebClient 의
 
wc.IsBusy
 
프로퍼티가 제공된다. 하지만, 체감상 제대로 된 동기화가 되지 않는 것 같아 따로 isBusy 맴버를 선언하여 구현한 것 뿐이다.
 
 
UI 동기화 메서드
 
우리는 ProgressBar 가 변경되면 이 Value 값을 변경하여, UI 에 적용하여야 한다.
하지만, 사용자의 취소 버튼 클릭을 위해 Thread 로 전송버튼을 구현하였고, UserControl 과 frmProgress 의 서로다른 스레드의 UI 를 변경하기 위해 Cross Thread 를 구현해야 한다.
 
Cross Thread 를 위한 코드는 흔희 볼 수 있는 코드일 것이다.
 

private delegate void FileProgressHandler(int percent);
private void SetFileProgress(int percent)
{
         if (frmProgress.pbFileProgress.InvokeRequired)
         {
                  Invoke(new FileProgressHandler(SetFileProgress), percent);
         }
         else
         {
                  frmProgress.pbFileProgress.Value = percent;
         }
}
UI 변경 작업을 위해 Delegate 에서 UI 변경작업을 위임하는 코드

 
이외에도 몇가지 메서드가 더 존재하니, 샘플 코드를 통해 확인해 보도록 하자.
 
 
Thread 종료하기
 
Upload() 메서드에서
 
frmProgress.FormClosing+=new FormClosingEventHandler(frmProgress_FormClosing);
 
와 같이 FormClosing 이벤트를 등록되어있다.
 
코드를 보자.
 

void frmProgress_FormClosing(object sender, FormClosingEventArgs e)
{
         // 폼이 닫힐때 스레드를 종료한다.
         if (thread.IsAlive)
         {
                  thread.Abort();
                  thread.Join();
         }
}
폼이 닫힐 때 Thread 를 종료하도록 한다.

 
업로드 중 사용자에 의해 “취소” 버튼이 눌려지면, 스레드를 강제로 종료하도록 구현되어 있다.
 
Thread.Abort() 메서드의 MSDN 왈~

이 메서드가 호출되는 스레드에서 ThreadAbortException을 발생시켜 스레드 종료 프로세스를 시작합니다. 이 메서드를 호출하면 대개 스레드가 종료됩니다.

 
Thread.Abort() 메서드는 그다지 아름다운 코드는 아니다.
위에 설명을 보아도 “이 메서드를 호출하면 대게 스레드가 종료됩니다” 라고 한다.
 
어쨌든, Thread.Abort() 메서드를 호출하게 되면 ThreadAbortException 이 발생하게 되고, 스레드가 종료되는 시점을 catch 하여 스레드 종료에 관련된 코드를 삽입할 수 있다.
 
UploadThread() 메서드에서
 
catch (System.Threading.ThreadAbortException)
{
}
 
코드를 통해 스레드 종료에 대해 아무런 작업을 하지 않음을 알 수 있다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요



스마트클라이언트 프로그래밍을 하다보면, 웹페이지와의 연동이 필요한 부분이 생기기 마련이다.
가령, 스마트클라이언트 UI 의 특정 버튼 클릭에 의해 브라우져의 URL 이 변경된다던가, 특정 팝업창을 띄어야 한다는 등의 경우에 브라우져는 스마트클라이언트의 특정 이벤트를 감지할 필요가 있다.
 
테스트 환경은 Internet Explorer 5.5 이상 버전이면 충분하다.
 
소스를 작성하기 앞서 Delegate 와 Event 에 대한 선수 지식이 필요하므로, 필요하다면 MSDN 에서 Delegate 와 Event 에 대해서 잠시 살펴 보는것도 좋을 것 같다.


2009-06-20 아래의 샘플은 접속할 수 없습니다.
소스의 샘플은 다음의 URL 을 통해서 테스트 해 볼 수 있습니다. ( 단, CAS 권한을 설정해야 합니다 )

 
그럼 이제 구현에 들어가 보도록 하자.
 
 
Interface 를 통한 이벤트 메서드 선언
 
일반적으로 ComVisible(true) 로 설정되어 있다면, 우리는 자바스크립트로 public 으로 된 프로퍼티나 메서드 등에 바로 접근 할 수 있다.
하지만, 이벤트는 그렇지 않다. 이벤트는 Interface 를 통해 COM 에 노출 시킬 방법을 결정해야 합니다.
 
///<summary>
/// COM 에 노출할 이벤트 메서드
///</summary>
[Guid("F9F21A39-FC35-4503-95E1-FBB13E749C3E")]
[InterfaceType( ComInterfaceType.InterfaceIsIDispatch )]
public interface IUmcClick
{
         [DispId(0x60020000)]
         void UmcClick();
 
         [DispId(0x60030000)]
         void UmcParamClick( string param );
}
[코드1] COM 에 노출시킬 Interface 정의
 
[InterfaceType( ComInterfaceType.InterfaceIsIDispatch )] 구문을 통해 COM 에 노출시키는 방법을 결정합니다.
 
[DispId(0x60020000)] 구문을 통해 디스패치 식별자를 각각의 이벤트마다 서로 다르게 지정합니다.
 
아직 저 또한, COM 에 대해 구구절절 설명해 줄만큼 잘 알지 못하고, 위에서 사용된 Attribute 은 공식처럼 사용된다는 것 정도만 알아 두면 될 것 같다.
 
 
UI 에서의 이벤트 발생
 
다음 UserControl 코드는 두개의 버튼을 올려 놓았으며,
각각 버튼은 인자가 없는 버튼과, 인자를 전달하는 이벤트에 대한 소스입니다.
 
[ClassInterface( ClassInterfaceType.AutoDual)]
[ComSourceInterfaces(typeof(IUmcClick))]
public partial class UserControl1 : UserControl
{
         // 인자없는 이벤트
         public delegate void UmcClickHandler();
         public event UmcClickHandler UmcClick;
 
         // 문자열 인자를 넘길 이벤트
         public delegate void UmcParamClickHandler( string param );
         public event UmcParamClickHandler UmcParamClick;
 
         public UserControl1()
         {
                  InitializeComponent();
         }
 
         ///<summary>
         /// Raise Event
         ///</summary>
         private void OnUmcClick()
         {
                  if( UmcClick != null )
                      Invoke( UmcClick, null );
         }
 
         ///<summary>
         /// Raise Event
         ///</summary>
         private void OnParamUmcClick()
         {
                  // 인자값을 넘긴다.
                  if( UmcParamClick != null )
                      Invoke( UmcParamClick, "스마트클라이언트가 전달한 메세지");
         }
 
         private void button1_Click(object sender, EventArgs e)
         {
                  OnUmcClick();
         }
 
         private void button2_Click(object sender, EventArgs e)
         {
                  OnParamUmcClick();
         }
}
[코드2] UserControl 의 두개 버튼에 대한 이벤트 발생
 
public delegate void UmcClickHandler();
public event UmcClickHandler UmcClick;
 
이벤트를 발생시키기 위해 델리게이트와 이벤트를 선언하였다.
 
if( UmcClick != null )
    Invoke( UmcClick, null );
 
이 구문을 통해 이벤트를 발생을 시킵니다.
 
위 두 부분이 잘 이해가 되지 않는다면, 이벤트에 대해 좀 더 학습 해야 할 것 같다.
 
한가지 주의할 것은, Interface 를 통해 선언한 이벤트메서드와 UI부분의 event 의 네이밍을 맞춰주도록 하자. 그렇지 않으면 이벤트를 자바스크립트에서 제대로 받기가 힘들다.
 
[주의] 위 처럼 이벤트와 Interface 메서드의 네이밍을 맞추어주자.
 
 
HTML 에서 이벤트 받기
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>SmartClient 와익스플로러의연동</title>
    `<script event="UmcClick" for="control1">
                  alert('인자없는이벤트전달받음');
    </script>
         <script event="UmcParamClick(a)" for="control1">
                  alert(a);
         </script>
</head>
<body>
    <form id="form1">
    <div>
                  <object id="control1" classid="SmartClient1.DLL#SmartClient1.UserControl1"></object>
    </div>
    </form>
</body>
</html>
[코드3] test.html
 
<object id="control1" classid="SmartClient1.DLL#SmartClient1.UserControl1"></object>
 
Object 태그를 통해 웹에 우리가 만든 UserControl 이 엠베디드 될 것이다.
 
<script event="UmcClick" for="control1">
<script event="UmcParamClick(a)" for="control1">
 
위의 script 태그를 통해 엠베디드 된 스마트클라이언트의 이벤트를 받게 된다.
주목할 것은 두번째 태그이다. 인자가 없는 이벤트는 단지 이벤트 네이밍만 명시하면 되지만, 인자가 있을 경우는 인자로 받을 임의의 변수를 지정해 주도록 하자.
 
그럼 인자를 받는 이벤트의 결과만 보도록 하자.
 
[그림2] 스마트클라이언트가 전달한 이벤트
 
 
UserControl 에서 우리는 다음과 같은 메시지를 전달하였다.
 
Invoke( UmcParamClick, "스마트클라이언트가 전달한 메세지");
 
 
CAS 설정하기
 
브라우져에서 UI 의 이벤트를 전달받기 위해선 CAS 권한 제약을 받는다.
간단한 검색만으로 권한을 추가할 수 있을 거라고 생각한다.
 
 
 
 
재미있는 문제
 
UserControl 의 생성자에 다음과 같은 구문을 추가해 보자.
 
public UserControl1()
{
         InitializeComponent();
 
         button1.Click += new EventHandler(delegate(object sender, EventArgs e) {
                           MessageBox.Show("UI 에서 연결한 이벤트");
                  } );
}
 
신기한 구문이다. 위 구문은 익명 메서드라는 것으로, 메서드 구현하지 않고, 대리자를 통해 메서드의 구현을 위임하는 코드이다.
 
그럼 문제를 내도록 하겠다.
UI 에 브라우져에 엠베디드 될 것이고, HTML 에서 자바스크립트로 이벤트를 구현하는 부분이 있다.
하지만, UI 에 이미 버튼에 대한 이벤트를 연결하였다.
 
그럼 두 이벤트중 누가 먼저 실행될까???
 
처음 나는, 오직 자바스크립트 이벤트만 실행될 거라고 생각했다. 자바스크립트는 C# 코드의 이벤트 등록 연산자(+=) 를 못 알아 먹을거라고 생각했기 때문이다.
 
하지만… 결과는 ㄷ ㅐ 반전이다.
자바스크립트의 이벤트가 먼저 발생되고, C# 코드의 이벤트가 발생한다.
 
결과를 보면 분명, 이벤트라는 것이 마지막에 등록된 이벤트의 꽁무니에 등록이 되어야 당연하다고 생각했는데, 자바스크립트의 script event 구문은 무척이나 당황한 결과를 만들어 내었다.
 
 
마치며
 
점점 스마트클라이언트 프로젝트가 활성화 되고, 언젠가는 스마트클라이언트를 접할 기회가 올 거라고 확신하다. Internet Explorer 에 종속적인 기술이긴 하지만, 점점 사양되는 ActiveX 보다 월등히 닷넷스런(?) 기술이다.
아직까지 이해할 수 없는 배포 관련 이슈들이 남아 있지만, 언젠가는 트러블 슈팅할 날이 올 거라고 생각한다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 야구개폐인 2010.02.04 11:20 Address Modify/Delete Reply

    정말 감사합니다 ㅠㅠ 흑흑흑

  2. ash84 2010.08.31 14:42 Address Modify/Delete Reply

    안녕하세요 ^^

    위의 스마트 클라이언트 소스를 가지고 배포를 해봣는데요,

    왜 익스플로러 상에서 인트라넷이 꺼져 있다고 하면서 계속 복원만 하다가

    아무것도 나오지 않는지 궁금합니다.

  3. 시험모드 2012.05.22 10:01 Address Modify/Delete Reply

    시작부분에..
    일반적으로 ComVisible(true) 로 설정되어 있다면, 우리는 자바스크립트로 public 으로 된 프로퍼티나 메서드 등에 바로 접근 할 수 있다.

    라고 하셨는데... 혹시 프로퍼티나 메서드 에 접근하는 방법 포스팅 해놓으신 부분 있으신가요...?

    그 부분이 필요한데 ㅠㅠ 휙 지나가서 아쉽네요..

  4. 김영대 2012.11.15 19:28 Address Modify/Delete Reply

    정말 감사드립니다 엄준일님 항상 잘 배우고 있습니다.