'개발/ClickOnce'에 해당되는 글 25건

  1. 2009/05/08 ClickOnce 애플리케이션의 자동 시작과 제거 by dalbong2
  2. 2009/04/23 URL에 의한 ClickOnce 애플리케이션 구동시 문제 by dalbong2
  3. 2009/04/23 URL을 이용한 ClickOnce 애플리케이션 구동 절차 by dalbong2
  4. 2009/04/23 [연재 09] ClickOnce 애플리케이션의 필수 프로그램 배포 - 부트스트래퍼(Bootstrapper) by dalbong2
  5. 2009/04/23 [연재 08] ClickOnce 보안 모델( 두번째 이야기 ) by dalbong2
  6. 2009/04/23 [연재 07] 비관리형 파일의 배포 by dalbong2
  7. 2009/04/23 [연재 06] ClickOnce 보안 모델 by dalbong2
  8. 2009/04/23 [연재 05] HOW TO : Upate ClickOnce Application by dalbong2
  9. 2009/04/23 [연재 04] ClickOnce : Look Into The Internals by dalbong2
  10. 2009/04/23 [연재 03] ClickOnce : Enjoy The Appearance 2 by dalbong2
  11. 2009/04/23 [연재 02] ClickOnce : Enjoy The Appearance 1 by dalbong2
  12. 2009/04/23 [연재 01] What is ClickOnce by dalbong2
  13. 2009/04/23 ClickOnce에서 변경되지 않은 어셈블리의 다운 여부 테스트 by dalbong2
  14. 2009/04/23 [연재 03] 원격 데이터 읽기 by dalbong2
  15. 2009/04/23 [연재 02] 로컬 데이터 파일 읽기 by dalbong2
  16. 2009/04/23 [연재 01] 데이터 파일, ClickOnce 애플리케이션과 함께 배포하기 by dalbong2
  17. 2009/04/23 .NET3.0을 부트스트래퍼로 설치시 발생하는 에러 by dalbong2
  18. 2009/04/23 ClickOnce and FireFox by dalbong2
  19. 2009/04/23 MAGE를 이용한 ClickOnce 어플리케이션 게시 by dalbong2
  20. 2009/04/23 ClickOnce에서 필수 구성 요소별로 지원 URL 지정하기 by dalbong2
  21. 2009/04/23 ClickOnce 어플리케이션의 파일 관리(Ⅳ) - 데이터 파일(ii) by dalbong2
  22. 2009/04/23 ClickOnce 어플리케이션의 파일 관리(Ⅲ) - 데이터 파일(i) by dalbong2 (1)
  23. 2009/04/23 ClickOnce 어플리케이션의 파일 관리(Ⅱ) by dalbong2
  24. 2009/04/23 ClickOnce 어플리케이션의 파일 관리(Ⅰ) by dalbong2
  25. 2009/04/23 ClickOnce 인증서 만료 문제 by dalbong2

방명록에 어떤 분이 질문을 올렸는데, 답변이 약간 길어져서 포스트로 올린다.
정신적 여유가 없어서 글이 성의없게 작성되었다는 것을 미리 밝힌다.


음...기본적으로 ClickOnce에서는 어플리케이션을 시작 프로그램(Startup)으로의 등록과 제거를 지원하지 않고 있습니다.

startup등록은 약간의 꽁수면 됩니다. 사실 clickonce와 startup으로 구글링해보면 만족할 만한 결과가 나옵니다.
제 책에 이 내용을 썼던 것 같은데, 기억이 가물합니다( 지금 원고가 어디로 가 버렸는지 찾을 수가 없습니다.-_-;; ).
요는 ClickOnce 어플리케이션을 설치하면 생성되는 shortcut 파일을 Startup 폴더에 복사하는 방법입니다.
다음 링크를 참조하면 코드가 나와 있습니다. 이 문서에는 xp, vista에서의 이슈도 기술되어 있습니다.
http://keithelder.net/blog/archive/2009/04/18/how-to-run-a-clickonce-application-on-startup.aspx
http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/75d2112c-707c-4051-a5fc-eb51802558bb/

근데, 제거에 대해서는 사실 이전에 생각해 본 봐가 없습니다.
오늘 처음으로 님의 글을 보고 심각하게 생각해봤습니다.
startup 폴더로 복사한 shortcut 파일을 삭제해야 하는데 이것을 어떻게 할 수 있느냐가 문제인 것이죠.
프로그램 추가/제거를 통해서 삭제를 하면 일단 ClickOnce가 머신에 복사하고 변경한 파일은 모두 삭제해 주는데 코딩을 통해서 복사한 이 shortcut 파일은 ClickOnce가 모른다는 거죠.

구글링을 해봐도 그에 대한 이슈는 여러명이 제기한 것 같은데 답은 없군요.

근데 그 기능이 꼬옥 필요하다면 궁여지책의 방법이 하나 생각나긴 하는데, 실험 정신(?)이 필요할 것 같습니다.
저도 실은 한번도 테스트해보지 않은 방법입니다.

이런 식의 어플리케이션을 일반 사용자를 대상으로 한다면 다음 방법 또한 쓸모가 없게 됩니다.
인트라넷 애플리케이션인 경우에만 유효하다는 것을 미리 말씀드립니다.

저의 포스트중에서 .NET 카테고리에 가보면 "기본 AppDomain 생성자 변경하기"라는 것이 있는데, 이것을 이용하면 어떨까 하는 생각을 해 봤습니다.
간단히 말하면( 간단히 말할 수 있으려나...) 사용자가 .NET 어플리케이션을 실행하면 그 어플리케이션이 실행되기 전에 .NET의 CRL은 그 어플리케이션이 실행될 공간 즉 AppDomain을 먼저 생성되는데,
이 공간을 생성하는 녀석을 우리가 커스터마이징할 수 있다는 얘기입니다.
이 커스터마이징 코드에서 만약 님이 원하시는 애플리케이션이 삭제되었다는 것을 확인(?)만 할 수 있다면,
예의 shortcut 파일을 삭제하고 AppDomain을 생성하는 작업을 포기하고 그냥 리턴하면 되지 않을까 하는 생각을 해 봤습니다.

AppDomainManager 클래스를 상속해서 InitializeNewDomain()을 상속해서 님의 어플리케이션이 제거되었는지 여부를 확인해서 제거 되었다면 shortcut 파일을 제거하고 그렇지 않다면
AppDomain을 생성하는 base.InitializeNewDomain() 메소드를 호출하면 되지 않을까 되지 않을까하는 편한 생각을 해 봅니다.
AppDomainManager에 대한 설명은 앞에서 말한 필자의 포스트나 MSDN( http://msdn.microsoft.com/en-us/library/system.appdomainmanager_methods.aspx )을 참조하시길 바랍니다.

님께서 방향을 원하셔서 답글은 남깁니다만 개인적인 지적 호기심이라면 모르겠지만, 고객의 요청이라면 먼저 고객과의 합의를 시도해보는 것은 어떨런지.
참내...기술 블로그에서 이런 해결책을 제시하다니...저도 이제 게을러 진건지...죄송합니데.

Posted by dalbong2

1 에러 상황

다음은 현재 달봉이가 투입된 프로젝트에서 발생하는 현상이다.
ClickOnce로 배포된 EXE 타입의 .NET 애플리케이션을 링크 URL 호출을 통해서 구동하는 형태의 시스템 구조이다. 문제는 URL에 첨부한 파라미터가 EXE 애플리케이션으로 전달되지 않는다는 것이다.

http://***/Container/LemContainer.application?userid=lem15

url로 넘기는 GET 파라미터 userid 값이 전달되지 않는다는 것이다. fiddler를 통해서 HTTP 요청을 캡쳐해보니 다음 그림처럼 원래의 요청외에도 계속 다른 요청이 추가되는 것이었다.
1244482260
두번째의 HTTP 요청에는 GET 파라미터가 삭제된다는 것을 확인 할 수 있었는데, 결국 문제는 두번째 요청이 왜 발생하는지에 대한 원인을 밝혀내는 것이었다.

2. 테스트

가상 디렉토리 Container 대신에 새로운 가상 디렉토리 ClickOnce를 생성해서 테스트를 해봤다. 구조는 그림과 같다.

1140340762

새로 구성된 ClickOnce 애플리케이션은 IIS에서 제공하는 거의 디폴트 세팅을 유지하고 있다. 그런 다음 ClickOnce 디렉토리의 파일을 요청했다.

http://***/lem/clickonce/Lem.Win.TestTeam.Winform.application?userid=lem15

HTTP 요청을 캡쳐한 모습은 다음과 같았다.
1365211929

달봉이가 원하는 결과였다.

3 달봉이의 추측

그럼 가상 디렉토리 세팅과 관련된 문제일까?
아니면 ClickOnce 배포 방식과 관련된 문제? 두번째 호출과 세번째 호출 .exe.manifest 호출은 언제 일어나는 것일까?

4 솔루션

달봉이의 두번째 추측이 해답이었다.
Visual Studio.NET 2005 에서는 ClickOnce 게시를 위한 프로젝트 드자이너를 제공하고 있다. Visual Studio.NET의 솔루션 탐색기에서 배포하고 싶은 프로젝트 오른쪽 클릭->속성->게시(publish)탭 을 선택하면 볼 수 있다.
1181499744

ClickOnce 게시 디자이너 폼

이 프로젝트 디자이너 폼에서 옵션 버튼을 클릭하면 다음과 같은 화면이 팝업된다.
1235807062

게시 옵션 창

그림에 표시한 것처럼 "응용 프로그램으로 URL 매개변수가 전달되도록 허용"이라는 옵션이 있는데, 이것을 체크하면 해결된다. 이렇게 하면 다음 코드처럼 배포 메너페스트 파일(.application)에 GET 파라미터를 붙여서 호출하고, 그것을 EXE 프로그램에서 받을 수 있게 된다.
http://***/lem/clickonce/Lem.Win.TestTeam.Winform.application?userid=lem15

5. 과제

문제는 해결하기 했지만, 해결하는 과정에서 ClickOnce 애플리케이션이 구동되는 정확한 메커니즘을 정리해야 겠다는 생각을 다시 한번 더 하게 됐다. 분면 .application 확장자와 관련된 MIME 타입과 MIME 필터가 관여되어 있을 것이라는 추측이다.
Posted by dalbong2

현 프로젝트의 구조 및 구동

달봉이가 현재 투입된 프로젝트에서는 스마트클라이언트 기술을 사용해서 기업의 ERP 시스템을 개발하고 있다. 스마트클라이언트 애플리케이션의 배포 방식은 ClickOnce 방식과 NTD 방식을 혼용하고 있다.

우선 업무 화면의 컨테이너 역할을 하는 MDI 컨테이너 및 공통 모듈은 ClickOnce 방식으로 배포한다. 그리고 업무용 화면은 사용자가 메뉴를 클릭했을 경우 해당 메뉴의 화면이 포함된 어셈블리를 On-Demand 형식으로 NTD를 사용해서 다운하고 있는 것이다. 다음 그림은 현재 프로젝트에서 사용하고 있는 스마트클라이언트 애플리케이션의 구조와 배포 방식을 나타내고 있다.

1360069327

현 스마트클라이언트 시스템의 구조와 배포 방식
이 시스템의 구동은 URL을 통해서 하고 있다. Visual Studio.NET 2005를 통해서 게시를 하면 기본적으로 publish.htm을 제공한다. 이 페이지를 주소창에서 호출하면 클라이언트에 필요한 프로그램들 예를 들어 .NET v2.0이 설치되었는지를 확인하고나서 .EXE 프로그램을 구동시켜준다.

URL에 의한 구동 절차에 대해서 이렇게 두리 뭉실하게 알고 있는 것이 달봉이의 상황이고, 이제 달봉이가 그 내용을 좀 더 상세히 알아보기 위해 삽질을 함 시작해 보려 한다. 어디까지 알 수 있을 지는 모르지만...쩝

삽질 준비

삽을 준비한다.
삽질은 ClickOnce에 대한 연재중의 하나인 ClickOnce : Look Into The Internals을 참고하면 된다.

Posted by dalbong2

프로덕트 XML 파일

부트스트래퍼(Bootstrapper)는 ClickOnce 애플리케이션이 구동되기 전에 애플리케이션에서 필요로 하는 필수 프로그램(prerequisties)이 먼저 설치되어 있는지를 확인하는 작은 부피의 프로그램을 말한다. Visual Studio.NET 2005에는 내장된 부트스트래퍼 기능이 있다. 우리는 이 기능을 사용하여 ClickOnce 애플리케이션이 실행되기전에 필요한 다른 컴포넌트를 설치할 수 있다.
달봉이는 지금까지 알고 있는 방법중에서 이전의 COM 컴포넌트나 .NET 어셈블리나 가리지 않고 클라이언트로 배포를 할 수 있는 가장 쉬운 방법중의 하나로 여기고 있다. 단점은 ClickOnce에서만 가능하다는 것이다.[현재 2006.09.16]

VS.NET의 부트스트래퍼 기능을 사용하기 위해서는 우선 컴포넌트를 설치하는 설치 프로그램(.exe or .msi)을 작성해야 한다. 그런 다음 이 설치 프로그램을 부트스트래퍼에 등록해야 한다. 그러기 위해서는  XML형식의 프로덕트 파일(product file)을 제작해야 한다. 

1200932437 

프로덕트 파일

프로덕트 파일은 ClickOnce 프로그램이 실행되기 전에, 어떤 필수 프로그램들이 설치되어 있어야 하는지를 설명해주는 부트스트래퍼용 manifest 파일을 생성해준다. 프로덕트 파일에는 ClickOnce 애플리케이션이 필요로하는 필수 컴포넌트들을  설치하는 설치프로그램들에 대한 정보를 여러개 포함시킬 수 있다.
이 그림처럼 프로덕트 파일을 생성해서 설치 프로그램과 같이 적절한 위치에 두면  ( 달봉의 경로는 다음과 같다. C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages ) 아래 그림처럼 VS.NET의 게시 디자이너에서 필수 구성 요소 버튼을 클릭했을때 선택할 수 있는 박스에 나타나게 된다. 이제 필요한 프로그램을 선택해서 게시를 하면 된다.

1197118260

VS.NET에서 필수 구성 요소 선택
(파일을 클릭하면 선명한 그림이 뜹니다)

이제 이 프로덕트 XML 파일을 제작하는 방법을 알아본다.

프로덕트 파일 제작

프로덕트 파일을 제작하기 위해서는 부트스트래퍼가 이해할 수 있는 규칙에 맞도록 작성해야 한다. 이런 XML 스키마는 다음 링크에서 찾아볼 수 있다.
Product and Package Schema Reference - msdn
http://msdn2.microsoft.com/en-us/library/ms229223.aspx

그렇지만 이 스키마를 이해하고 직접 손으로 작성하라고 하면 MS에 짜증이 날것이다. 이런 스키마가 있다면 이런 것을 제작할 수 있는 편집기도 주면 좋잖은가. 근데 아직 공식적으로 출시된 유틸 프로그램은 없는 것으로 안다. 다행히도 이 XML 파일을 생성해주는 편집 툴을 제공하는 훌륭한 분이 계시다.

프로덕트 파일 생성 툴
ClickOnce Bootstrapper Manifest Generator

자세한 내용은 Powertoys blog에서 볼 수 있다.

이제 이 툴을 사용하는 방법을 소개한다. 툴을 실행시키고 File->New를 선택한다.

1230043952

Package Manifest 프로젝트 타입을 선택한다.

1363539001

1329959148

그림처럼 package 노드가 하나 기본적으로 생성되면 적절하게 package 이름을 입력한다. 이 이름은 C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages 아래에 생성되는 폴더명이 될 것이다.

1125104282

그런 다음 위 그림처럼 "Add Install File" 버튼을 클릭하면 아래처럼 설치파일을 추가할 수 있는 창이 뜬다.

1104026117
적절한 설치파일을 검색해서 선택한다. 이 설치 파일은 ClickOnce 애플리케이션이 실행되기 전에 미리 설치되어 있어야 하는 컴포넌트를 클라이언트 PC에 설치하게 될 것이다. 달봉이는 임시로 iis60rkt.exe라는 파일을 선택하고 있다. ClickOnce 애플리케이션과는 전혀 상관없는 셋업 파일이긴 하지만.

1022242726

그런 다음 적절한 display 명을 준다. 이 이름은 VS.NET의 필수 구성요소 선택하는 창에 출력될 것이다.

1104462412

그런 다음 System Checks 탭을 선택한다. 이 탭에서는 이 컴포넌트가 이미 설치되었는지를 체크하는 로직을 선택할 수 있다. 설치 여부를 체크하는 로직은 여러가지를 제공하고 있다. 그림의 아이콘에 커서를 올려놓으면  설치 여부를 체크하는 로직으로
"File Check",
"Registry Check",
"Registry File Check",
"MSI Product Check",
"External Check",
"Assembly GAC Check"

등이 있음을 알 수 있다. File Check라는 것은 앞의 그림처럼 "어떤 경로의 폴더에 지정한 파일이 있으면 이미 이 PC에는 원하는 셋업이 이뤄져있다"는 것으로 간주하겠다는 것이다. 레지스트리에 특정 값이 있는지를 판단해서 설치여부를 판단할 수도 있다. 시스템 체크 로직에 대한 다른 구체적인 방법에 대해서는 앞에서 알려준 MSDN 링크 페이지를 통해서 알아보길 바란다.
달봉이는 단순히 그림과 같은 경로에 iisTools.chm 파일이 있는지를 판단해서 그 결과값을 IIS60rktInstalled에 받아 두도록 하고 있다.
1108723164

다음은 Install Conditions 탭을 선택한다. 이곳에서는 System Checks 탭에서 시스템의 상태를 체크한 값을 이용해서 값에 따라서 어떻게 할 것인지를 설정할 수 있다. 그림은 앞에서 설정된 IIS60rktInstalled 값이 0보다 크거나 같으면 그냥 설치를 통과하도록 설정하고 있다. IIS60rktInstalled 같은 변수에 어떤 값이 저장될 지는 시스템 체크 로직에 따라 다르다. 이런 구체적인 값도 앞에서 알려준 msdn 링크를 참조하기 바란다.

1053607751

다음은 Exit Codes 탭을 선택한다. 설치 파일이 exit code를 리턴하고 그것에 따라 실패, 성공, 재부팅들을 설정해줄 수가 있다. 달봉은 exit code에 대한 설정을 해 주지 않고 있다. 다만 아래 부분을 보면 "Use Default System Exit Codes"가 선택되어 있고 결과값으로 Fail이 선택되어 있는데, 이것은 설치 후 상태를 기본적으로 설치 실패로 설정하겠다는 것이다. 우리는 이것의 체크를 없애 줘야 한다.  

1229986605

그런 다음 저장 버튼을 클릭하면 지금까지 설정한 내용을 저장할 수 있다. 이때 저장된 내용은 우리가 원하는 xml 형식의 프로덕트 파일은 아니다. 이렇게 저장된 내용은 언제든지 다시 불러와서 재 편집을 할 수 있다(근데, 이 기능이 아직은 제대로 작동하지 않는듯하다)
1230226784

우리가 원하는 xml의 프로덕트 파일을 최종적으로 만들어 내기 위해서는 위 그림처럼 "Project->Build" 메뉴를 선택해야 한다. 이 메뉴를 선택하고 나면 아래 그림처럼 결과가 나타난다.

1147821584

이 그림은 최종적으로 프로덕트 파일이 저장된 경로(기본적으로 C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages )와 프로덕트 파일을 생성하는과정에 남긴 로그를 보여주고 있다. 프로덕트 파일의 저장 경로는 "Project->Properties"에서 변경할 수 있다.

이렇게 해서 최종적인 프로덕트 파일이 생성되었고 적절한 폴더에 위치시켰다. 이제 VS.NET을 실행시키고 게시 디자이너를 통해서 필요한 설치 프로그램을 선택한 후 게시를 하면 된다.

--------------------------------------------------------------------------------

참조 문서

Product and Package Schema Reference - msdn
http://msdn2.microsoft.com/en-us/library/ms229223.aspx
ClickOnce Deployment Technology - Peter Bromberg
http://www.wise.com/Library/ClickOnce.pdf
ClickOnce : Bringing Ease and Reliability to SmartClient Deployment
http://www.code-magazine.com/article.aspx?quickid=0601041&page=1
msdn : Deploying applications
http://msdn2.microsoft.com/en-us/library/6hbb4k3e(VS.80).aspx

Posted by dalbong2

■ ClickOnce 애플리케이션의 권한 설정

VS.NET으로 ClickOnce용 애플리케이션을 게시할 때 사용하는 프로젝트 디자이너를 다시 보여주고 있다. 그림은 보안 페이지에서 설정하는 모습이다.
1203533187
달봉이는 처음에 이 설정이 계속 마음에 걸렸고, 결국은 달봉이가 이 그림이 뭔지 이해하지 못하고 있다는 것을 알게 되었다.

달봉이가 처음 의문을 갖게 된 사연을 보면 다음과 같다. 먼저, “ClickOnce 보안 설정 사용”이라는 선택을 하고 있는데, 이것이 뭔지 이해가 안 갔다. “ClickOnce 보안”이라는 것이 별도로 있다는 것인가? 그리고 “완전 신뢰 응용 프로그램”과 “부분 신뢰 응용 프로그램”이라는 말도 이해가 가지 않았다. 기존의 모델이라면 “완전 신뢰 어셈블리”, “부분 신뢰 어셈블리”이어야 할 것이다. 거기다 “부분 신뢰의 응용 프로그램”을 선택한 경우는 “응용 프로그램에 필요한 권한”을 선택할 수 있는 리스트가 활성화된다.

앞 뒤 설정 내용을 근거로 해서 다음과 같은 추측을 했다. “ClickOnce 애플리케이션은 권한이 애플리케이션(정확히 말하면 애플리케이션 도메인)에 부여되는 것은 아닐까”. 만약 그렇다면 보안 탭을 통해서 필요한 권한이 설정되면 CodeGroup별 멤버 조건을 설정하고 권한 집합을 설정하는 작업을 하지 않아도 되지 않을까 하는 생각을 해 봤다. 그래서 .NETv2.0 구성 관리자를 실행시켜 특정 CodeGroup을 삭제해봤다. 그 코드 그룹은 이전에 IE 임베딩 방식의 스마트클라이언트에서 추가했던 것이었다. 예상대로 ClickOnce로 배포된 애플리케이션은 실행이 여전히 잘 되었다.

테스트후, 달봉이는 “ClickOnce 보안”이라는 것에서는 “애플리케이션 도메인(AppDomain)” 기반 즉 권한이 도메인에 부여될 것이다는 추측을 하게 되었다. 그러나 심증만 있을 뿐 아직 그것을 뒷받침해줄만한 문서를 보지 못했다. 그래서 근 한달여간을 가슴 한켠에 뭔가가 무겁게 짓누르고 있는 듯한 답답함을 가지고 지내야 했다. 근데, 어제 퇴근길 지하철에서 읽은 블로그 문서들이 달봉이의 답답함을 풀어줬다. 그 시원함이란! 크으~~

참조 문서의 링크를 보면 그 내용을 볼 수 있다. 이 블로그에서는 “AppDomain 중심의 보안 모델”을 “Sandboxed AppDomain”이라는 말로 표현하고 있다. 심증을 확인해 주는 문서들이다.

■ 요약

▶ v1.x의 보안 모델

증거와 보안 정책 기반의 이전 모델은 어셈블리 중심의 권한 부여 사고 방식이다. 이것을 요약하면 다음과 같다.

“어셈블리가 가지고 있는 증거들을 기반으로 해서 4레벨(엔터프라이즈, 컴퓨터, 사용자, 애플리케이션)의 보안 정책을 적용함으로써 어셈블리에 부여되는 권한을 결정한다”

▶ v2.0에 추가된 보안 모델

v2.0에서는 v1.x의 보안 모델외에도 추가된 모델이 있다. 이 추가된 v2.0 보안 모델에서는 애플리케이션이 정상적으로 작동할 수 있는 권한(permission set)을 요청하게 되고 그 요청이 수락되면 정확히 요청한 권한만 애플리케이션에 부여된다. 그 후 애플리케이션의 도메인으로 로딩되는 모든 어셈블리들은 부여받은 그 권한내에서만 작동을 하게 된다. 즉 애플리케이션의 샌드박스(sandboxed AppDomain)이 조성되는 것이다. 만약 애플리케이션이 요청한 권한이 정상적인 CAS 정책에 정의된 것보다 많은 것을 요구하게 되면 사용자의 승인을 요하는 보안 대화상자가 뜨게 된다. 사용자가 승인(elevation of permissions) 을 하게 되면 그 내용은 매너페스트 파일에 저장되어, 다음부터는 대화상자가 뜨지 않게 된다.

“권한이 부여되는 대상은 AppDomain이다. 그리고 그 AppDomain으로 로딩되는 모든 어셈블리는 그 AppDomain의 권한을 부여받게 되는 것이다.”

ClickOnce 배포를 사용하는 애플리케이션은 이런 애플리케이션 샌드박스 보안 모델이 적용된다. 즉 ClickOnce 배포를 사용하게 되면, 이전 모델에서처럼 클라이언트측 PC의 CAS 설정을 위하여 MSI 파일같은 것을 사용하지 않아도 된다. ClickOnce 애플리케이션을 게시할때 필요한 권한을 설정해서 배포 서버로 게시하면 된다.

앞의 그림은 Debug-In-Zone 기능이라는 것을 보여주고 있다. 개발자가 ClickOnce 애플리케이션을 개발할때 애플리케이션이 배포될 Zone을 선택하고 그리고 필요한 권한을 설정해서 최종 배포되기 전에 미리 sandboxed된 환경에서 테스트해 볼 수 있는 기능을 제공하는 것이다.

테스트 결과 애플리케이션이 수행되는데 충분한 권한이 주어졌다고 생각되면 해당 권한 설정을 저장한다. 그런 상태에서 클라이언트 PC로 배포되면 설정된 권한을 클라이언트에 요청할 것이고 만약 그것이 정상적인 권한을 넘어서는 요청이라면 사용자에게 대화창을 띄워 승인을 얻게 되는 것이다.

개발 당시는 "ClickOnce 보안 설정 사용"을 체크하지 않는 것이 편할 것이다. 이렇게 하면 sandbox 환경이 없어져서 일반 로컬 애플리케이션와 같은 환경에서 개발할 수가 있게 된다.

■  미해결

AppDomain 단위의 Sandbox에 대해서도 아직 궁금한게 많이 있다. 예를 들면, AppDomain에도 증거(evidence)라는 것이 있단다. 이 AppDomain에 대한 증거는 어디에 사용되는지도 아직 알 수 없다. AppDomain을 생성하는 API인데 이것을 봐도 AppDomain은 Evidence타입의 객체를 필요로 한다.

AppDomain.CreateDomain( string friendlyName,
                      Evidence securityInfo,
                      AppDomainSetup info,
                      PermissionSet grantSet,
                      params StrongName[] fullTrustAssemblies);

"A Closer Look at the Simple Sandboxed AppDomain"를 보면  다음과 같은 내용이 있다.

Technically the domain itself doesn't need that parameter, and it won't ever look at it directly.  However, the evidence is still stored on the AppDomain's Evidence property.  This means that other features which make decisions based upon AppDomain evidence (such as isolated storage), can still work with code executing in a simple sandboxed domain without modification.

증거(evidence)가 권한 부여 조건 말고도 다른 부분에 사용된다는 것인지, 그리고 참조 문서의 블로그를 읽다보면 AppDomain에도 증거가 부여된다는 것을 알 수 있는데

그러나 지금은 “Sandboxed AppDomain”라는 모델이 있다는 것만으로도 달봉이의 많은 의문이 풀렸다.

참조 문서

The Simple Sandboxing API
http://blogs.msdn.com/shawnfa/archive/2005/08/08/449050.aspx
A Closer Look at the Simple Sandboxed AppDomain
http://blogs.msdn.com/shawnfa/archive/2005/08/09/449563.aspx
5 Reasons to Choose Simple Sandboxing
http://blogs.msdn.com/shawnfa/archive/2006/04/19/579066.aspx

Posted by dalbong2

▶ .NET 애플리케이션에서도 여전히 비관리형 파일을 사용하는 경우가 있다. 다음 url의 문서들은 unmanaged 파일을 배포하는 방법에 대해서 설명하고 있다.


참조 문서

Versioning/Deploying Unmanaged Files - Suzanne Cook's blog
http://blogs.msdn.com/suzcook/archive/2004/10/28/249280.aspx

HOW TO: Deploy COM Interop Programs to Internet Explorer - msdn
http://support.microsoft.com/?kbid=311297

Escape DLL Hell
Simplify App Deployment with ClickOnce and Registration-Free COM - Dave Templin
http://msdn.microsoft.com/msdnmag/issues/05/04/regfreecom/default.aspx

The Limitations of Reg-Free COM
http://msdn.microsoft.com/msdnmag/issues/05/04/regfreecom/default.aspx?side=true#a



▶ 비관리형 COM등을 배포하는 방법으로 ClickOnce의 부트스트래퍼 기능을 이용해 볼 수도 있을 것이다. 다른 포스트를 참조하길 바란다.

Posted by dalbong2

어셈블리가 로딩될 때, 어셈블리의 근원지가 로컬 PC가 아닌 경우 즉 로컬 PC의 외부에서 유입된 모든 어셈블리에서 대해서는 기본적으로 CAS가 적용된다. 달봉이는 처음에 보안(권한)과 관련해서, 로컬 PC에서 실행되는 일반 애플리케이션과 “온/오프라인용” 모드로 설치된 ClickOnce의 차이점이 궁금했다. ClickOnce 애플리케이션은 “온/오프라인용”으로 설정되면 로컬에 캐시된 애플리케이션이 구동된다. 이런 경우 스마트클라이언트 애플리케이션은 로컬 PC에서 구동되는 애플리케이션과 어떻게 구분되어서 적절한 보안 정책을 적용시킬 수 있게 되는 것일까? 이 포스트를 읽고 나면 답이 나올 것이다.

■ ClickOnce 애플리케이션 권한 설정

ClickOnce 애플리케이션의 권한은 애플리케이션 메너페스트(.exe.manifest)에 기술되어 있다. 애플리케이션이 부여받는 권한은 정확히 매너페스트에 기술된 것만 부여받는다. 그 이상도 이하도 아니다.
VS.NET2005에서는 프로젝트 디자이너의 보안 페이지에서 ClickOnce 애플리케이션의 권한을 설정할 수 있다. 
1350716813

VS.NET의 기본 설정

VS.NET2005에서는 기본적으로 애플리케이션이 FullTrust 권한을 필요로 하는 것으로 설정된다. “ClickOnce 보안 설정 사용” 체크박스가 선택되어 있고, “완전 신뢰 응용 프로그램”이 선택되어 있어서 FullTrust 권한을 선택하고 있다.
그러나 개발시에는 권한 관련한 설정을 모두 해제하고 작업을 하는 것이 편할 수도 있다. 그런 경우 “ClickOnce 보안설정 사용”을 해제하면 된다.
FullTrust도 필요한 경우도 있겠지만, 더 보안이 강화된 프로그램을 만들기 위해서 필요한 권한만을 요청하도록 설정할 수 있다.  다음 그림은 개발자가 직접 필요한 권한을 선택하는 것을 보여주고 있다.
1089587048 

필요한 권한 선택

“부분 신뢰 응용 프로그램”을 선택하면 비활성화된 영역 선택 부분과 권한 선택 부분이 활성화된다. 영역 선택의 콤보 박스에서는 애플리케이션을 게시할려고 하는 영역을 선택한다. 이렇게 하면 VS.NET은 영역에 부여된 기준 권한을 미리 알 수 있게 된다. VS.NET은 개발자가 선택한 권한이 개발자가 선택한 영역의 기준 권한을 벗어나지 않도록 할 수 있다.
녹색 체크 표시의 “포함” 컬럼은 해당 권한이 애플리케이션에 주어졌다는 것을 말한다. 개발자는 이 컬럼을 선택해서 “포함” 또는 “제외”를 선택할 수 있다. 또한 “속성…”버튼을 클릭하면 해당 권한에 대한 좀 더 자세한 설정을 할 수는 창이 뜬다. 
1135582803

권한 설정 창

이렇게 권한을 개발자가 직접 하나씩 설정해도 되지만, 자동 계산해주는 방법도 제공해주고있다. “권한 계산” 버튼을 클릭하면 현재 애플리케이션에서 필요로 하는 권한을 자동 계산할 수 있다. 이런 최종 권한 설정은 애플리케이션의 배포 메너페스트(.exe.manifest)에 기록된다.
이제 사용자가 ClickOnce 애플리케이션을 설치할 때, 매너페스트 파일에 기록된 대로 애플리케이션에 권한이 부여될 것이다. 그러나 매너페스트에서 요청하는 권한이 정상적인 CAS 정책에 정의된 것보다 더 많은 권한을 요구할 때는 사용자에게 애플리케이션이 요청한 권한을 부여할 것인지(elevation of permissions)를 확인하는 대화창이 뜬다. 사용자가 허락을 하게 되면 필요한 권한 설정이 추가되어 저장된다. 그래서 이후 실행시는 같은 대화창이 뜨지 않는다.

■ ClickOnce 애플리케이션의 런칭

애플리케이션이 권한에 맞도록 설치되었다. 이제 실행시킬 일만 남았다. 사용자는 적절한 URL(publish.htm , .application)을 호출해서 애플리케이션을 실행시킬 수도 있고 또는 “시작” 메뉴에 있는 바로가기 메뉴를 클릭해서 호출할 수도 있을 것이다. 바로 가기 메뉴를 클릭하면 확장자가 .appref-ms인 파일이 호출된다. 이 파일에는 .application 파일에 대한 바로가기 값이 있다.
사용자가 ClickOnce 애플리케이션을 호출할때 완전 신뢰 애플리케이션과 부분 신뢰 애플리케이션에 따라 구동 절차가 달라진다. 만약 완전 신뢰의 애플리케이션이라면 일반 로컬 PC의 애플리케이션과 동일하다. 즉 CAS 정책을 받지 않는다.
그러나 부분 신뢰의 애플리케이션에서는 호출되는 ClickOnce 애플리케이션이 구동되기 전에 AppLaunch.exe 이 실행된다. 
1318438464

ClickOnce 애플리케이션 구동 절차

일반 로컬 애플리케이션은 애플리케이션 자체가 독립적인 프로세스로 구동된다. 그러나 ClickOnce 애플리케이션을 구동하면 즉 .application 파일이나 .appref-ms 파일을 호출하면 먼저 dfshim.dll 파일을 거치게 된다. 이것은 윈도우 탐색기의 "도구->폴더옵션" 메뉴를 확인하면 알 수 있다. 
1223387852

ClickOnce 애플리케이션 파일 형식 구동 #1

1099820944 

ClickOnce 애플리케이션 파일 형식 구동 #2

1086896538 

ClickOnce 애플리케이션 파일 형식 구동 #3

그림은 .application 확장명을 클릭하면 rundll32.exe 명령어에의해 dfshim.dll이 호출됨을 보여주고 있다. .appref-ms 파일을 클릭해도 마찬가지다.
dfshim.dll은 ClickOnce 애플리케이션이 FullTrust 권한을 갖는다면 일반 로컬 애플리케이션처럼 실행시키지만 만약 부분 신뢰의 애플리케이션이라면 일단 AppLaunch.exe를 구동시키고 그런 다음 그것이 ClickOnce 애플리케이션을 로딩하고 호스팅하게 된다. 이것은 작업관리자(task manager)를 이용해서 쉽게 확인해 볼 수 있다. 이렇게 구동된 AppLaunch.exe는 ClickOnce 애플리케이션의 보안 샌드 박스(Security Sand Box)역할을 한다. 애플리케이션에 부여된 권한 밖의 일을 못하도록 하는 제어를 하게 된다.

■ 애플리케이션 인증

ClickOnce 애플리케이션이 배포되기 위해서는 매너페스트 파일들이 인증서를 이용해서 서명이 되어야 한다. 공인된 인증 기관으로부터 인증서를 구할 수 있는 상황이 아니라면 예를 들어 개발단계에서라면 테스트용 인증서를 만들어서 사용하면 된다. .NET 프레임워크 SDK에 포함된 MakeCert.exe 프로그램을 사용할 수도 있고, VS.NET을 사용해서도 쉽게 만들 수 있다. 다음은 EXE 프로젝트의 프로젝트 디자이너에 있는 서명 페이지를 보여주고 있다. 
1315509090

서명 페이지

만약 인증기관으로부터 받은 인증서가 있다면 서명 페이지에서 "저장소에서 선택..." 또는 "파일에서 선택..."을 이용해서 인증 정보를 가져오면 된다. 공인된 인증서가 없다면 "테스트 인증서 만들기..."버튼을 이용해서 테스트용 인증서를 만들 수 있다. 이 버튼을 클릭하면 다음과 같은 대화창이 뜬다. 이곳에 인증서를 만들기 위한 적절한 암호를 입력하고 확인을 하면 임시로 사용할 수 있는 인증서(.pfx)가 생성되고 정보가 인증서에 대한 내용이 자동으로 채워진다.

1179097507 

테스트 인증서 암호 입력

1025995781 

생성된 테스트 인증서

이렇게 설정을 마친 후 게시를 하면 애플리케이션의 매너페스트 파일(.application, .exe.manifest) 파일이 테스트용 서명이 추가된다. 애플리케이션의 매너페스트 파일만 서명하는 것이지 모든 어셈블리 파일에 적용되는 것은 아니다. 다들 잘 알겠지만, 인증서에 의한 서명은 CAS와는 상관없는 보안 정책이다.

부연. Authenticode 서명 vs. Strong Name 서명

하나의 어셈블리는 Authenticode 서명과 Strong Name 서명 모두를 가질 수 있다. 두 서명이 모두 게시자를 검증하기 위한 방법이긴 하지만 그 검증 시나리오는 사뭇 다르다. 또한 두 서명을 모두 가지고 있는 어셈블리의 경우 로딩과 보안 검증에 있어서도 재밌는 현상들이 있다. 지적 유희를 좋아하시는 분들은 다음 블로그를 참고하시면 된다.
Authenticode and Assemblies - shawnfa
http://blogs.msdn.com/shawnfa/archive/2005/12/13/502779.aspx
Athenticode 서명의 개념이 필요하다면  "Introduction to code signing"을 참조하라.



참조 문서

NewSecurityFeatures in .NET 2.0 - Pierre Nallet
http://www.theserverside.net/tt/articles/showarticle.tss?id=NewSecurityFeatures
What is new for security in .NET 2.0 - Pierre Nallet
http://www.develop.com/downloads/NEW_dotNETsec_v071.pdf
ClickOnce and Security - Pierre Nallet
http://develop.com/us/downloads/DM_Clickonce.pdf
.NET Security Blog
http://blogs.msdn.com/shawnfa/archive/category/2864.aspx
Authenticode and Assemblies
http://blogs.msdn.com/shawnfa/archive/2005/12/13/502779.aspx
ClickOnce Manifest Signing and Strong-Name Assembly Signing Using Visual Studio Project Designer's Signing Page
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/ClickOnceVS05.asp
Authenticode - msdn
ClickOnce Deployment Technology - Peter Bromberg
http://www.wise.com/Library/ClickOnce.pdf
ClickOnce : Bringing Ease and Reliability to SmartClient Deployment
http://www.code-magazine.com/article.aspx?quickid=0601041&page=1
Deploying applications - msdn
http://msdn2.microsoft.com/en-us/library/6hbb4k3e(VS.80).aspx
ClickOnce 배포 및 보안 - msdn(한)
http://msdn2.microsoft.com/ko-kr/library/76e4d2xw.aspx
ClickOnce 배포 및 Authenticode - msdn(한)
http://msdn2.microsoft.com/ko-kr/library/ms172240.aspx
Posted by dalbong2

이곳에서는 ClickOnce 애플리케이션의 업데이트와 롤백(roll-back) 그리고 업데이트를 선택적으로 하거나 필수적으로 설정할 수 있는 방법에 대해서도 알아본다.

1. 업데이트

2. 버전 롤백

3. 선택적 업데이트







참조 문서
ClickOnce Deployment Manifest - msdn
http://msdn2.microsoft.com/en-us/k26e96zf.aspx
http://msdn2.microsoft.com/en-us/t7050f3w(VS.80).aspx

ClickOnce Deployment Technology - Peter Bromberg
http://www.wise.com/Library/ClickOnce.pdf
ClickOnce : Bringing Ease and Reliability to SmartClient Deployment
http://www.code-magazine.com/article.aspx?quickid=0601041&page=1

Deploying applications - msdn
http://msdn2.microsoft.com/en-us/library/6hbb4k3e(VS.80).aspx
Posted by dalbong2

앞의 포스트까지는 "보이는대로, 하라는대로" 했다. 이제 그 이면에서 무슨 일들이 일어나는지에 포커스를 두겠다. 주로 다음 3가지 주제를 다루려고 한다.
- ClickOnce와 관련된 메너페스트 파일(애플리케이션 메너페스트, 배포 메너페스트)
- 클라이언트측 캐시
- 애플리케이션 구동 메커니즘

1. 메너페스트 파일

Visual Studio.NET 2005를 이용하면 애플리케이션에 포함된 파일을 지정된 곳으로 복사할뿐만 아니라 몇 개의 메너페스트(manifest)[각주:1]  파일을 자동 생성해준다 : 배포 메너페스트(.application), 애플리케이션 메너페스트(.exe.manifest). ClickOnce 애플리케이션은 인스톨 패키지가 없이 이 두 메너페스트 파일에 애플리케이션 인스톨에 필요한 모든 정보가 기술되어 있다. 이 두 파일에 기술되어 있는 정보를 읽어들어 그대로 수행하는 것은 .NETv2.0이 한다. 메너페스트 파일, .NET v2.0 그리고 IIS 웹 서버 그리고 MIME 타입, MIME 타입 핸들러( MIME 필터)등은 ClickOnce 배포의 근간을 이루는 구성 요소이다.

배포 메너페스트(deployment manifest)
은  다음과 같은 정보가 있는 XML 파일이다.
- 클라이언트가 사용할 애플리케이션의 버전 정보
- 언제 업데이트를 할 것인지, 업데이트는 필수인지 선택적인지 등 업데이트 기준. 선택적이라면 사용자가에 업데이트를 할지 프로프트 대화창이 뜬다.
- 현재 버전의 애플리케이션 메너페스트 파일의 위치
- 확장자 : .application
업데이트 설정은 온라인 전용의 애플리케이션에서는 사용할 수 없다. 업데이트에 대한 자세한 내용은 뒤에 오는 포스트에서 다루겠다.

애플리케이션 메너페스트(application manifest)
파일의 다음과 같은 정보가 있다.
- 애플리케이션을 구성하는 어셈블리 목록과 파일 목록
- 어셈블리의 full name 정보( 어셈블리명, 버전 등 )
- 애플리케이션에서 필요로 하는 권한 정보
- 확장자 : .exe.manifest
ClickOnce 런타임은 참조되는 어셈블리를 알아내기 위해서 .NET의 CLR(어셈블리 메너페스트)를 검색하지 않고 바로 애플리케이션 메너페스트에 있는 정보를 이용하는 구조로 되어 있다. 생각해보면 당연하겠지만 애플리케이션 파일은 버전별로 별도의 디렉토리에 존재한다.
ClickOnce 애플리케이션은 이 두 메너페스트 파일에 애플리케이션 타입(온, 오프라인) 그리고 초기 배포, 업데이트에 대한 모든 정보가 들어있다. ClickOnce 애플리케이션의 배포와 업데이트는 모두 메너페스트 기반(manifest-driven)으로 이해하는 것이 가장 좋은 이해 방식일 것 같다. 이제 이 두 메너페스트 파일을 .NET 프레임워크에서 어떻게 사용하는지를 알아보자.

2. 애플리케이션 구동 메커니즘

이제 애플리케이션이 구동되는 절차를 좀 자세히 알아보자. 클라이언트 PC가 웹 서버에 ClickOnce 애플리케이션을 요청을 하는 것부터 시작하는 절차를 그림으로 그렸다.1402209406

ClickOnce 애플리케이션 구동 절차
그림을 보면 클라이언트 PC에서는 두 단계의 작업이 일어난다.


부트스트래퍼 수행 단계 : setup.exe 실행
ClickOnce 엔진 수행 단계 : dfsvc.exe 실행


1) 부트스트래퍼 실행

ClickOnce 애플리케이션이 클라이언트 PC에 설치되는 것은 결국 서버에 배포 메너페스트 파일.application을 요청하면서부터이다. 이때부터 애플리케이션이 활성화된다고 볼 수 있다. .application 파일이 호출되는 두 가지 방법이 있다. setup.exe를 호출하면 setup.exe는 자기 할일을 한 후 .application 파일을 호출해준다. 또는 직접 .application 파일을 호출할 수도 있다.
setup.exe가 수행되는 단계는 ClickOnce 배포 단계에 반드시 포함되는 단계는 아니다. 만약 클라이언트 PC에 원하는 버전의 .NET 프레임워크가 설치되어 있는 상태에서, 필수 프로그램으로서 .NET 프레임워크만이 선택되었다면 setup.exe는 수행되지 않을 것이다. publish.htm을 요청할때 이미 서버측에서는 HTTP 요청으로 올라오는 헤더 부분을 통해서 클라이언트에 .NET 프레임워크가 설치되었는지를 알 수 있다. 따라서 publish.htm 파일을 만들어서 내려보낼때 "실행"버튼을 클릭하면 setup.exe 대신에 바로 .application 파일을 요청하도록 링크를 수정한다. 그러나 만약 .NET 프레임워크 외에 다른 필수 프로그램도 선택했다면 "실행"버튼을 클릭하면 항상 setup.exe 프로그램이 실행될 것이다.
따라서 실전 프로젝트에서는 애플리케이션을 최초로 호출하는 사용자를 위한 인스톨 페이지와 그리고 인스톨을 정상적으로 마친 사용자를 위한 구동 페이지를 분리하는 것이 적절한 시나리오일 것으로 보인다.
인스톨 페이지에서는 반드시 부트스트래퍼를 사용할 필요는 없을 것이다. 다른 방식(MSI, 셋업 프로그램)을 사용해도 될 것이다. 그리고 구동 페이지에서는 반드시 .applicatoion 페이지를 호출하도록 하는 것이다. 많은 경우 구동 페이지가 로그인 페이지가 될 것이다. 즉 로그인이 완료된 사용자에게 .application을 반환해 줌으로써 애플리케이션이 구동되도록 하는 것이다.

2) 배포 메너페스트 다운

ClickOnce 엔진 수행단계를 보면 일단 서버로부터 .application 페이지가 반환되는 때부터 시작된다. .NETv2.0이 클라이언트에 설치되어 있다면 .application 확장자에 대한 MIME 타입으로 application/x-ms-application이 설정되어 있고 그리고 이 MIME 타입에 대한 핸들러는 .application 파일을 ClickOnce 엔진에게 넘겨준다.

중요한 것은 배포 매너페스트 파일과 애플리케이션 메너페스트 파일은 같은 곳에 있을 필요는 없다는 것이다. 예를 들어 배포 매너페스트 파일은 CD를 통해서 사용자에게 배포될 수 있다. 그것이 활성화될때 ClickOnce 엔진이 가동되고 ClickOnce엔진은 다시 배포 매너페스트에 설정된 애플리케이션 매너페스트 파일을 찾는다.

3) ClickOnce 엔진 수행 단계

ClickOnce 엔진은 dfsvc.exe 프로세스를 말하는데, 이 프로세스는 업데이트 확인 및 다운로드를 처리하는 실제 "ClickOnce" 엔진이다. 이 프로세스는 "ClickOnce" 응용 프로그램의 업데이트가 필요한 경우에 실행되며, 15분간 사용되지 않으면 저절로 종료된다. 
exe 애플리케이션을 직접 호출해서 구동시키는 애플리케이션은 ClickOnce 애플리케이션이 아니다. 즉 ClickOnce 엔진이 시작되지 않는다.

ClickOnce 엔진은  다음과 같은 일들을 한다.
-  애플리케이션은 어떤 권한(permission)들을 필요로 하는가?
- 권한이 필요하다면 사용자에게 프롬프트 대화창을 띄운다.
- 애플리케이션이  (새로운) 매너페스트(.exe.manifest) 파일을 다운받는다. 만약 새롭게 게시되지 않은 애플리케이션이라면 애플리케이션 매너페스트 파일은 다운되지 않는다.
- 새로운 애플리케이션 메너페스트 파일을 다운받았다면, 그 파일에 기록된 어셈블리와 파일들을 다운받는다.

4) 관련 대화창

애플리케이션 파일들이 다운되는 동안 몇가지 대화창이 뜰 수 있다.
- Windows XP SP2 경고창
- 소프트웨어 라이센스 동의 창
- 알려지지 않은 게시자를 알리는 창
1042469086 

보안경고

1397239259 

IE_알려지지 않은 게시자

1274940444 

애플리케이션_알려지지 않은 게시자
이 대화창에 대한 이야기는 뒤에 오는 보안 관련 포스트에서 한번 더 언급할 기회가 있을 것이다.

5) ClickOnce 애플리케이션 캐시

다운된 애플리케이션 파일들은 클라이언트 PC에 캐싱되는데, 이 캐시는 NTD에서 사용하고 있는 캐시와는 별도의 공간을 갖는다.
1277978241 

NTD 어셈블리 다운로드 캐시


ClickOnce 애플리케이션 파일들이 저장된 모습을 보고 싶다면 우선 숨겨진 파일 및 폴더를 보이도록 설정한다.
1251126565 

숨겨진 파일 및 폴더 표시 설정

그리고 탐색기를 통해서 다음 경로의 하위 디렉토리를 보면 다운된 파일을 모습을 볼 수 있다.

C:\Documents and Settings\로그인사용자계정\Local Settings\Apps\2.0\XXX

1000620820 

ClickOnce 애플리케이션 캐시

EXE 파일 및 기타 필요한 어셈블리는 이렇게 일단 클라이언트 PC로 다운된 후 실행된다. 내공있는 독자라면, 애플리케이션 베이스 디렉토리가 NTD 배포와는 다르게 로컬 디렉토리를 가리키고 있을것이라는 추측이 스치고 있을지도 모르겠다. 그렇다. ClickOnce 애플리케이션의 베이스 디렉토리는 로컬 경로를 갖기 때문에 퓨전(fusion)은 모든 어셈블리를 로컬에서 검색하게 된다. NTD에서처럼 참조되는 어셈블리를 서버에서 찾는 일은 없다. 혹시 노파심에서 그러는데, 참조에의한 검색과 업데이트를 혼동하지는 않으리라 믿는다. 런타임시 어셈블리 바인딩시의 검색은 퓨전이 담당하지만, 서버측에 새로운 업데이트 버전이 올라와있다면 퓨전이 다운받는 것이 아니라 ClickOnce 엔진에 의해서 업데이트 규칙에 의해(예를 들어 애플리케이션 시작 전 또는 종료 후에) 업데이트가 수행된다.

6) ClickOnce 애플리케이션 구동

애플리케이션 다운까지 정상적으로 수행이되었다면 ClickOnce 엔진은 이제 애플리케이션을 구동시킨다. 그러나 애플리케이션에 주어진 권한에 따라 다른 방식을 취한다.

만약 Full Trust를 갖는 애플리케이션이라면 로컬 PC에서 구동되는 애플리케이션과 동일한 환경에서 구동된다. 그러나 권한에 제한이 있는 애플리케이션이라면 AppLaunch.exe라는 프로세스가 먼저 실행된다. 그런 다음 ClickOnce 애플리케이션이 구동되는데 AppLaunch.exe는 ClickOnce 애플리케이션의 보안 Security SandBox 역할을 한다. 즉 안전한 실행 환경(Secure Execution Environment, SEE)을 제공한다. 만약 애플리케이션이 권한 밖의 행동을 하려고 하면 SandBox의 제어를 받게 된다. 이것과 관련해서 ClickOnce 보안 모델을 설명하는 포스트를 참조하기 바란다.

7) 다운받은 애플리케이션 삭제하기
추가로 PC에 다운받은 clickonce 애플리케이션 어셈블리들을 삭제하는 명령어를 소개한다.

mage -cc

이 명령어는 디렉토리 Local Settings 밑으로 다운받은 모든 애플리케이션과 데이터 파일을 삭제한다.


참조 문서
ClickOnce and FireFox with a custom setup equals ClickThrice and be disappointed - Scott Hanselman

ClickOnce Deployment Technology - Peter Bromberg
http://www.wise.com/Library/ClickOnce.pdf
ClickOnce : Bringing Ease and Reliability to SmartClient Deployment
http://www.code-magazine.com/article.aspx?quickid=0601041&page=1
Deploying applications - msdn
http://msdn2.microsoft.com/en-us/library/6hbb4k3e(VS.80).aspx

  1. manifest를 영영사전에서 찾아보면 다음과 같다. "a customs document listing the contents put on a ship or plane" 우리나라말로는 "적하 목록, 승객명단"으로 번역을 해 놓고 있다. [본문으로]
Posted by dalbong2

"보이는대로 하라는대로" 하면서 ClickOnce를 즐기는 두번째 시간이다.

앞 포스트에서 본 것처럼 다음 메뉴를 선택하면 Visual Studio.NET 2005의 게시마법사를 만날 수 있다.

프로젝트 선택->빌드 메뉴->게시 선택
프로젝트 오른쪽 클릭->게시...선택


이런 메뉴를 통해서 게시 마법사를 바로 실행할 수 있지만, Visual Studio.NET에서는 그전에 여러가지 설정을 할 수 있도록 하고 있다.  이 포스트에서는 게시전에 여러 가지 설정들을 수행할 수 있는 Visual Studio 2005의 게시 디자이너를 소개한다.

배포할 EXE 프로젝트 오른쪽 클릭->속성->게시탭

이 순서대로 수행하면 다음과 같은 게시용 디자이너가 출력된다.

1056162081

게시 디자이너

각 설정들을 알아보면 다음과 같다.

게시 위치  : 애플리케이션 구성 파일과 필요한 기타 파일을 복사할 디렉토리 경로값이다. 이 디렉토리 하위에 게시 버전별로 디렉토리를 구분해서 복사될 것이다. 게시 버전은 그림을 보면 아래에 나와 있다. 게시 위치를 나타내는 값으로는 웹사이트 경로, FTP 경로, 파일 공유 서버 경로등도 사용될 수 있다.
또한 애플리케이션(.exe.manifest)과 배포 메너페스트(.application)도 함께 자동 생성되어 복사된다. 이 두 파일은 아주 중요한 의미를 갖는다. 뒤의 Internals를 다루는 부분에서 설명한다.

설치 위치 : 사용자들이 브라우저를 통해서, 애플리케이션을 요청할때 사용하는 URL 주소이다. 그림에서 처럼 게시위치 값과 설치 위치 값이 서로 다를 수도 있다.

설치 모드 및 설정 : 애플리케이션을 온라인 전용 또는 온/오프라인용으로 설치할지를 선택하는 설정이다. 온라인 전용 타입은 애플리케이션을 구동시킬때 항상 클라이언트 PC가 온라인 상태로 있어야 한다. 반면에 온/오프라인 타입은 애플리케이션이 클라이언트 PC에 직접 설치된다. 그래서 제어판의 "프로그램 추가/제거"창에도 나타나고 "시작->모든 프로그램"메뉴에도 나타난다. 이 타입의 프로그램은 반드시 온라인일 필요는 없다.


게시버전 : 이 값을 나름대로의 의미를 갖도록 적절한 값을 사용하면 게시를 버전별로 구분하여 수행할 수 있다. 예를 들어 그림에서처럼 년도---수정번호 의 포맷으로 설정할 수도 있다.


응용 프로그램 파일 버튼: 현재 애플리케이션에 포함된 파일과 참조된 모든 파일을 보여주는 창이 뜬다.

1197285058

게시 대상 파일


파일 목록에 추가하려면, 프로젝트의 컨텍스트 메뉴를 통해서 프로젝트에 파일을 추가하면 된다. 이렇게 추가된 파일은 게시 상태 컬럼의 "포함(자동)"으로 될 것이다. 만약 xml 파일을 추가하면 그림처럼 게시 상태는 "데이터파일(자동)"으로 자동 선택될 것이다.

어셈블리 파일(.dll)의 게시 상태 컬럼의 드롭 다운 리스트를 클릭해보면 "필수 구성 요소"라는 선택 항목이 보일것이다. 만약 이것을 선택하면 ClickOnce 애플리케이션과 함께 클라이언트 PC로 배포되지 않을 것이다. 대신에 애플리케이션이 클라이언트 PC에 인스톨될때 클라이언트의 GAC에서 해당 파일을 찾게된다. 여기서 파일을 찾지 못하면 애플리케이션의 인스톨은 중단된다. 또한 "필수 구성 요소"로 선택된 파일은 ClickOnce 업데이트 메커니즘에 의해 업데이트되지 않는다[각주:1]
필수 구성 요소 버튼: ClickOnce 애플리케이션이 정상적으로 실행되기 위해서 사전에 설치되어 있어야 하는 경우도 있을 것이다. 그런 필수 프로그램중의 하나로 .NET v2.0은 모든 클라이언트 PC에 반드시 설치되어 있어야 한다. 이처럼 ClickOnce 애플리케이션이 시작하기 전에 사전에 미리 설치되어 있어야 하는 프로그램을 확인하고 필요하다면 설치도 수행해주는 프로그램이 있다. 이것을 부트스트래퍼(Bootstrapper)라 하는데, .NETv2.0과 Visual Studio.NET2005에는 부트스트래퍼를 지원하는 기능이 있다. 필수 구성 요소 버튼을 클릭하면 ClickOnce 애플리케이션을 구동시키기 전에 부트스트래퍼가 확인할 프로그램들을 선택할 수 있는 창이 뜬다.

1293626952

사전 필수 프로그램 선택

필수 프로그램창에는 미리 Visual Studio.NET에서 제공하는 기본적인 프로그램 목록이 출력된다. 이중에서 필요한만큼 선택하면 된다. 만약 다른 프로그램들이 필요하다면 이 목록에 출력되도록 커스터마이징을 할 수 있는 방법이 있다. 그것에 대해서는 뒤에 오는 포스트에서 다룰것이다.

업데이트 버튼 : 업데이트 버튼은 온/오프라인 타입을 선택하는 경우만 활성화된다. 온라인 타입의 애플리케이션에서는 애플리케이션을 실행할때마다 업데이트 버전이 있는지에 대한 여부를 확인하게 된다. 온/오프라인 타입 애플리케이션의 업데이트 전략에 대해서는 따로 포스트를 준비하겠다.

옵션 버튼 : 옵션 사항 선택할 수 있는 창이 뜬다.

1291568720

게시 옵션

애플리케이션의 컬쳐를 선택할 수 있는 옵션이 있다. 그리고 게시자 이름, 제품이름 그리고 기본적으로 제공되는 publish.htm에 나타나는 지원 URL도 지정할 수 있다.  publish.htm 파일을 다른 이름으로 지정할 수도록 하고 있다.

"URL을 통한 응용 프로그램 활성화 방지"를 선택하지 않으면 애플리케이션을 설치후 자동으로 실행시킨다. 선택하면 "시작"메뉴의 응용 프로그램 바로가기를 통해서 애플리케이션을 실행시켜야 한다.

".deploy 파일 확장명 사용" 옵션을 선택하면 웹 배포 서버로 게시되는 응용프로그램의 파일에 .deploy가 붙은 형태가 된다. 즉 .exe.deploy 또는 .dll.deploy 형태가 된다. IIS 웹 서버는 보안을 위해 어떤 확장자의 파일은 다운이 되지 못하도록 차단하고 있다. 예를 들어 확장명이 .dll, .config 및 .mdf 등인 파일은 차단될 수 있다. Windows 기반 응용 프로그램에는 보통 이와 같은 확장명을 가진 파일이 포함되어 있다. 사용자가 ClickOnce 응용 프로그램을 실행하여 웹 서버의 차단된 파일에 액세스하려고 하면 오류가 발생한다. 게시를 할때 기본적으로 파일 확장명에 ".deploy"을 덧붙임으로써, 모든 종류의 확장자에 대해 각각 차단을 제거하는 대신에 ".deploy"에 대한 차단만 제거해서 다운로드가 가능하도록 하면 되는 것이다. 참고로 ClickOnce 애플리케이션에서는 .deploy외에도 .application, .manifest 확장자에 대해서도 다운로드가 가능하도록 웹 서버를 구성해야 한다.


응용 프로그램으로 URL 매개 변수가 전달되도록 허용기본적으로 선택되어 있지 않다. 이것을 선택하게 되면 URL을 통해서 ClickOnce 애플리케이션을 구동시킬때 파라미터를 넘기고 애플리케이션에서는 이 파라미터에 접근할 수 있게 된다. 예를 들어 인증된 사용자만 애플리케이션을 사용하도록 하려는 시스템에서 인증 시스템과 애플리케이션이 분리되어 있는 경우, URL을 통해서 로그인한 사용자의 ID값을 애플리케이션으로 넘겨줄려고 하는 시나리오에서는 이 옵션을 사용해야 할 것이다. 애플리케이션이 구동되려면 배포 매너페스트 파일(.application)이 호출되어야 하는데 파일에 대한 URL에  GET 파라미터를 덧붙여서 호출하고 실행 파일 .exe의 내부에서는 .NETv2.0에서 제공하는 API[각주:2]를 통해서 넘겨진 파라미터를 받을 수 있다.

"웹 서버로 업로드된 파일 확인"이 선택되면(기본값) 웹 서버로 게시되는 파일들이 다운로드될 수 있는지 확인을 하게 됩니다. 다운로드할 수 없는 파일이 있으면 알림을 받는다.

마지막으로 게시 버전을 적절하게 입력하고 "지금 게시"를 클릭하면 된다. 모든 애플리케이션이 정상적으로 게시가 되면 Visual Studio.NET은 기본적으로 제공하는 publish.htm(다른 선택을 하지 않았다면)이 자동 호출될 것이다.
1200791289

게시 후 기본 웹 페이지 자동 호출


실행 버튼을 클릭하며 도중에 출력될 수도 있는 보안 경고 창을 무시하고 계속 실행하면 애플리케이션은 정상적으로 실행될 것이다.

  1. .NETv2.0은 응용 프로그램 구성 파일 목록에는 나와있지 않지만 애플리케이션 메너페스트 파일(.exe.manifest)에 기본적으로 "필수 구성 요소"로 선택되어 있다.
    [본문으로]
  2. System.Deployment.Application 네임스페이스의 클래스들을 보면 관련된 여러 API를 볼 수 있다 [본문으로]
Posted by dalbong2

이번 포스트부터는 되도록이면 목차를 자세히 잡더라도 좀 짧게 할려고 한다. 포스트가 너무 길면 지쳐서 또는 질려서 끝까지 읽어보는데도 상당한 수고가 필요할 것이라는 생각에서다.

이번 포스트에서는 ClickOnce 배포를 위해서 Visual Studio.NET 2005에서 지원해주는 내용을 "보이는대로 하라는 대로" 따라 하면서 즐기는 시간이다. 이름하여 "Enjoy The Appearance"이다. 다음 포스트도 마찬가지일 것이다. 그런 다음 그 보이는 이면을 이해하는 시간을 갖게 될 것이다. 이름하여 "Look Into The Internals" 시간을 갖겠다.

1. ClickOnce 작업 절차 개요

애플리케이션이 ClickOnce 배포 방식에 의해서 최종적으로 사용자 PC까지 다운되기 까지의 절차를 그림을 중심으로 설명한다.
1057652074

ClickOnce 배포 절차

간단히 설명하자면 우선 관리자(또는 개발자)가 개발한 애플리케이션을 서버로 게시하면 사용자는 게시한 애플리케이션을 HTTP 요청을 통해서 자동 다운받게 된다.

2. 게시

애플리케이션을 서버로 게시하는데 사용할 수 있는 툴로는 Visual Studio.NET 2005을 사용할 수도 있고, 그리고 MageUI.exe툴이나 mage.exe를 사용할 수도 있다. MageUI.exe 툴은 UI를 갖는 툴은 반면 mage.exe는 UI가 없는 커맨드 툴이다. .NET SDK와 함께 제공되는 명령창에서 mageUI.exe을 실행시켜 보면 다음과 같은 UI 툴이 실행된다. 
1152156840

mageUI.exe 실행모습 예

mageUI.exe를 통해서도 그림처럼 애플리케이션 메너페스트(.exe.manifest)와 애플리케이션 메너페스트(.application)을 직접 구성해서 만들 수도 있다. mage.exe를 사용하면 게시 툴을 직접 제작할 수도 있을 것이다. 여기서는 Visual Studio.NET 2005에서 제공하는 툴을 사용할 것이다.

1) 우선 그림처럼 ClickOnce 배포를 이용해서 게시할 EXE 프로젝트를 필요한 모든 참조들을추가해서 구성시켜 놓는다.
1004920412

ClickOnce 배포용 EXE 프로젝트 구성 예제

2. 게시

Visual Studio.NET 2005는 ClickOnce 게시를 할 수 있도록 하는 마법사를 포함하고 있다.

프로젝트 선택->빌드 메뉴->게시 선택
프로젝트 오른쪽 클릭->게시...선택

이렇게 게시 메뉴를 선택하면 다음과 같은 마법사가 실행된다.
1196100945

게시 마법사 창

애플리케이션을 게시할 서버의 경로를 입력한다. 서버의 경로로는 웹 서버 경로뿐만 아니라 파일 공유 경로, FTP 경로도 사용할 수 있다. 다음을 클릭한다.
1396373253

애플리케이션 타입 선택

이 화면은 애플리케이션을 온라인용으로만 사용할지 또는 온/오프라인용으로 사용할지에 대한 선택을 하는 화면이다. 온라인 전용으로 선택하면 항상 진입점 프로그램 EXE가 서버로부터 다운되어 시작한다. 따라서 항상 클라이언트 PC는 네트워크가 연결된 상태이어야 한다. 반면에 온/오프라인용의 애플리케이션은 클라이언트 PC에 설치되어 “프로그램 추가/제거”에 등록이 된다. 그래서 “시작” 메뉴를 통해서도 애플리케이션을 실행시킬 수 있게 된다. 마침 버튼을 클릭하면 모든 게시가 완료된다. 게시가 완료된 후의 게시 경로의 디렉토리는 다음과 비슷한 모습일 것이다.
1375144674

게시 완료 후의 디렉토리 모습

게시를 완료하면 그림처럼 publish.htm을 자동 생성해준다. publish.htm 파일은 단지 Visual Studio.NET이 편의를 위해 제공하는 페이지이다. ClickOnce 기술적인관점에서는  .application 확장자의 파일이나 기타 파일이 더 중요하다. 이런 파일에 대해서는 뒤의 고급 단계에서 다루기로 한다. Visual Studio.NET은 게시를 완료하면 자동으로 publish.htm을 호출해 준다.
1149599443

기본적으로 제공되는 웹 페이지 실행 모습

이 페이지의 실행 버튼을 클릭한다. 만약 게시하는 동안 디폴트 옵션만 선택했다면 보안 경고 창이 뜰 것이다.
1161149074

보안 경고 창

ClickOnce 애플리케이션은 기본적으로 게시자가 누구인지를 확인다. 게시자를 밝히기 위해서 디지털 사인을 필요로 하는데, 이 창은 게시자의 디지털 사인이 없다는 내용이다. 무시하고 실행을 클릭하면 게시된 애플리케이션의 모든 파일이 클라이언트 PC에 캐시되고 나서 애플리케이션이 실행된다.
1072911848

EXE 애플리케이션 최종 실행 모습

3. 필요 조건

ClickOnce 애플리케이션이 구동되기 위해서는 클라이언트 PC에 반드시 .NET 프레임워크 2.0이 설치 되어 있어야 한다. 만약 .NET V2.0이 설치되어 있지 않은 클라이언트 PC에는 Visual Studio.NET에 내장되어 있는 부트스트래퍼 기능을 이용해서 쉽게 배포할 수 있다. 이것에 대해서는 뒤에 다룬다.

서버측에서는 반드시 .NET v2.0이 설치될 필요는 없다. 단지 웹서버를 사용한다면 서버가 HTTP1.1을 지원하거나 또는 UNC경로나 FTP경로로 접근할 수 있는 파일 서버면 된다. 여기서는 다른 말이 없으면 웹서버로 IIS 6.0을 사용하는 것으로 한다.



참조 문서

ClickOnce Deployment Technology - Peter Bromberg
http://www.wise.com/Library/ClickOnce.pdf
ClickOnce : Bringing Ease and Reliability to SmartClient Deployment
http://www.code-magazine.com/article.aspx?quickid=0601041&page=1
msdn
Deploying applications
http://msdn2.microsoft.com/en-us/library/6hbb4k3e(VS.80).aspx
Posted by dalbong2

1. What is ClickOnce?


ClickOnce!  최초 한번만 클릭하는 수고를 해 주면 모든 것을 알아서 해 주겠다는 말인데, 이제 그 속 사정을 알아보도록 하자.

ClickOnce나 NTD(No-Touch Deployment)는 배포 기술이다. 개발에 의해 제작된 실제 애플리케이션은 Windows Forms애플리케이션과 동일하다고 보면 된다. 즉  Windows Forms 애플리케이션을 어떻게 클라이언트로 배포할것인가에 대한 솔루션인 것이다.

클라이언트로의 배포는 항상 말도 많고 탈도 많은 여러모로 비용이 많이 드는 프로세스이다. 모든 클라이언트 PC에 초기 배포/설치뿐 아니라 애플리케이션이 수정되면 다시 모든 클라이언트 PC에 업데이트 버전이 설치되도록 해야 한다. 어떻게 해서든지 최종적인 목표는 클라이언트 PC의 애플리케이션 버전이 항상 최신 버전으로 설치되어 있어야 한다는 것이다. 문제는 얼마나 쉽게 얼마나 부작용(소위 DLL Hell)이 없이 클라이언트 PC의 애플리케이션의 버전과 최신 버전의 싱크가 이뤄지는가이다.

ClickOnce는 이런 문제를 해결하기 위해 나온 배포 기술의 진화 과정 선상에 있는 기술중의 하나이다. 그 기술이 내세우는 특징적인 내용은 다음과 같은 것이 있을 수 있다.

■ 초기  설치 자동화

ClickOnce 배포는 웹 기반의 배포 방식이다. 애플리케이션에 필요한 모든 파일을 중앙의 서버에 모아두고 사용자들은 애플리케이션에 대한 URL을 통지받는다. 그런 다음 사용자들은 주소창을 통해서 주어진 URL로 직접 서버에 접근할 수 있다. URL로 접근하면 해당 애플리케이션이 클라이언트로 자동 다운되어 설치된다.
또한 Visual Studio.NET 2005에는 애플리케이션을 서버로 게시할 수 있는 기능이 있는데, 이것을 사용해서 서버로 게시를 하면 기본적으로 애플리케이션 URL을 갖는 링크 버튼이 있는 다음과 같은 페이지를 제공해준다. 사용자들은 이 버튼을 클릭해서 애플리케이션 배포를 서버에 요청할 수도 있다.
1167089881 

애플리케이션 배포용 기본 페이지
"실행(Install)" 버튼을 클릭하면 애플리케이션이 활성화되기 전에 그림과 같은 진행바가 출력된다. 이 그림이 출력되기 전에 상황에따라서는 게시자를 알 수 없다는 보안 경고창이 뜨기도 한다.
애플리케이션이 실행되기 위해서는 미리 설치되어 있어야할 필수 프로그램들이 있을 수 있다. 예를 들면 기본적으로 .NET V2.0은 설치되어 있어야 한다. ClickOnce 배포 방식을 이용하면 이런 필수 프로그램들을 체크하고 설치되어 있지 않으면 자동설치도 대신해주는 프로그램이 있다. 이런 프로그램을 부트스트래퍼(BootStrapper)라 한다. 이 부트스트래퍼를 잘 이용하면 초기 설정을 손 쉽게 수행할 수 있다.

■ 자동 업데이트
ClicOnce 애플리케이션(앞으로 ClickOnce로 배포된 애플리케이션을 줄여서 이렇게 부른다)은 업데이트 또한 쉽게 수행된다. 새로운 버전의 애플케이션을 서버로 게시만 해주면 클라이언트 PC 업데이트는 자동 업데이트를 수행한다.

■ 강력한 보안 모델
ClickOnce 애플리케이션에서는 애플리케이션 차원의 보안 개념을 새롭게 도입했다. ClickOnce 애플리케이션은 보안 샌드 박스(Security sand box)라는 제한된 영역에서 실행된다. 만약 그 영역을 벗어나는 일을 하려면 즉 애플리케이션에 주어진 권한 밖의 일을 하려고 하면 애플리케이션의 신뢰도를 승급시킬지 여부에 대한 사용자의 결정을 요청하게 된다.

■ .NET v2.0의 설치 조건
ClickOnce 배포 방식과 관련 장점들을 사용하기 위해서는 클라이언트 PC에 .NET v2.0이 설치되어 있어야 한다.

2. ClickOnce 배포 vs. 기존의 MSI 인스톨러

개발자들이 이해해야 하는 것이 있는데, ClickOnce 배포 방식은 기존의 MSI인스톨러에 의한 배포 방식을 대체하는 기술이 아니라 보완의 기술이라는 것이다. 마이크로소프트는 계속해서 MSI의 업그레이드된 버전을 출시하고 있다. ClickOnce 배포 방식도 그렇지만 NTD 방식 또한 마찬가지다. .NET과 함께 나온 이런 배포 기술들은 기존의 배포 방식을 대체할 수 있는 성격이 아니다. 서로 다른 적절한 쓰임새가 있는 상호 보완적이다.

언제 ClickOnce 배포가 적절한지 MSI 인스톨러가 적절한지에 대한 정해진 기준이나 간단한 규칙은 없다. 다만 결정을 하는데 도움이 될 수 있는 일반적인 가이드 라인이 있을 수 있다.

▶ 인스톨시 사용자의 입력이 필요한가?

▶애플리케이션이 Active Directory(AD) COM+ 환경을 필요로 하는가?

▶인스톨 후, 파일을 생성하거나 레지스트리에 값을 쓸 필요가 있는가?

▶ 애플리케이션이 COM 컴포넌트를 인스톨시켜야 하는가?

▶ 애플리케이션이 COM-Interop용 컴포넌트를 등록할 필요가 있는가?

▶ 애플리케이션이 어떤 서비스 프로그램을 설치할 필요가 있는가?

▶ 애플리케이션을 특별한 위치에 인스톨하거나 또는 어셈블리를 GAC에 등록할 필요가 있는가?

▶ 애플리케이션이 OS나 또는 런타임 환경에 따라 조건적으로 설치될 필요가 있는가?

대강 이런 문제들에 대한 답이 그렇다로 나온다면 MSI 인스톨러가 배포 방식으로 적절한 선택이 될 것이다.

배포 방식을 어떻게 선택하느냐를 결정하기 위해서는 각 배포 방식에 대한 이해가 충분히 있어야 할 것이다. 이 연재에서는 ClickOnce에 대한 개요에서부터 심도있는 인터널 얘기까지 해 볼 생각이다. 그럼 Let’s go!



참조 문서

ClickOnce Deployment Technology - Peter Bromberg
http://www.wise.com/Library/ClickOnce.pdf
ClickOnce : Bringing Ease and Reliability to SmartClient Deployment
http://www.code-magazine.com/article.aspx?quickid=0601041&page=1
msdn
Deploying applications
http://msdn2.microsoft.com/en-us/library/6hbb4k3e(VS.80).aspx

Posted by dalbong2

Q&A 게시판에 올라온 질문중에서 ClickOnce 배포에서 애플리케이션을 게시할때마다(기본적으로 게시 버전은 계속 높아진다), 어셈블리는 변경이 없더라도 다시 다운받는가 라는 질문이 있었다. 이에 대해서 간단히 테스트를 해 봤다.  달봉이의 테스트 노트북 환경은 이렇다.

.NET 2.0 설치

HTTP를 통한 어셈블리 다운 관찰 툴 : IE Inspector(http://www.ieinspector.com/index.html)


테스트를 해 본 결과 ClickOnce 애플리케이션의 버전은 올라가도 그 구성 어셈블리들은 버전이 동일하면 다운되지 않음을 알 수 있었다.

■ 테스트 #1 : 최초 다운 로드 모습

먼저 로컬 머신에 이미 다운된 ClickOnce 어셈블리들을 mage.exe –cc 명령을 통해서 모두 삭제한 후의 다운로드 모습이다. 당연히 모든 파일들을 다 다운받는다.

1298631170

<그림 1>(클릭하면 선명한 그림이 뜹니다)

■ 테스트 #2 : 애플리케이션 버전 업, 어셈블리 버전 변함 없음

다음은 애플리케이션의 버전은 높이고, 구성 어셈블리들의 버전은 동일한 경우의 다운되는 모습니다.

1076056019

<그림2>(클릭하면 선명한 그림이 뜹니다)

테스트 #3: 애플리케이션 버전 업, 어셈블리 버전 업

다음은 애플리케이션의 버전도 높이고, 특정 어셈블리의 버전을 하나 1.0.0.0->2.0.0.0으로 업시켜서 다시 게시를 했다. 그러나 그 내용은 변경되지 않았다. 그런 다음 다운되는 모습을 캡쳐한 것이다.

1073417992

<그림3>(클릭하면 선명한 그림이 뜹니다)

버전이 변경된 어셈블리만 추가로 다운되는 것을 볼 수 있습니다.

테스트 #4 : 애플리케이션 버전 업, 어셈블리 버전 변화 없음, 어셈블리 내용 변경

다음은 어셈블리 버전은 변화가 없으나 어셈블리의 내용은 변경된 경우입니다.

1073417992

<그림4>(클릭하면 선명한 그림이 뜹니다)

어셈블리 버전은 변경이 되지 않았지만 그 내용이 변경된 경우도 마찬가지로 해당 어셈블리만 다운되는 것을 볼 수 있다.

결론

ClickOnce 애플리케이션의 버전은 업되더라도, 구성 어셈블리들은 버전과 내용이 동일한 경우는 다운되지 않는다.

추측

ClickOnce에서는 배포될 어셈블리들에 대한 정보를 매너페이스 파일에 기록한다. ClickOnce : Look into the internals(http://dalbong2.net/42) 제목의 지난 포스트를 참조하길 바란다.

애플리케이션 매너페스트 파일(.exe.manifest)에는 애플리케이션 구성 어셈블리들과 그리고 각 어셈블리들에 대한 해쉬값을 값을 가지고 있다. 클라이언트측 ClickOnce 엔진은 클라이언트측의 해쉬값과 서버측 해쉬값을 비교함으로써 그 어셈블리 다운 여부를 결정할 것으로 보인다.

클라이언트 머신에서는,  수정이 없는 같은 내용의 어셈블리라고 해서 여러 버전의 그 어셈블리를 공유하지는 않는다. 애플케이션별로 모든 어셈블리를 별도의 공간(디렉토리)에 동일한 어셈블리의 복사본을 관리한다. 즉 버전이 동일해서 다운을 받지 않았다고 해서 이전의 어셈블리를 다른 버전의 애플리케이션과 공유하지는 않는다. 그렇다면 .NET 프레임워크에서는 지능적으로 동일한 버전의 어셈블리는 서버측으로부터 다운하는 대신에 클라이언트 머신상에서의 로컬 복사가 이뤄질 것이라는 예상을 해 본다.

Posted by dalbong2

공부하고 싶고, 쓰고 싶은 토픽은 많다. 디자인 패턴, WCF, Attribute 프로그래밍, AOP .... 근데 달봉이의 나태가 태산처럼 가로막고 있고 날이 갈수록 높아만 지는 것 같다. -_-;; 어찌 할까나.

때로는 원격에 있는 데이터 파일을 읽어야 하는 경우가 있다. 예를 들어 동적으로 생성되는 데이터를 미리 웹 서버에 캐싱해두고 클라이언트 머신에서 읽어 오려는 경우가 있을 것이다. 달봉이가 지금 참여하고 있는 프로젝트에서는, 클라이언트에서 시스템이 구동될 때 항상 데이터베이스에서 메뉴를 조회해와서 출력해주는 구조로 되어 있다. 그러나 시스템의 운영시에는 메뉴의 수가 너무 과도해져서 시스템의 구동 시간이 길어질 것으로 예상되고 있다. 해서 메뉴가 변경될때마다 XML형태로 해서 웹 서버에 저장해놓고 클라이언트의 시스템이 구동될 때 그 파일을 바로 읽어 오는 방식을 고려하고 있다.

이 포스트에서는 이런 경우 사용할 수 있는 .NET API를 소개한다.

HTTP 통해서 원격의 파일 접근하기


다음은 웹 서버에 있는 데이터를 접근하는데 사용할 수 있는 클래스들이다.

네임스페이스 : System.Net (http://msdn2.microsoft.com/en-us/library/system.net.aspx )

클래스 : WebClient (http://msdn2.microsoft.com/en-us/library/system.net.webclient.aspx )

      HttpWebRequest(http://msdn2.microsoft.com/en-us/library/system.net.httpwebrequest.aspx )

데이터는 정적인 파일일 수도 있고, 아니면 텍스트나 XML 데이터를 반환하는 ASP.NET 애플리케이션 페이지일 수도 있다. 다음은 정적 파일을 읽어와서 Stream으로 반환하는 샘플 코드이다.

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);

HttpWebResponse res = (HttpWebResponse)req.GetResponse();

Stream stream = res.GetResponseStream();

만약 반환되는 데이터가 XML 형식이라면 데이터를 얻는 가장 쉬운 방법은 XmlDocument(http://msdn2.microsoft.com/en-us/library/system.xml.xmldocument.aspx ) 클래스를 사용하는 방법일 것이다.

public static void ReadXmlStream(MenuSourceProvider provider, Stream stream)

{

    // XML로 부터 새로운 메뉴 트리를 로드 한다.

    XmlDocument xmlDoc = new XmlDocument();

    XmlNode xmlNode;

    xmlDoc.Load(stream);

    //지금부터 xmlDoc객체를 이용해서 작업을 하면 된다.

    // 자세한 내용은 Reading an XML Document into the DOM(http://msdn2.microsoft.com/en-us/library/azsy1tw2.aspx ) 및 관련 MSDN 문서를 참고한다.

서버 파일 접근 권한


HTTP
를 통해서 원격 웹 서버의 데이터를 접근할때는 보안을 고려해야 한다. 기본적으로 ClickOnce 애플리케이션은 그 배포 형태에 따라 네트워크 리소스에 대한 접근이 제한적이다. 다음은 배포 전략과 그에 따라 기본적인 웹 접근 권한을 나타내고 있다.

배포 형태

기본적인 네트워크 권한

웹 서버 인스톨

애플리케이션이 배포된 웹 서버에만 접근할 수 있다.

파일 공유 서버 인스톨

웹 서버를 접근할 수 없다.

CD-ROM 인스톨

어떤 웹 서버도 접근할 수 있다.

물론 Full Trust를 갖는 ClickOnce애플리케이션은 어떤 웹 서버도 접근할 수 있다.

기타 원격의 데이터를 접근하는 방법으로 웹 서비스 클라이언트를 제작하거나 또는 직접 데이터베이스 서버에 접근하는 방법도 있다. 이 방법에 대해서는 익히 잘 알고 있을 것으로 생각한다.

참조 문서

http://msdn2.microsoft.com/en-us/library/d8saf4wy.aspx

How to: Include a Data File in a ClickOnce Application

ClickOnce Deployment for Windows Forms Applications

Posted by dalbong2
앞의 포스트에서는 ClickOnce 배포로 로컬 PC에 데이터 파일을 저장하는 방법에 대해서 알아봤다. 이번에 그 저장된 파일을 읽는 방법에 대해서 알아본다.

2. 로컬 데이터 파일 읽기

ClickOnce 애플리케이션과 함께 배포되는 데이터 파일은 데이터 디렉토리(Data Directory)에 저장되는데, 그 디렉토리 경로는 애플리케이션 인스톨시 동적으로 결정된다고 했다. 따라서 직접적으로 그 물리적 경로를 기반으로 해서는 그 파일을 읽어들일 수 없다. 그래서 .NET 프레임워크에서는 데이터 파일이 저장되는 데이터 디렉토리에 접근(Read/Write)할 수 있는 API를 제공하고 있다.

데이터 디렉토리에서 읽고 쓰기

우선 데이터 디렉토리에서 파일을 읽고 쓰기 위해서는 ClickOnce 애플리케이션에는 디렉토리에 Read, Write할 수 있는 권한이 있어야 한다. 만약 애플리케이션이 Full Trust 권한을 갖는 것으로 설정되면 자동으로 이런 권한을 갖게 될 것이다. 애플리케이션이 이런 권한을 갖게 된다면 이제 System.IO 네임스페이스에 있는 클래스들을 이용해서 데이터 디렉토리에 접근할 수 있게 된다.  다음 코드는 데이터 디렉토리에 있는 CSV.txt 텍스트 파일에 접근하는 방법을 보여주고 있다.

if (ApplicationDeployment.IsNetworkDeployed)

{

  try

  {

       using (StreamReader sr = new StreamReader(

           ApplicationDeployment.CurrentDeployment.DataDirectory + @"\CSV.txt"))

       {

           MessageBox.Show(sr.ReadToEnd());

       }

  }

  catch (Exception ex)

  {

       MessageBox.Show("Could not read file. Error message: " + ex.Message);

  }

}

이 코드에서처럼 ClickOnce 애플리케이션에서는 ApplicationDeploymentCurrentDeployment 속성에 정의된 DataDirectory 속성을 이용하면 데이터 디렉토리에 접근할 수 있다.

3. 데이터 디렉토리와 애플리케이션 버전

애플리케이션의 각 버전은 서로 독립된 자신의 데이터 디렉토리를 갖는다고 했다. ClickOnce 엔진은 애플리케이션 배포 목록에 데이터 파일이 없더라도 항상 데이터 디렉토리를 만든다. 그렇게 해서 애플리케이션 런타임시에 새롭게 생성된 데이터 파일을 원한다면 이곳에 저장될 수 있도록 한다.

새로운 버전의 애플리케이션이 설치되면, ClickOnce 엔진은 이전 버전의 데이터 디렉토리에 있는 기존의 모든 데이터 파일을 새로운 버전의 데이터 디렉토리로 복사한다- 새로운 버전의 애플리케이션에 기존의 데이터 파일이 포함되지 않아도 복사한다.

그런 다음 새롭게 배포된 애플리케이션에 포함된 데이터 파일들이 이전 버전의 애플리케이션에도 포함되었는지를 확인한다. 만약 그렇다면 그 새로운 버전의 데이터 파일의 해시값과 이전 파일의 해시값을 비교해서 값이 틀리다면 이전 버전의 데이터 파일을 대체한다. 또한 이전 버전의 애플리케이션의 런타임시에 생성된 파일이 우연찮게 새로운 버전의 애플리케이션에서 새로 추가한 데이터 파일과 이름이 동일한 경우도 이전 버전의 파일을 대체한다. 이 두 경우 모두 이전 버전의 파일은 새로운 버전의 데이터 디렉토리에 .pre 라는  이름의 서브 디렉토리를 만들어 그쪽으로 캐싱한다.

만약 데이터 파일의 이동에 대한 좀 더 상세한 전략이 필요하다면 ClickOnce 배포 API를 이용할 수 있다. IsFurstRun(http://msdn2.microsoft.com/en-us/library/system.deployment.application.applicationdeployment.isfirstrun.aspx  ), Update(http://msdn2.microsoft.com/en-us/library/system.deployment.application.applicationdeployment.update.aspx ), UpdateAsync(http://msdn2.microsoft.com/en-us/library/system.deployment.application.applicationdeployment.updateasync.aspx )등을 참고하라.

클라이언트 PC에 안전하게 데이터 파일을 저장할 수 있는 방법은 데이터 디렉토리외에도 다음과 같은 방법을  이용할 수도 있다.

- Isolated Storage 이용

- 그 외의 로컬 파일로 저장

외의 데이터 파일 저장소

Isolated Storage

Isolated Storage는 부분 신뢰의 애플리케이션에서도 추가적인 권한이 없어도 안전하게 접근해서 파일을 생성하고 접근할 수 있도록 설계된 저장소이다. Isolated Storage도 파일 시스템상의 디렉토리이지만 그 실제의 물리적인 경로는 개발자와 사용자들에게는 숨겨져 있다. 대신에 .NET에서 제공하는 API를 통해서 접근하게 된다.

만약 애플리케이션이 부분 신뢰의 환경에서 구동되어야 하고 그러나 애플리케이션별 데이터 파일을 저장해야 한다면 Isolated Storage를 이용해야 한다. 자세한 내용은 MSDN에서 소개하는 Introduction to Isolated Storage(http://msdn2.microsoft.com/en-us/library/bdts8hk0.aspx )

다른 로컬 파일 이용

만약 엔드 유저가 리포트, 이미지, 음악 파일 같은 파일을 저장하고 읽어오는 애플리케이션을 만들어야 한다면, 그 애플리케이션은 로컬 파일 시스템을 읽고 쓸 수 있는 FileIOPermission(http://msdn2.microsoft.com/en-us/library/system.security.permissions.fileiopermission.aspx ) 필요할 것이다. 이런 경우라면 데이터 파일을 저장하기 위해서 자유롭게 파일 시스템을 이용할 수 있을 것이다.

다음에는 원격의 데이터 읽는 방법에 대해서 정리해 본다.
Posted by dalbong2

ClickOnce 애플리케이션을 작성하다 보면 텍스트 파일, XML 파일 또는 MS 액세스 DB 파일(.mdb)같은 데이터 파일을 각 클라이언트로 배포할 필요도 생기게 된다. 이번 포스트에서는 이런 데이터 파일을 ClickOnce에 포함시켜 클라이언트로 배포하는 방법을 알아본다. 그리고 클라이언트 애플리케이션에서 그 데이터 파일에 접근하는 방법을 알아본다.

1. 데이터 파일, ClickOnce 애플리케이션과 함께 배포하기

ClickOnce 애플리케이션이 클라이언트 PC에 인스톨되면 각 버전의 애플리케이션마다 자신만의 데이터 디렉토리(Data Directory)를 할당받게 된다. 데이터 디렉토리 경로가 미리 정해지지는 않는다. 애플리케이션마다 인스톨될 때 동적으로 정의된다. VS.NET의 배포 디자이너 폼을 사용하면 이 데이터 디렉토리에 저장될 파일을 설정할 수 있다.

VS.NET의 배포 디자이너 폼을 이용해서 데이터 파일 포함하기

1157062569

그림처럼 게시상태 컬럼값을 데이터값으로 세팅하면, 애플리케이션이 클라이언트에 인스톨될 때 해당 파일은 데이터 디렉토리로 이동하게 된다. 

이렇게 데이터 파일을 포함시키는 방법은 VS.NET 이용하는 것외에도 Mage.exe MageUI.exe 툴을 사용해서도 수행할 수 있다. 이 방법에 대해서는 다음의 링크가 가리키는 MSDN 사이트를 참고하기 바란다.

How to: Include a Data File in a ClickOnce Application

(http://msdn2.microsoft.com/en-us/library/6fehc36e.aspx )

다음은 저장한 데이터 파일을 읽는 방법에 대해서 정리해본다.

Posted by dalbong2

.NET3.0으로 제작된 어플리케이션을 ClickOnce로 배포하는 경우, 부트스트래퍼로 .NET 3.0을 미리 설치할때 나는 에러라 한다. 아직 체크해 보지 않았지만 북마크해둔다.

Using ClickOnce With .NET Framework 3.0

Posted by dalbong2
글을 읽다 보면 ClickOnce 어플리케이션이 어떻게 클라이언트 PC로 내려와서 활성화되는지를 잘 알 수 있다.
http://blogs.msdn.com/saurabh/archive/2006/03/02/541988.aspx
Posted by dalbong2

MAGE(Manifest Generating and Editing tool)를 이용한 게시

ClickOnce 어플리케이션을 게시할때 흔히 Visual Studio 2005를 사용한다. 그러나 이것의 단점이 있다. 어플리케이션 업데이트 버전을 Visual Studio로 게시하면 변경된 파일만 다운받는 것이 아니라 전체를 다운받게 된다. 이것은 ClickOnce가 지원을 못하는 것이 아니라 Visual studio가 게시를 하기 전에 빌드를 하게 되는데 이로 인해 모든 파일이 변경된 것으로 되어 버리기때문이다. 그리고 서버 작업을 하는 관리자에게는 Visual Studio가 적절한 툴은 아닐 수도 있다는 것이다. 서버에 Visual Studio를 설치해야 하는 것부터 부담이 될 수 있고 그리고 실행 프로젝트를 구성해야 하는 등 관리자가 개발툴을 다뤄야 하는 것도 그렇게 바람직한 것은 아닐 것이다.

이런 경우 .NET SDK와 함께 배포되는 MAGE 툴은 실행 프로젝트를 구성하지 않아도 되기 때문에 관리자에게는 적절한 대안이 될 수 있다. MAGE는 UI 툴뿐만 아니리 커맨드 툴로도 제공된다. 편한 것을 사용하면 될것이다. MAGE 툴은 그러나 어플리케이션 수정 후 업데이트하는데 필요한 작업 단계가 Visual Studio를 사용하는 경우보다 복잡해진다는 단점이 있다.  MAGE UI 툴을 실행시키려면 SDK 명령 프롬프트 창에서 다음 명령을 실행하면 된다.

mageui.exe( mage.exe를 실행시켜도 된다 )

이 명령을 실행하면 다음처럼 메뉴와 툴바만 있는 윈도우 프로그램이 실행된다.

1012495995

그림 MAGE 실행 모습

MAGE 툴은 클라이언트로 배포될 파일들을 대상으로 해서 두 개의 매니페스트 파일(.exe.manifest, .application)을 생성해주는 일을 한다. MAGE 툴의 File->New를 선택해보면 생성 항목으로 “Application Manifest”와 “Deployment Manifest” 밖에 없는 것을 알 수 있다. MAGE가 빌드를 해 주지는 않는다.

이제 이 툴을 사용해서 어플리케이션을 배포하는 작업 절차에 대해서 정리해 본다. 이 내용은 MSDN 도움말(http://msdn2.microsoft.com/en-us/library/xc3tc5xx(VS.80).aspx )을 보면 자세히 나와 있다.

MAGE 작업 전

1. 우선 게시할 파일들이 준비되어 있어야 한다.

MAGE 툴은 어플리케이션 빌드 툴이 아니기에 빌드는 하지 않는다. 따라서 어플리케이션을 구성 하는 어셈블리들은 모두 Visual Studio 등을 통해서 미리 빌드되어 있어야 한다. 그리고 어셈블리들뿐만 아니라 데이터 파일들, 리소스 파일등 클라이언트로 내려가야 하는 모든 파일들도 준비되어 있어야 한다.

이때 어플리케이션 파일들을 모아 두는 폴더가 반드시 배포 서버의 게시 위치일 필요는 없다. 오히려 MAGE 작업은 게시 폴더와는 다른 별도의 폴더에서 수행하는 것이 바람직한 구조이다. 

2. MAGE 작업이 수행될 폴더를 만든다. 여기서는 폴더명을 “MAGE작업”으로 하겠다.

3. MAGE 작업 폴더에 현재 버전의 파일을 모아둘 디렉토리를 생성한다. 처음 배포되는 경우 “1_0_0_0” 이름의 폴더가 될 것이다.( 이 버전은 어플리케이션 버전과 같을 필요는 없다).

4. 버전 폴더로 MAGE 작업 전에 준비해둔 어플리케이션 파일들을 복사한다.

지금까지의 작업 결과를 윈도우 탐색기에서 보면 다음과 같다. 

1049172577

[그림] 작업 디렉토리 구성

MAGE 작업

MAGE 작업이란 준비된 배포 파일들을 대상으로 해서 두 매니페스트 파일들(.exe.manifest, .application)을 생성하는 작업이다. 앞에서 MAGE 작업 폴더가 생성되었고 이GUI 방식의 MAGE 툴을 사용해서 이 두 파일을 생성하는 전체적인 절차를 알아보도록 하겠습니다.

각 항목에 대한 설명은 Brian Noyes 책의 설명을 참조한다.

▶ 어플리케이션 매니페스트(.exe.manifest) 만들기

1. SDK 커맨드 프롬프트에서 mageui.exe(mage.exe도 가능)로 툴을 실행시킨다.

2. 메뉴에서 File->New->Application Manifest을 선택한다.

1069269709
3. Name 탭에서 배포 이름과 버전을 입력한다. 배포 이름은 임의로 입력해도 좋지만 되도록이면 어플리케이션 이름을 입력하도록 한다.

배포 이름 : MyWebPageViewer

버전 번호 : 1.0.0.0

4. Files…탭을 선택한다.

5. 텍스트박스 Application Directory 옆에 있는 검색 버튼(…)을 클릭한다.

6. 어플리케이션 파일들을 가지고 있는 버전 폴더 “1_0_0_0”를 선택한다.

7. 파일들에 확장자 “.deploy”를 덧붙이겠다는 체크 박스를 선택한다.

8. Populate 버튼을 클릭해서 배포할 파일 목록을 추가한다. 실행 파일(exe)이 하나인 경우 MAGE 툴은 자동으로 File Type을 Entry Point로 선택해 준다. 만약 2개 이상인 경우는 시작 어플리케이션으로 어떤 것을 사용할지를 게시자가 선택해야 한다. 다른 파일들의 File Type도 적절히 선택한다. “.deploy”를 붙이도록 선택했다면 이때 물리적인 파일명이 변경된다.

9. Permissions Requeired 탭을 선택해서 어플리케이션에서 필요로 하는 권한 설정 작업을 해 준다. Full Trust가 기본 권한으로 되어 있는데 대부분의 어플리케이션은 이것을 선택할 수 있다. 보안이 좀더 강화된 어플리케이션을 제작할 필요가 있는 경우는 다른 항목을 선택하거나 Custom을 선택해서 필요한 권한을 직접 코딩할 수 있다. 여기서는 Full Trust를 선택하도록 하겠다.

10. File->Save를 선택해서 어플리케이션 매니페스트 파일을 저장한다. 이때 어플리케이션 매니페스트에 전자 서명을 추가하라는 창이 뜨게 된다.

1098682408
11. 인증서를 파일 시스템에 파일로 가지고 있다면 Sign as certificate file을 선택하면 된다. 또는 인증서 저장소에 등록된 인증서들이 있다면 Sign with stored certificate option을 선택해서 등록된 인증서중에서 선택하면 됩니다. 인증서가 아직 준비되지 않은 경우는 New 버튼을 사용해서 임시 인증서를 생성해서 사용한다. 임시 인증서는 다음 업데이트때도 사용해야 하므로 MAGE 작업 폴더에 저장해 두도록 한다.

12. 어플리케이션 매니페스트 파일 이름을 입력하고 저장한다. 매니페스트 파일명으로는 어플리케이션명을 입력하도록 한다.

▶ 배포 매니페스트(.application) 만들기

1. File->New->Deployment Manifest를 선택한다.

1359171900

2. Name 탭에서 어플케이션 명과 어플리케이션 버전 번호를 입력한다.

어플리케이션 이름 : MyWebPageViewer

어플리케이션 버전 번호 : 1.0.0.0

3.Publisher 탭을 선택해서 Publisher와 Product 값을 입력한. 게시 옵션을 설명하면서 언급했듯이 만약 어플리케이션이 클라이언트에 인스톨 모드로 설치된다면 Publisher에 입력한 값은 바로가기 메뉴의 디렉토리명이 되고, Product에 입력한 값이 바로가기 메뉴의 이름이 된다.

4. Deployment Options 탭을 선택한다. Start Location값으로 사용자가 .application 파일에 접근할 수 있는 URL 주소를 입력한다. 어플리케이션 매니페스트 파일 작성시, 파일들에 확장자 “.deploy”를 덧붙였으므로 “Use .deploy file name extension"을 선택한다. 다른 옵션은 필요한대로 입력한다. 옵션들의 의미가 필요하다면 앞에서 ClickOnce 게시 옵션을 설명한 부분을 참조하시기 바란다.

4. Update탭을 선택해서 어플리케이션의 업데이트 정책을 설정한다.

5. Application Reference 탭을 선택한다. Select Manifest 버튼을 이용해서 앞에서 만들어둔 어플리케이션 매니페스트 파일을 선택하면 이 탭의 비어있는 텍스트 박스가 자동으로 채워지게 된다.

6. File->Save를 선택해서 배포 매니페스트 파일을 저장한다. 이때 배포 매니페스트에 전자 서명을 추가하라는 창이 뜨게 된다.

7. 어플리케이션 매니페스트 경우처럼, 인증서를 파일 시스템에 파일로 가지고 있다면 Sign as certificate file을 선택하고 인증서 저장소에 등록된 인증서들이 있다면 Sign with stored certificate option을 선택해서 등록된 인증서중에서 선택하면 된다.

8. 배포 매니페스트 파일명으로 어플리케이션 이름을 입력하고 저장한다. 배포 메니페스트 파일의 저장 위치는 “1_0_0_0” 폴더의 상위에 저장하도록 하는 것이 좋다. 이렇게 하면 나중에 업데이트 버전을 게시해야 하는 경우 배포 매니페스트를 업데이트 버전의 하위 폴더에 있는 어플리케이션 매니페스트를 참조하도록만 편집하면 된다.

매니페스트 파일을 생성해서 사용자에게 배포할 폴더 구성을 완성했다. 다음은 게시할 최초 버전 폴더를 생성한 최종 모습이다.

1027833587

그림 게시 구성 폴더 최종 모습

MAGE 작업 후

1. 버전 폴더 1_0_0_0과 배포 매니페스트 파일(.application)을 웹 서버, 파일 공유 또는 CD로 복사하면 된다.

2. 그러고 나서 사용자에게는 배포 매니페스트 파일(.application)에 접근할 수 있는 URL, UNC 경로 또는 CD를 제공하면 된다.

3. 사용자는 제공된 배포 매니페스트 파일을 호출한다. 웹 서버로 노출되는 페이지에 대한 링크를 클릭하거나 CD로 웹 서버로 노출되는 경우는 배포 매니스트파일의 링크를 클릭하면되고, CD로 배포하면 윈도우 탐색기에서 해당 파일을 더해서 ClickOnce로 배포되는 로컬 머신에 설치한다.

MAGE 업데이트

이제 MAGE를 이용해서 업데이트를 하는 절차에 대해서 생각해 보겠다.

1. 변경된 파일을 준비하고 확장자 “.deploy”를 덧붙인다.

2. 새로운 버전의 디렉토리 1_0_0_1을 생성한다.

3. 이전 버전 폴더 1_0_0_0에 있던 파일들을 새로운 버전의 폴더로 복사한다.

4. “.deploy”를 붙인 변경된 파일을 새로운 버전 폴더로 복사한다. 만약 기존에 해당 파일이 존재하면 덮어쓰도록 한다.

5. 이제 MAGE 툴을 시작하고, 1_0_0_1 폴더에 있는 기존의 1_0_0_0 버전의 어플리케이션 매니페스트 파일(.exe.manifest)을 불러온다.

6. Name 탭의 버전을 새로운 값 1.0.0.1로 변경한다.

7. Files 탭의 “Populate” 버튼을 클릭해서 새로운 버전의 파일들을 불러온다.

8. 변경된 어플리케이션 매니페스트 파일의 내용을 새로운 버전 폴더에 저장한다. 이때 서명을 다시 요구하는 창이 뜨면 앞에서와 같은 방법으로 서명 작업을 하며 된다.

9. 이제 배포 매니페스트(.application)을 다시 업데이트한다. MAGE 툴에서 기존의 배포 매니페스트 파일을 불러온다.

10. Name 탭 페이지에서 버전을 1.0.0.1로 변경한다.

11. Application Reference 탭의 Select Manifest 버튼을 선택해서 새로 업데이트한 어플리케이션 매니페스트 파일(.exe.manifest)을 선택한다.

12. 서명을 다시 하고 저장 하면 된다.

13. 마지막으로 새로 만들어진 버전 폴더와 배포 매니페스트 파일을 배포 서버로 복사한다.

이제 사용자가 클라이언트에서 어플리케이션을 실행하면 서버측의 새로운 버전의 배포 매니페스트 파일을 확인하고 업데이트를 수행한다.

MAGE 작업 정리

이제 MAGE를 사용해서 게시를 하는 프로세스를 정리하고 MAGE를 사용하는 방법에 대해서는 마무리짓겠다.

1155783192

[그림] MAGE 작업 정리

빌드된 어플리케이션 파일들을 준비한 후 게시자는 MAGE 툴을 사용해서 매니페스트 구성 작업을 버전별로 수행한다. 최종 완성된 버전 폴더와 배포 매니페스트를 배포 서버 또는 CD로 복사하면 된다.

Posted by dalbong2

오늘 메모를 정리하다가 그냥 버리기가 아까운 정보가 있어 아직 테스트 해 보지는 않았지만 기록을 하나 남겨두려고 한다.
ClickOnce 어플리케이션이 실행되기 전에 필요한 필수 프로그램이 사전에 설치되어 있어야 한다면 우리는 당연히 부트스트래퍼를 떠올릴 것이다(아닌가...-_-;;). ClickOnce 어플리케이션이 실행되다가 반드시 있어야 하는 구성요소가 사전에 설치되어 있지 않다면 에러창을 보여준다. 에러창에서 문제 해결을 위한 좀 더 구체적인 내용을 담고 있는 페이지에 대한 URL을 보여줄 수 있다는것이다. 예를 들어 사전에 GAC에 등록되어 있어야 하는 어셈블리가 있다면 그것을 설치할 수있는 MSI 파일에 대한 링크를 그 지원 페이지에 포함시킬 수도 있을 것이다. 사용자는 링크의 페이지의 설치 링크를 통해서 필요한 구성요소를 설치하고 다시 어플리케이션을 실행시킬 수 있다.
물론 부트스트래퍼를 이용해서 모든 필수 구성 요소를 사전에 설치해야 겠지만 추후에 새로운 필수 프로그램이 하나 더 추가되었다고 해 보자. 사정이 허락하지 않아, 음 어떤 사정일지는 모르겠지만 사용자들이 다시 부트스트래퍼를 실행하는 것이 힘든 상황이라면 게시자는 어플리케이션 매니페스트 파일(.exe.manifest)에 수작업으로 supportUrl 어트리뷰트를 필수 구성 요소별로 지정해 줄 수 있다. 그렇게 기존의 매니페스트 파일을 수정했다면 다시 두 매니페스트 파일에 서명을 한 후 ClickOnce 어플리케이션을 다시 게시해야 할 것이다. 다음은 supportUrl을 사용하는 몇가지 예제이다. .
먼저 필요한 OS의버전에 대한 지원 URL입니다.
  <dependency>
    <dependentOS supportUrl="http://www.adatum.com/MyApplication/wrongOSFound.htm">
      <osVersionInfo>
        <os majorVersion="4" minorVersion="0" servicePackMajor="0" servicePackMinor="0" />
      </osVersionInfo>
    </dependentOS>
  </dependency>

다음은 필요한 CLR의 버전에대한 지원 URL 설정예입니다.

<dependency>
    <dependentAssembly dependencyType="preRequisite" allowDelayedBinding="true" supportUrl=" http://www.adatum.com/MyApplication/wrongClrVersionFound.htm">
      <assemblyIdentity name="Microsoft.Windows.CommonLanguageRuntime" version="2.0.40607.0" />
    </dependentAssembly>
  </dependency>

다음은 GAC에 등록되어있어야 하는 필수 어셈블리에 대한 지원 URL입니다.

  <dependency>
    <dependentAssembly dependencyType="preRequisite" allowDelayedBinding="true" supportUrl=" http://www.adatum.com/MyApplication/missingSampleGACAssembly.htm">
      <assemblyIdentity name="SampleGACAssembly" version="5.0.0.0" publicKeyToken="04529dfb5da245c5" processorArchitecture="msil" language="neutral" />
    </dependentAssembly>

각 지원 URL이 가리키고 있는 페이지에서는 필수 구성 요소를 설치할 수 있는 설치 파일에 대한 링크를 가지고 있으면 된다.
  </dependency>

Posted by dalbong2
1.1.4 XML 파일 데이터 병합하기

먼저 간단히 XML 파일이 “데이터 파일”로 배포된 경우의 데이터 병합에 대해서 생각해보겠습니다. 현재 클라이언트에는 1.0 버전의 authors.xml 파일이 내려가 있습니다. 사용자들은 각자 authors.xml 파일에 필요한 편집 작업을 한 후 저장해 두고 있습니다. 다음 그림을 보면 최종 사용자가 버전 1.0의 어플리케이션에서 authors.xml에 레코드를 하나 추가하고 있습니다. 이제 프로그램의 버그로 인해 버전 2.0의 어플리케이션으로 업데이트하려고 합니다. 그런데 버전 2.0의 authors.xml 파일의 내용에도 1.0 배포할때보다 레코드가 하나더 추가되어 배포됩니다.

업데이트를 수행하게 되면 서버측에서 변경된 authors.xml 파일이 클라이언트의 2.0 데이터 폴더로 다운로드됩니다. 그런 다음 다시 변경된 버전 1.0의 authors.xml 파일도 2.0 데이터 폴더의 하위에 있는 .pre 폴더로 복사됩니다. 어플리케이션에서는 1.0버전의 authors.xml 파일의 데이터와 2.0 버전의 authors.xml 파일의 데이터를 병합합니다. 병합때 2.0의 authors.xml과 1.0의 authors.xm의 데이터중에서 “au_id”가 중복되는 레코드는 2.0의 레코드만 남겨두고 1.0의 레코드는 삭제합니다. 결과는 그림과 같습니다.

1003076484

그림  데이터 병합 결과

이처럼 클라이언트에 있는 버전 1.0의 XML 파일이 제대로 버전 2.0의 XML로 옮겨가려면 버전2.0의 어플리케이션에는 데이터 이행을 수행하는 코드가 있어야 합니다. 이 결과를 보려면 책과 함께 배포된 예제 프로젝트를 다음과 같이 실행시켜야 합니다.

1. “XML파일데이터이행” 폴더밑에 보면 버전1.0과 2.0 프로젝트가 있습니다. 먼저 버전1.0 프로젝트를 오픈합니다. 추가된 파일중에서 authors.xml 파일을 열어서 추가되어 있는 데이터를 확인합니다. authors.xml 파일의 “빌드 작업” 속성이 “내용”으로 선택되어 있는지 확인합니다.

2. 프로젝트 디자이너에서 게시 탭으로 이동합니다. “응용 프로그램 파일…” 버튼을 클릭해서 구성 파일 속성 편집 창을 띄웁니다. authors.xml 파일의 “게시 상태”가 “데이터 파일”로 설정되어 있는지 확인합니다. 창을 닫습니다.

3. 게시 버전을 “1.0.0.0”으로 설정합니다.

4. “게시” 버튼을 클릭해서 어플리케이션을 게시합니다.

5. 자동 생성되는 publish.htm 페이지의 “설치” 버튼을 클릭해서 버전 1.0 어플리케이션을 실행합니다.

6. “XML로딩” 버튼을 클릭해서 배포된 authors.xml의 데이터를 로딩합니다.

7. 레코드를 하나 그리드에 추가하고 저장 버튼을 클릭합니다.

8. 버전 1.0 어플리케이션을 종료합니다.

다음은 서버측에서 변경된 버전 2.0을 게시할 차례입니다.

1. 버전2.0 프로젝트를 오픈합니다. 추가되어 있는 authors.xml 파일에는 원래의 버전 1.0의 초기 데이터보다 한건이 더 추가되어 있음을 확인합니다. 다시 파일의 “빌드 작업”속성이 “내용”으로 설정되어 있는지 확인합니다.

2. 이전처럼 어플리케이션 구성 파일 창을 열어서 “게시 상태”를 확인합니다.

3. “게시 버전”을 “2.0.0.0”으로 변경합니다.

4. “게시”버튼을 클릭해서 버전 2.0을 게시합니다.

5. 자동 호출되는 publish.htm 페이지를 닫습니다.

6. “시작” 메뉴의 바로가기를 선택합니다.

7. 업데이트를 묻는 창이 뜨면 확인을 선택해서 업데이트를 수행합니다.

8. “XML로딩” 버튼을 클릭해서 1.0의 클라이언트 버전과 2.0의 서버측 버전 authors.xml의 데어터가 병합되어 출력되는 결과를 확인합니다.

버전 2.0의 어플리케이션에서 구현하는 데이터 병합하는 절차는 이렇습니다. .pre 폴더에 저장되어 있는 1.0의 클라이언트 버전 authors.xml과 데이터 디렉토리에 다운로드되어 있는 새로운 2.0 버전의 authors.xml을 각각의 데이터셋으로 로딩합니다. 그런 두 데이터 셋을 병합합니다. 병합하기 전에 “au_id” 컬럼값이 중복되는 레코드는 서버측 레코드를 삭제해서 중복을 피하도록 했습니다. 다음은 예제 프로젝트 “XML파일데이터이행”에 포함되어 있는 코드입니다.

코드  이전 버전과 새로운 버전의 데이터 파일 병합 예제

string _DirPath = "";

string _File = "authors.xml";

public Form1()

{

InitializeComponent();

//데이터 디렉토리 경로

if (_DirPath == "")

{

_DirPath = GetDataDirectory();

}

//ClickOnce 어플리케이션이고

//해당 버전이 처음 실행되는지를 확인한다.

if (ApplicationDeployment.IsNetworkDeployed

&& ApplicationDeployment.CurrentDeployment.IsFirstRun)

{

//이전 버전과 새로운 버전의 데이터를 병합니다.

MergeData();

}

}

//XML파일이 포함된 디렉토리를 구한다

private string GetDataDirectory()

{

string dir = "";

// ClickOnce로 배포된 어플리케이션인지 확인하기

if (ApplicationDeployment.IsNetworkDeployed)

{

// 데이터 디렉토리 조회

dir = ApplicationDeployment.CurrentDeployment.DataDirectory;

}

else

{

// 어플리케이션 디렉토리 조회

dir = AppDomain.CurrentDomain.BaseDirectory;

}

return dir;

}

// 이전 버전의 XML데이터와 새로운 버전의 XML 데이터를 병합하기

private void MergeData()

{

// 데이터 파일에 대한 전체 경로

string fullPath = Path.Combine(_DirPath, _File);

// 이전 데이터 파일에 대한 전체 경로

string preFullPath = Path.Combine(_DirPath, @".pre\" + _File);

//데이터이행할 파일이 없음

if (!File.Exists(preFullPath))

return;

// 새로운 버전의 XML 데이터 로딩하기

_AuthorSet.Clear();

_AuthorSet.ReadXml(fullPath);

// 이전 버전의 XML데이터 로딩하기

DataSet preAuthorsDataSet = new DataSet();

preAuthorsDataSet.ReadXml(preFullPath);

preAuthorsDataSet.Tables[0].TableName = "authors";

// 병합

// 이 부분은 상황에 따라서 적절한 병합 정책을 구현해야 합니다.

DataRow[] drs = null;

foreach (DataRow row in preAuthorsDataSet.Tables[0].Rows)

{

drs = _AuthorSet.Tables["authors"].Select("au_id='" + row["au_id"].ToString() + "'");

if (drs.Length >= 1)

{

drs[0].Delete();

}

}

_AuthorSet.Merge(preAuthorsDataSet);

//XML 파일에 저장

_AuthorSet.WriteXml(fullPath);

}

폼 인스턴스가 생성되면서 바로 GetDirectory() 메소드를 호출해서 ClickOnce로 배포된 경우(IsNetworkDeployed)의 데이터 디렉토리에 대한 경로를 구합니다. 그런 다음 MergeData()를 호출하고 있습니다. 그러나 이 메소드는 역시 ClickOnce로 배포된 경우이여야 하고 그리고 해당 버전에서 처음 실행되는 경우(IsFirstRun)만 호출합니다.

MergeData()에서는 현재 데이터 디렉토리에 있는 2.0 버전의 authors.xml과 .pre 폴더에 있는 이전 버전의 authors.xml을 각각 다른 데이터셋에 로딩합니다. 그런 다음 병합을 합니다. 여기서는 예제를 간단히 하기 위해서 “au_id”가 중복된 레코드는 새로 다운로드한 버전의 레코드를 삭제한 다음 DataSet 클래스의 Merge()를 통해서 병합하는 간단한 규칙을 사용하고 있습니다. 그러나 실전 프로그램에서는 데이터 병합 규칙이 좀더 복잡할 수도 있습니다. 때로는 스키마의 변경까지도 있을 것입니다.

하여튼 적절한 위치에서 이전 버전의 데이터와 최신 버전의 데이터를 병합하는 코드를 구현하지 않으면 이전 버전의 데이터는 사라지게 된다는 것입니다.

1.1.5 데이터베이스 파일 배포하기

■ 클라이언트측 데이터베이스

요즘은 무선을 지원하는 기술과 디바이스가 계속 나오면서 이제는 온라인과 오프라인을 모두 지원해야만 하는 시나리오가 증가하고 있습니다. 기존의 연결 지향형 데이터베이스로는 이런 기술들을 지원하기가 힘들어지고 있습니다. 클라이언트 어플리케이션이 오프라인 상태가 되더라도 사용할 수 있는 저장소가 필요하게 되었고 그러다 필요하다면 온라인으로 연결시켜 서버측의 중앙 데이터베이스와 동기화할 수 있는 방안이 필요하게 된 것입니다.

SQL Server 2005 제품군에는 이런 로컬 저장소에 적합한 두 제품이 포함되어 있습니다: Microsoft SQL Server 2005 Compact Edition 과 Microsoft SQL Server 2005 Express Edition. 두 제품 모두 클라이언트 어플리케이션의 로컬 저장소와 캐시 제공을 목적으로 하는 제품들입니다.

Microsoft SQL Server 2005 Express Edition( 이하 SQL Server Express 버전으로 줄입니다)는 기존의 Microsoft Data Engine (MSDE)이 진화한 제품입니다. 다운로드 용량은 대략 53MB 정도가 됩니다. 따라서 대부분의 클라이언트 배포 시나리오에서는 여전히 큰 사이즈입니다. 대신에 데이터베이스 엔진의 완전한 기능이 필요하다면 SQL Express는 좀 더 적합한 로컬 데이터 저장소가 될 것입니다.

SQL Server 2005 Compact Edition( 이하 SQL Compact 버전으로 줄입니다)도 데스크톱 시나리오를지원하는 제품으로서 기존의 SQL Server 2005 Mobile Edition( 그 이전에는 SQL CE)이 진화한 제품입니다. 마이크로스프트에서는 클라이언트 어플리케이션의 기본 로컬 저장소로서 SQL Express 보다 이 제품을 더 권장하고 있습니다. SQL Compact 버전은 몇 개의 DLL로 구성되는데 그 다운로드 용량이 1.7M 이하로서 배포에도 별 부담이 없습니다. 따라서 데스트톱 그리고 모바일 장치용 어플리케이션에는 SQL Compact가 적합한 버전이라 하겠습니다.

■ SQL Compact Edition의 특징

SQL Compact 엔진을 배포하는 방법은 두 가지가 있습니다. SQL Compact엔진은 설치 프로그램으로 제공되는데, 이 설치 프로그램을 부트스트래퍼에 포함시켜 사전 필수 프로그램으로 배포할 수 있습니다. SQL Compact 엔진은 몇 개의 DLL들로 구성되는데 설치 프로그램으로 배포되면 이 DLL들은 GAC에 등록이 됩니다. 그리고 설치 프로그램을 이용해서 배포하는 방식은 관리자 권한이 필요합니다.

다른 방법으로는 SQL Compact DLL들을 프로젝트에 파일(참조 추가가 아닙니다)로서 포함시켜 ClickOnce를 이용해서 어플리케이션과 함께 배포하는 방법이 있습니다. 이렇게 임베딩된 SQL Compact DLL들은 설치되는 어플리케이션의 전용으로 배포가 됩니다. 이렇게 어플리케이션 전용 배포를 하게 되면 권리자 권한이 필요하지 않습니다.

앞의 두 방법은 데이터베이스 엔진 배포 방법입니다. 이제 데이터를 배포해야 하는데 이것도 간단합니다. SQL Compact 버전은 파일(.sdf)기반의 데이터베이스입니다. 즉 .sdf 데이터 파일만 배포하면 됩니다. 데이터 파일을 배포하려면 앞에서 알아본 authors.xml 파일처럼 “데이터 파일”로서 포함되면 됩니다.

SQL Compact의 데이터베이스 엔진 DLL은 어플리케이션에 파일 형태로 추가하고 그리고 데이터 파일“.sdf”는 ClickOnce의 “데이터 파일”로 설정하게 되면 어플리케이션의 로컬 데이터 저장소가 간단히 클라이언트로 배포됩니다. 이 책에서의 예제도 이 방식을 사용합니다.

1.1.6 SQL Compact 프로젝트 구성하기

여기서는 SQL Compact엔진의 구성 어셈블리들을 프로젝트에 파일로서 임베딩하는 절차에 대해서 알아봅니다. 이렇게 구성 어셈블리들을 프로젝트에 임베딩시키게 되면 부트스트래퍼로 사전 필수 프로그램을 SQL Compact를 설치할 필요가 없게 됩니다. 그래서 앞에서 말한대로 관리자 권한이 필요없게 됩니다. 프로젝트를 구성하기 위해서는 사전에 다음이 설치되어 있어야 합니다.

Visual Studio 2005 SP1, SQL Server Compact Edition , SQL Server Compact Edition Tools for Visual Studio

설치가 끝나면 다음 절차를 따라서 SQL Compact 데이터베이스를 배포할 준비를 합니다.

1. 앞의 세 설치를 끝내고 나서 다음 디렉토리 C:\Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\SQL Server\Mobile\v3.0 하위로 이동합니다. 이 폴더와 하위 폴더에는 데이터파일 Northwind.sdf와 SQL Compact DLL들이 있습니다.

2. 이 파일들을 Visual Studio의 프로젝트 디렉토리로 복사해서 프로젝트에 포함시킵니다.

3. 이 파일들의 “출력 디렉토리로 복사” 속성을 “변경된 내용만 복사”로 설정합니다.

4. Northwind.sdf 파일을 프로젝트에 추가하면 자동적으로 System.Data.SqlServerCe.dll에 대한 참조도 추가됩니다. 확인하고 만약 추가되어있지 않다면 C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\System.Data.SqlServerCe.dll 경로의 파일을 참조추가합니다.

5. “로컬 복사” 속성을 “True”로 설정합니다.

다음은 구성된 결과 프로젝트입니다.

1305347355

그림 7‑31 SQL Compact 포함 프로젝트 구성

이렇게 해서 SQL Compact 데이터베이스를 임베딩 방식으로 배포할 준비가 되었습니다. 이제 데이터소스를 이용해서 필요한대로 프로그램을 작성하면 됩니다. 클라이언트에 배포되어서 데이터 파일에 연결하는 연결문자열을 어떻게 설정하는지에 대해서는 함께 제공되는 소스 “SQLCompact데이터이행”프로젝트의 코드를 참조하면 됩니다. 데이터 파일과의 연결이 이뤄졌으면 흔히 해 왔던 것처럼 T-SQL 쿼리와 ADO.NET 객체들을 이용해서 데이터 관리를 하면 됩니다.

앞에서 말한대로 .sdf 파일을 “데이터 파일”로 설정해서 배포하면 데이터디렉토리로 배포가 됩니다. 이 데이터 파일에 대한 연결 문자열은 좀 특별한 변수 DataDirectory를 사용해서 구성합니다.

"Data Source = |DataDirectory|\Mydb.sdf"

DataDirectory를 사용해서 이렇게 연결 문자열을 지정하면 이 경로는 런타임시 실질적인 물리적인 경로로 풀이됩니다. 개발시에 디버그 모드에서 실행된다면 DataDirectory는 거기에 맞는 디렉토리 즉 실행파일이 있는 bin폴더로 해석됩니다. 물론 이때 “Mydb.sdf”의 “출력 디렉토리로 복사” 속성의 값은 “항상 복사” 또는 “변경된 내용만 복사”로 설정되어 있어야 합니다.

1.1.7 SQL Compact 데이터 파일 업데이트하기

앞에서는 ClickOnce로 SQL Compact의 데이터베이스를 프로젝트에 파일로 포함시켜 배포하는 절차에 대해서 알아봤습니다. 이제 다시 데이터 파일의 업데이트에 대한 이야기로 돌아가보겠습니다. 데이터 파일로 XML 파일 authors.xml을 업데이트할때와 그 과정은 동일합니다. SQL Compact 데이터 이행을 위한 예제 코드는 “SQLCompact데이터이행” 폴더하위에 있는 버전 1.0과 버전 2.0 프로젝트를 보면 됩니다.

먼저 버전 1.0을 게시해서 클라이언트 머신에 설치합니다. 클라이언트에는 1.0 버전의 Northwind.sdf 파일이 함께 배포되어 내려가 있습니다. 이 데이터 파일에는 “Customers”라는 테이블이 하나 포함되어 있습니다. 사용자들은 각자 필요한대로 고객에 대한 정보를 편집해서 다시 저장해 둡니다. 이런 상태에서 2.0의 어플리케이션으로 업데이트하려고 합니다. 그런데 버전 2.0의 Northwind.sdf 파일의 내용에는 1.0 배포할때와는 다른 데이터가 들어있습니다. 동일한 테이블의 레코드가 추가되었거나 테이블이 추가되었거나 또는 테이블의 컬럼 구조가 변경되었을 수도 있습니다. 업데이트를 수행하게 되면 서버측에서 변경된 Northwind.sdf 파일이 클라이언트의 2.0 데이터 폴더로 다운로드됩니다. 그런 다음 다시 변경된 버전 1.0의 Northwind.sdf 파일도 2.0 데이터 폴더의 하위의 .pre 폴더로 복사됩니다.

이제 데이터의 병합은 어플리케이션의 책임입니다. 클라이언트의 1.0 버전의 Northwind.sdf와 새로 다운로드된 버전 2.0의 Nortwind.sdf의 데이터를 어떻게 병합할지는 어플리케이션의 코드로 구현되어야 합니다. 두 버전간에 데이터 및 테이블의 구조적인 차이가 크면 클수록 그 병합을 코드도 복잡해질 것입니다.

그림은 병합 과정중의 데이터들의 모습입니다. 클라이언트측에 설치되어 있는 버전 1.0의 어플리케이션을 실행시켜 데이터를 하나 수정해서 저장하고 있습니다.

1178023407

그림  SQL Compact-클라이언트측 버전1.0 데이터

현재 클라이언트측 버전 1.0의 데이터 레코드 수는 91개입니다. 다음은 서버측에서 레코드 하나 더 추가해서 92개인 데이터를 준비해서 버전 2.0으로 해서 배포합니다.

1047822232

그림 SQL Compact-서버측 버전 2.0 데이터

이제 클라이언트측에 배포된 1.0 버전과 서버측에서 내려온 2.0 버전이 병합된 결과가 어플리케이션 2.0의 데이터가 됩니다.

1149722432

그림 SQL Compact 클라이언트측 병합 버전 데이터

이런 결과를 얻기위해서는 제공되는 소스 프로젝트를 다음 절차대로 실행시킵니다.

1. 먼저, “버전 1.0” 폴더에 있는 “SQLCompact데이터이행” 프로젝트를 Visual Studio에서 오픈합니다. 이 프로젝트에는 버전 1.0의 Northwind.sdf 파일이 “데이터 파일”로 설정되어 있습니다.

2. 게시 탭페이지에서 게시 버전을 “1.0.0.0”으로 설정합니다.

3. “게시” 버튼을 클릭해서 게시를 수행합니다.

4. 자동 호출되는 publish.htm의 “설치” 버튼을 클릭해서 버전 “1.0.0.0” 어플리케이션을 설치합니다.

5. “데이터 로드”버튼을 클릭해서 1.0 버전의 Customers 테이블을 로드합니다.

6. 임의대로 데이터를 변경합니다.

7. “저장”버튼을 클릭해서 저장합니다.

클라이언트에 배포된 버전 1.0의 데이터가 변경되었습니다. 이제 서버측에서 레코드가 하나 추가되어있는 버전의 Northwind.sdf 게시하도록 하겠습니다.

1. “버전 2.0” 폴더에 있는 “SQLCompact데이터이행” 프로젝트를 Visual Studio에서 오픈합니다.

2. 게시 탭페이지에서 게시 버전을 “1.0.0.1”으로 설정합니다.

3. “게시” 버튼을 클릭해서 게시를 수행합니다.

4. 자동 호출되는 publish.htm의 “설치” 버튼을 닫고, “시작” 메뉴의 바로가기를 통해서 어플리케이션업데이트를 수행합니다.

5. 버전 2.0의 어플리케이션이 시작되면서 이전 버전과의 데이터 병합이 이뤄집니다.

6 “데이터 로드”버튼을 클릭하면 병합된 최종 데이터가 출력됩니다.

“SQLCompact데이터이행” 예제의 병합 코드는 단순합니다. authors.xml에서처럼 클라이언트측에서 변경된 1.0의 Customers 테이블의 레코드를 그대로 유지하고 2.0의 Customers테이블에 새로 추가된 레코드만 병합을 하고 있습니다. 다음은 “SQLCompact데이터이행” 프로젝트에서 병합하는 부분의 코드입니다. 데이터를 병합하는 코드는 모두 MergeData() 메소드에 있습니다. 이 메소드는 어플리케이션이 ClickOnce로 배포되고 그리고 새로운 버전 1.0.0.1이 처음으로 시작되는 경우만 호출됩니다. 다음에 소개되는 코드는 “SQLCompact데이터이행” 예제 프로젝트에 포함되어 있습니다.

코드  데이터 병합 시기 선택

//ClickOnce 어플리케이션이고

//해당 버전이 처음 실행되는지를 확인한다.

if (ApplicationDeployment.IsNetworkDeployed

&& ApplicationDeployment.CurrentDeployment.IsFirstRun)

{

//이전 버전과 새로운 버전의 데이터를 병합니다.

MergeData();

}

다음은 호출되는 MergeData() 메소드입니다.

코드 7‑10 SQL Compact 데이터 병합 예제

// 이전 버전의 Northwind.Custorms테이블과 새로운 버전의 Northwind.Custorms테이블를 병합한다.

private void MergeData()

{

string dataDirectory = "";

if (ApplicationDeployment.IsNetworkDeployed)

{

dataDirectory = ApplicationDeployment.CurrentDeployment.DataDirectory;

}

else

{

dataDirectory = AppDomain.CurrentDomain.BaseDirectory;

}

string preDataFile = Path.Combine( dataDirectory, @".pre\Northwind.sdf");

//데이터이행할 이전 파일이 없으면 작업을 종료한다.

if (!File.Exists(preDataFile)) return;

// 이전 데이터 파일에 대한 전체 경로

string preConnectString = @"Data Source = |DataDirectory|\.pre\Northwind.sdf";

SqlCeConnection preConnection = new SqlCeConnection(preConnectString);

// 데이터 파일에 대한 전체 경로

string newConnectString = @"Data Source = |DataDirectory|\Northwind.sdf";

SqlCeConnection newConnection = new SqlCeConnection(newConnectString);

//이전 버전의 데이터를 가져온다.

NorthwindDataSet preDataSet = new NorthwindDataSet();

CustomersTableAdapter preAdapter = new CustomersTableAdapter();

preAdapter.Connection = preConnection;

preAdapter.Fill(preDataSet.Customers);

//새로운 버전의 데이터를 가져온다.

NorthwindDataSet newDataSet = new NorthwindDataSet();

CustomersTableAdapter newAdapter = new CustomersTableAdapter();

newAdapter.Connection = newConnection;

newAdapter.Fill(newDataSet.Customers);

//두 버전의 데이터를 병합한다.

//이 부분은 현실적인 시나리오에 있어서는 더 복잡해질 수 있다.

DataRow[] drs = null;

foreach (DataRow row in preDataSet.Customers)

{

drs = newDataSet.Customers.Select("[Customer ID]='" + row["Customer ID"].ToString() + "'");

if (drs.Length >= 1)

{

drs[0].Delete();

}

}

newDataSet.Merge( preDataSet);

//데이터 파일에 저장

newAdapter.Update( newDataSet.Customers);

}

이곳에서 이야기하고자 했던 것은 병합 로직이나 코드가 아닙니다. 중요한 것은, 클라이언트에 배포된 데이터 파일이 수정된 상태이고 그리고 서버측에서 새롭게 배포된 데이터 파일도 이전 버전의 초기 데이터 파일을 수정한 경우라면 어떤 과정을 거쳐 그 파일의 업데이트가 일어나는가입니다. 클라이언트에서 수정된 이전 버전의 데이터 파일은 새로운 버전의 데이터 디렉토리의 하위 디렉토리 .pre로 복사됩니다. 서버측의 수정된 데이터 파일도 새로운 버전의 디렉토리로 다운로드됩니다. 그런 상태에서 아무런 병합 작업을 해주지 않은채 다시 한번 더 어플리케이션 업데이트 작업을 해 준다면 .pre 폴더에 있던 이전 버전의 데이터 파일제 사용할 수 없게 됩니다. 해서 상황에 맞게 적절한 병합 코드가 필요합니다.

Posted by dalbong2

1.1 데이터 파일 관리

앞에서는 어플리케이션을 구성하는 파일중에서 데이터 파일을 제외한 일반 구성 파일들의 배포와 관리에 대해서 알아봤습니다. 이제부터는 “게시 상태”를 “데이터 파일”로 설정한 파일들의 배포와 관리에 대해서 알아봅니다.

ClickOnce 어플리케이션을 작성하다 보면 XML 파일 또는 MS 액세스 DB 파일(.mdb)같은 파일을 각 클라이언트로 배포할 필요도 생기게 됩니다. ClickOnce 어플리케이션이 클라이언트 머신에 설치되면 각 버전마다의 데이터 디렉토리(Data Directory)도 사용자 프로파일 디렉토리 하위에 함께 생성됩니다. 어플리케이션 구성 파일 속성 편집창에서 “데이터 파일”로 설정된 파일은 어플리케이션을 설치할 때 모두 데이터 디렉토리로 복사합니다. SQL Express 파일(.ldf, .mdf), Access 파일(.mdb), SQL Compact 파일(.sdf)그리고 XML 파일(.xml) 확장자를 갖는 파일은 Visual Studio는 기본적으로 데이터 파일로 설정합니다. 그러나 데이터 파일로 설정하는 것은 어떤 종류의 파일도 가능합니다.

1.1.1 데이터 파일 배포 결과

다음처럼 두개의 파일에 대해 “게시 상태” 속성을 “데이터 파일”로 설정했다고 해 보겠습니다. 각각의 파일은 “리소스\비트맵”과 “리소스\아이콘”의 하위 디렉토리에 포함되어 있습니다.

1234415029

그림  데이터 파일 설정

이렇게 설정된 어플리케이션을 클라이언트 머신에 설치하면 다음 그림처럼 계층 구조를 그대로 유지한채로 데이터 디렉토리 하위로 배포가 됩니다.

1104249060

그림  데이터 파일 계층 구조 배포

“데이터”로 설정하지 않은 파일은 어플리케이션 디렉토리(application directory)에 포함되는데 앞에서도 알아봤지만 완전 신뢰냐 부분 신뢰의 어플리케이션이냐에 따라서 동일한 API가 다른 값을 반환할 수도 있습니다. 그러나 데이터 디렉토리는 어플리케이션 전용 디렉토리로서 완전 신뢰든 부분 신뢰든 상관없이 프로그램상에서도 동일한 API를 사용해서 자유롭게 파일 관리를 할 수 있습니다.

1.1.2 데이터 파일 관리

앞에서 본 것처럼 데이터 파일도 계층구조를 그대로 유지하면서 클라이언트 머신에 설치되기 때문에, 파일에 접근하기 위해서 데이터 디렉토리를 기준으로 한 상대경로값을 이용하면 개발시의 코드를 그대로 유지해서 사용할 수 있습니다 : 데이터 디렉토리 + “\리소스\비트맵\Bitmap1.bmp”와 같은 형식이 됩니다. 이때 데이터 디렉토리 값을 구하기 위해서 ApplicationDeployment.DataDirectory 속성을 사용할 수 있습니다. DataDirectory 속성이야 말로 ClickOnce 어플리케이션에서의 데이터 파일 관리의 시작점입니다.

그러나 역시 이런 방법은 어플리케이션이 ClickOnce로 인스톨된 경우에는 이용할 수 있다는 것입니다. 어플리케이션을 개발할때는 이 형식을 그대로 사용할 수 없습니다. 개발때에는 우선 해당 파일을 빌드 출력 파일로 복사시켜야 하고 그 다음 다른 형식의 접근 경로가 필요합니다. 빌드시 해당 파일들을 출력 경로로 복사시켜주기 위해서는 파일들에 대한 “출력 디렉토리 복사”속성 값을 “항상 복사” 또는 “변경된 내용만 복사”로 선택해야 합니다. 이때 파일이 복사될때도 그 계층 구조는 프로젝트에서 구성한 그대로 유지됩니다.

그런 다음 코드상에서는 ApplicationDeployment 클래스의 IsNetworkDeployed 속성을 이용해서 현재 어플리케이션이 ClickOnce로 배포된 어플리케이션인지 아니면 개발시 디버그 모드에서 실행하는지를 구분합니다. 만약 ClickOnce로 배포된 어플리케이션의 경우라면 앞에서처럼 데이터 디렉토리를 이용한 경로를 이용하고 그렇지 않다면 데이터 디렉토리 대신에 어플리케이션 디렉토리를 이용해서 경로를 구성합니다.

코드  데이터 파일 접근 경로 결정

private void Form1_Load(object sender, EventArgs e)

{

string dataPath = string.Empty;

// ClickOnce로 배포된 어플리케이션인지 확인합니다.

if (ApplicationDeployment.IsNetworkDeployed)

{

// 데이터 디렉토리 조회

dataPath = ApplicationDeployment.CurrentDeployment.DataDirectory;

}

else

{

// 어플리케이션 디렉토리 조회

dataPath = AppDomain.CurrentDomain.BaseDirectory;

}

// 데이터 파일에 대한 전체 경로

dataPath = Path.Combine(dataPath, @"리소스\비트맵\Bitmap1.bmp");

// 파일을 Bitmap으로 읽어오기

Bitmap b = new Bitmap(dataPath);

pictureBox1.Image = b;

}

파일에 대한 경로가 완전히 구성되면, FileStream 또는 앞의 코드처럼Bitmap 클래스를 이용해서 저장된 파일을 읽어와서 원하는대로 이용하면 됩니다.

데이터 디렉토리를 이용해서 ClickOnce 어플리케이션에서 데이터 파일을 읽고 쓰기 위해서는 어플리케이션에 파일 시스템에 대한 읽기, 쓰기 권한이 있어야 합니다. 만약 완전 신뢰의 어플리케이션으로 배포한 경우는 자동으로 이런 권한을 갖게 됩니다.

1.1.3 데이터 파일의 업데이트 과정

데이터 파일(“게시 상태”를 “데이터 파일”로 설정한 파일)은 그 파일에 대한 업데이트 과정이 일반 어플리케이션 구성 파일과는 조금 다릅니다. 이해를 돕기 위해서 앞에서 설명한 일반 어플리케이션 업데이트 과정을 간략히 요약해보도록 하겠습니다.

ClickOnce 어플리케이션이 클라이언트에 설치되어 있는 상태에서 새로운 업데이트 버전이 다운로드하게 되면 일단 새로운 버전의 파일들이 캐싱될 폴더가 생성됩니다. 서버측에 업데이트된 버전의 파일과 클라이언트에 캐싱되어 있는 이전 버전의 파일들이 비교됩니다. 그래서 변경되거나 새로 추가된 파일만 새로운 버전의 폴더로 다운로드받습니다. 그리고 나서 변경되지 않은 파일은 이전 버전의 폴더에서 새로운 버전의 폴더로 로컬 복사가 일어납니다. 그러나 만약 클라이언트측의 이전 버전에 캐싱되어 있는 파일중에서 변경된 파일이 있다면 로컬 복사가 일어나는 것이 아니라 해당 파일은 서버측에서 다운로드받게 됩니다. 이것은 일반 어플리케이션 파일은 클라이언트측에서 변경되면 외부로부터의 강제 수정이 이뤄졌다는 것으로 보는 것입니다.

조금만 생각해 보면 데이터 파일은 일반 어플리케이션 파일과는 업데이트 과정이 다를 것이라고 예상할 수 있습니다. 데이터 파일은 당연히 클라이언트측에서 수정될 일이 많을 것이기 때문에 변경되었다고 해서 서버측 버전을 다시 다운로드해서 덮어써 버리면 안될 것이기 때문입니다. 정확히 어떤 상황인지를 알아봅니다.

1.0.0.0버전의 어플리케이션과 함께 게시되는 데이터 파일 MyData.xml의 “게시 상태”를 “데이터 파일”로 설정했다고 가정해 보겠습니다. 이 XML 파일은 클라이언트로 배포되면 데이터 디렉토리로 저장되고 사용자가 어플리케이션을 통해서 로딩해서 편집 작업을 한 후 다시 데이터 디렉토리로 저장하는 파일입니다.

그런 다음 어느 순간 어플리케이션의 버그를 수정하기 위해서 새로운 업데이트 버전 2.0.0.0의 어플리케이션을 게시합니다. 그러나 이 버전에 포함된 XML 파일의 데이터는 여전히 변경되지 않은 최초 파일입니다.

클라이언트로 업데이트 버전의 어플리케이션이 다운로드되면 사용자가 수정한 MyData.xml 파일은 일반 어플리케이션 파일처럼 새로운 버전 2.0.0.0의 데이터 디렉토리로 복사됩니다. 그런 다음 서버측의 MyData.xml 파일은 다운로드되지 않습니다. 이제 사용자가 버전 2.0.0.0을 시작하면 1.0.0.0에서 작업했던 수정된 데이터가 보이게 됩니다.

이처럼 서버측의 데이터 파일이 수정되지 않은 한 업데이트가 일어날때마다 클라이언트의 데이터 파일은 새로운 버전의 데이터 디렉토리로 복사되어서 사용자로 하여금 연속적인 작업을 할 수 있게 됩니다.

그러나 서버측에 게시된 데이터 파일도 변경되면 어떻게 될까요? 예를 들어 XML 파일의 스키마를 약간 변경하거나 초기 데이터를 변경할 수도 있을 것입니다. 이런 경우 게시자가 의도적으로 서버측 파일을 변경하였으므로 서버측 버전을 새로운 버전의 데이터 디렉토리로 복사해야 할까요? 아니면 클라이언트에서 사용자가 작업을 내용을 보존하기위해서 클라이언트측의 이전 데이터 파일을 새로운 폴더로 복사해야 할까요?

클라이언트측의 버전 1.0.0.0의 데이터 디렉토리에 사용자가 수정한 데이터 파일 MyData.xml 파일이 있고, 서버측에는 새로운 버전 2.0.0.0의 어플리케이션에 수정된 MyData.xml이 “데이터 파일”로 포함되어 있다고 해 보겠습니다. 업데이트 버전의 어플리케이션이 클라이언트로 다운로드되면 다음과 같은 일이 일어납니다.

1. 이전 버전 1.0.0.0의 데이터 디렉토리에 있는 수정된 MyData.xml 파일은 새로운 버전 2.0.0.0의 데이터 디렉토리 하위에 있는 \.pre 폴더로 복사가 됩니다.

2. 버전 2.0.0.0에 있던 데이터 파일 MyData.xml 파일은 새로운 데이터 디렉토리로 다운로드됩니다.

1078602263

그림 데이터 파일 업데이트 과정

어플리케이션에서 특별한 조치를 취하지 않으면 버전 2.0.0.0에서는 이전 버전의 데이터 파일을 무시하고 새롭게 다운로드 한 2.0.0.0의 데이터 파일을 사용하게 됩니다.

이전 버전의 폴더 \.pre에 있는 데이터 파일도 코드상에서 접근할 수 있습니다. 따라서 이전 버전의 데이터 파일에 담긴 데이터를 계속해서 유지하고 싶다면 이런 식의 업데이트가 일어나는 경우에 실행될 수 있는 데이터 병합 프로그램 코드를 작성해야 합니다.

만약 .\pre 폴더에 있는 이전 버전의 데이터를 새로운 버전의 데이터 파일과 적절히 병합하지 않은 상태에서 또 다른 업데이트가 일어난다면 \.pre에 있는 데이터 파일은 사실상 유실된 데이터라 할 수 있습니다. 따라서 새로운 버전의 어플리케이션이 실행되자마자 데이터 병합을 하지 않으면 사용자가 편집한 데이터는 영원히 잃게 된다고 생각해야 합니다.

Posted by dalbong2

1.1 어플리케이션 파일 관리

ClickOnce 어플리케이션은 앞에서 말한대로 여러 종류의 파일들로 구성되어 있습니다 : 실행파일, 설정 파일, 리소스 파일, 데이터 파일, 참조 어셈블리 파일등. 먼저 어플리케이션을 구성하는 참조 어셈블리들외에 일반 파일들을 어떻게 배포에 포함시키는지를 알아봅니다. 그리고 어플리케이션의 코드상에서 직접 파일들을 그룹별로 다운로드해서 사용하는 방법 등을 알아봅니다.

1.1.1 ClickOnce 배포에 파일 포함시키기

ClickOnce로 배포되기 위해서는 해당 파일을 일단 배포 대상에 포함시켜야 할 것입니다. 그런 후에그 파일을 데이터 파일로 설정할 것인지 아니면 일반 어플리케이션 구성 파일로 설정할 것인지가 결정될 것입니다.

어플리케이션을 작성하다보면 참조되는 어셈블리 파일외에도 XML 파일, 텍스트 파일등 기타 필요한 파일들을 어플리케이션의일부로 사용하는 경우가 있습니다. 여기서는 이런 파일들을 어떻게 ClickOnce 배포에 포함시키는지를 알아보겠습니다.

■ 프로젝트에 포함시키기

어플리케이션을 배포 서버로 게시할 때 Visual Studio를 사용하는 경우, 파일을 배포에 포함시키고자 한다면 그 파일은 반드시 Visual Studio의 프로젝트에 포함되어 있어야 합니다. 파일을 Visual Studio에 포함시키려면 다음 절차대로 합니다.

1. 솔루션 탐색기에서 프로젝트 오른쪽 클릭-> 추가->기존 항목을 선택합니다.

2. 파일 대화창에서 추가하고자 하는 파일을 선택합니다.

3. 대화창의 추가 버튼을 클릭합니다.

1373990461

그림 프로젝트에 파일 추가

프로젝트에는 추가된 파일과 소스 파일이나 프로젝트 파일등이 함께 혼재되어 있지만, 최종 게시 결과물에는 소스 파일이나 프로젝트 파일은 포함되지 않고 추가된 파일만이 포함됩니다.

■ 빌드 작업 속성

그러나 프로젝트 일부로 추가된 파일들이 게시 최종 결과물에도 포함되기 위해서는 반드시 설정해줘야 하는 속성값이 있습니다. 바로 “빌드 작업(Build Action)” 속성입니다. 처음에 필자도 이 속성값을 제대로 설정하지 않아서 파일이 왜 포함되지 않는지 원인을 찾지 못해 헤매던 기억이 납니다.

빌드 작업 속성에서 선택할 수 있는 값은 다음과 같습니다: 없음(None), 컴파일(Compile), 내용(Content), 포함 리소스(Embedded Resource).

프로젝트에 파일을 추가하면 Visual Studio는 파일 확장자를 기반으로 해서 적절한 “빌드 작업” 속성 값을 결정합니다. 이 중에서 빌드 작업값을 “내용” 또는 “컴포넌트”로 설정한 파일은 “게시 상태” 그리고 “다운로드 그룹” 속성값들을 편집할 수 있습니다.

“내용”을 선택하게 되면 추가된 파일이 게시되는 파일목록에도 포함되게 됩니다. 만약 파일을 프로젝트에 추가했는데도 “빌드 작업” 속성값이 “없음”으로 선택되었다면, 이 상태로 게시를 해도 게시 결과물에는 포함되지 않게 됩니다.

소스 코드 파일에 대한 빌드작업 속성값은 “컴파일”로 선택됩니다. “컴파일”로 선택된 파일의 내용은컴파일러에 의해서 컴파일이 된 다음 어셈블리에 포함되게 됩니다.

“포함 리소스”로 선택된 파일도 빌드 후 출력되는 결과 어셈블리에 포함되지만 컴파일은 되지 않습니다. “컴파일” 또는 “포함 리소스”로 선택된 파일들 자체는 게시 결과물에 포함되지는 않지만 이 파일들이 포함된 어셈블리는 게시 결과물에 포함됩니다. 파일을 어셈블리에 리소스로 포함시키는 것에 대해서는 뒤에서 다시 보게 됩니다.

어셈블리 파일을 참조로 추가하는 것이 아니라 “항목추가”를 이용해서 일반 파일로서 프로젝트에 추가하면 기본적으로 빌드 작업값이 “컴포넌트”로 선택됩니다. 이 값으로 선택된 상태에서 게시를 하면 배포 목록에는 포함되지만 게시 결과물에는 파일이 직접 포함되지 않게 됩니다. 이 값은 플러그인 프로그램을 ClickOnce 배포에 포함하려는 경우 사용할 수 있습니다. 플러그인 프로그램을 배포하는 것은 뒤에서 알아봅니다.

1.1.2 계층 구조 배포

만약 계층 구조를 포함하고 있는 어플리케이션을 배포하면 클라이언트에서는 어떻게 캐싱되는지 알아보도록 하겠습니다. 예를 들어 그림처럼 여러 종류의 파일을 디렉토리별로 구분해서 관리하고 있는 실행 프로젝트가 있다고 해 보겠습니다.

1306206572

그림 계층 구조 프로젝트

게시 탭 디자이너에서 어플리케이션 구성 파일 편집하는 창을 띄워보면 “게시 상태” 속성을 모두 “포함(자동)”으로 선택하도록 합니다.

1392589266

그림 계층구조 파일의 게시 상태 설정

이 설정대로 게시를 한 어플리케이션을 사용자가 설치를 하게되면 프로젝트에서 구성되었던 구조 그대로가 로컬의 디렉토리로 배포가 됩니다.

1323062058

그림 계층 구조 배포 결과

ClickOnce 어플리케이션은 이처럼 클라이언트로 배포되어서도 그대로 계층 구조를 유지하고 있기 때문에 파일을 접근하기 위해서 개발시 상대 경로를 기반으로 코드가 작성되어 있다면 그 코드는 클라이언트로 배포되어서도 문제없이 실행될 수 있게 되는 것입니다. 상대 경로라고 했는데 그럼 무엇을 기준으로 하는 상대경로일까요. 현재 어플리케이션이 실행되는 그 디렉토리가 기준이 된다는 것이 너무 당연한 것일까요. 기준 디렉토리는 .NET 프레임워크에서 아주 중요한 기본 개념중의 하나입니다. 간단히 내릴 수 있는 정의는 아니고 바로 이어서 관련된 다른 개념들과 더불어 살펴봅니다.

1.1.3 어플리케이션 도메인 및 디렉토리

어플리케이션 파일들은 ClickOnce 어플리케이션에만 포함되는 것이 아니라 다른 일반 .NET 어플리케이션도 이런 파일로 구성됩니다. 사실 어플리케이션을 파일 관리는 .NET 프레임워크의 일반적인 주제입니다. ClickOnce의 어플리케이션 파일 관리 기능은 많은 부분을 .NET의 기본 개념에 의존하고 있습니다. 따라서 여기서 소개하는 .NET의 파일 관리에 필요한 기본적인 개념은 ClickOnce 배포와는 직접적인 관련이 없을 지 모르지만 ClickOnce 어플리케이션 파일 관리 구조를 이해하고 응용하기 위해서는 필요한 개념들입니다.

■ 어플리케이션 도메인

.NET의 기본 개념으로 “어플리케이션 기본 디렉토리(application base directory)”라는 개념이 있습니다. ClickOnce가 파일을 관리하는 것을 이해하기 위해서는 이 개념을 알고 있어야 합니다. 그러나 어플리케이션 디렉토리라는 개념을 알기 위해서는 또 어플리케이션 도메인(Application Domain)이라는 개념을 이해해야 합니다.

어플리케이션 도메인이나 어플리케이션 디렉토리 개념은 ClickOnce 어플리케이션에 국한되는 개념이 아니라 .NET 어플리케이션이라면 모두에 적용되는 개념입니다. 따라서 한번 잘 이해해 두면 다른 형태의 .NET 어플리케이션을 이해하는데도 많은 도움이 됩니다.

어플리케이션 도메인에 대한 개념을 이해하기 위해서 머리속에서 상상을 하나 해 보겠습니다. .NET 어플리케이션이 시작되면 공간이 하나 생성된다고 생각하십시요. 그리고 그곳에서 어플리케이션이 실행되는 것입니다. 상상을 한다는 것은 다른 말로 하면 그 공간이 논리적인 공간이라는 의미입니다. 이런 공간을 어플리케이션 도메인(Application Domain)이라고 합니다. .NET의 CLR(Common Language Runtime )은 .NET 어플리케이션을 실행시키기전에 기본적으로 하나씩의 어플리케이션 도메인을 생성하고 그곳에서 어플리케이션을 로딩시킵니다. 이런 어플리케이션 실행 공간은 그림처럼 하나의 OS 프로세스내에 여러 개 생성될 수 있습니다. 개발자가 직접 하나의 어플케이션 도메인에서 다른 어플리케이션 도메인을 생성해서 그곳에 다른 프로그램을 로딩할 수도 있습니다.

1111300478

그림 어플리케이션 도메인

어플리케이션 도메인은 어플리케이션마다 할당되는 독립적인 공간입니다. 어플리케이션 도메인의 독립성은 어플리케이션 도메인이 무엇인지를 잘 설명해주는 특징중의 하나입니다. 두 어플리케이션이 동일한 어셈블리를 참조하고 있다고 생각해보겠습니다. 어플리케이션은 각자의 어플리케이션 도메인으로 어셈블리를 로딩하고 객체도 각각의 도메인 안에서 별도로 생성합니다. 그림처럼 프로세스 A에 있는 도메인A와 도메인 B에는 동일한 타입의 객체들이 독립적으로 존재하게 되는 것입니다. 어플리케이션 도메인간에는 객체의 인스턴스 멤버뿐만 아니라 정적 멤버도 공유가 되지 않습니다. 즉 완전히 독립된 실행 공간입니다. 어플리케이션 도메인은 어플리케이션의 논리적인 독립된 실행 공간이라고 정의할 수 있겠습니다. 이 공간은 .NET에서 AppDomain이라는 클래스로 표현됩니다.

어플리케이션 도메인에는 어플리케이션에 필요한 속성들이 기본적으로 설정됩니다:어플리케이션 이름, 설정 파일(config) 위치, 참조되는 다른 어셈블리들이나 다른 파일들을 검색하기 위한 기준 디렉토리 등등. 그리고 어플리케이션 설정 파일(.config)을 통해서 사용자가 정의한 내용들도 이곳에 로딩됩니다. 어플리케이션 도메인의 기본 속성중에서 방금 언급한 “파일을 검색하기 위한 기준 디렉토리” 이것이 바로 어플리케이션 디렉토리(Application Directory)입니다.

OS 프로세스 하나에는 앞에서 본대로 여러 개의 AppDomain 인스턴스가 존재할 수 있습니다. 그러나 CLR 인스턴스는 OS 프로세스에 하나만 존재할 수 있습니다. 이 사실이 이슈가 될 수 있는 경우는 하나의 머신에 버전이 다른 .NET 프레임워크가 같이 설치되어 있는 경우입니다.

v1.1로 만들어진 어플리케이션에서 v2.0으로 만들어진 어셈블리를 사용하려면 문제가 됩니다. v1.1 어플리케이션이 실행되면 그 어플리케이션용 프로세스에는 v1.1의 CLR이 로딩되어 있습니다. 그런 상황에서 v2.0의 어셈블리를 어플리케이션에서 로딩하려고 하면 v2.0의 CLR은 로딩될 수 없고 결과 v1.1의 CLR에서 생성한 AppDomain 객체에서 v2.0의 어셈블리를 사용해야 하는데 이것은 .NET에서 지원하지 않고 있는 호환성입니다.

그러나 만약 v2.0에서 만들어진 어플리케이션에서 v1.1로 만들어진 어셈블리를 사용하려고 한다면 이것은 가능합니다. v2.0의 AppDomain에서 v1.1로 만들어진 어셈블리는 정상적으로 실행될 수 있는데 이것은 .NET에서 지원하는 호환성입니다. 정리하면 이렇습니다.

구버전(v1.1)의 어플리케이션 -> 최신버전(v2.0) 어셈블리 사용 ( 불가 )

최신 버전(v2.0) 어플리케이션 -> 구버전(v1.1) 어셈블리 사용 ( 가능 )

이것은 하나의 OS 프로세스에 하나의 CLR 버전만이 로딩될 수 있다는 사실을 알고 있다면 쉽게 이해할 수 있는 문제입니다.

■ 어플리케이션 (기본)디렉토리(Application Directory)

앞에서 ClickOnce 어플리케이션이 설치되는 디렉토리는 사전에 알 수 없다고 했습니다. 이것은 ClickOnce 어플리케이션 개발자는 특정 파일에 접근하기 위해서 드라이브 문자로 시작하는 절대 경로를 사용해서 개발할 수 없다는 의미입니다. 그러나 다운로드된 어떤 파일을 어플리케이션의 코드상에서 직접 검색해서 접근해야 한다면 현재 실행되고 있는 어플리케이션이 기준으로 삼고 있는 디렉토리가 있으면 가능할 것입니다. 그 어플리케이션이 디렉토리 검색을 할 때 사용할 수 있는 기준 디렉토리가 있고 어플리케이션에서는 그 값을 기준으로 해서 상대적인 검색을 하게 되면 특정 파일에 접근할 수 있게 되는 것입니다. 이때 사용하는 기준 디렉토리를 어플리케이션 기본 디렉토리(application base directory)라고 합니다. .NET 프레임워크에서는 설정 파일(.config)이 있는 디렉토리를 기본 디렉토리로 정의하고 있습니다. 만약 설정 파일이 없는 경우는 실행 파일(exe)이 있는 디렉토리가 있는 위치가 됩니다. ClickOnce에서는 .NET이 제공하는 이 값을 사용해서 파일을 관리하게 됩니다.

1071327086

그림 어플리케이션 기본 디렉토리

그림처럼 어플리케이션 기본 디렉토리는 어플리케이션의 논리적인 공간이 어플리케이션 도메인과 어플리케이션의 실제 물리적인 공간을 연결시켜주는 값이라고 볼 수 있습니다. 이 값은 AppDomain 클래스에는 BaseDirectory라는 정적 속성으로 표현됩니다.

AppDomain.CurrentDomain.BaseDirectory

이 API를 이용하면 상당히 프로그램적으로 파일을 관리하는 프로그램을 만들 수 있게 됩니다. 다음과 같은 코드를 보겠습니다.

Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "MyAssembly.dll");

assembly.LoadFrom() 메소드는 어셈블리에 대한 경로를 받아서 코드상에서 동적으로 어셈블리를 로딩할 수 있는 메소드입니다. ClickOnce 어플리케이션에서도 동적으로 파일을 로딩해야 하는 경우가 있을 수 있습니다. 그러나 불행히도 개발자는 어플리케이션 디렉토리가 어떤 이름으로 생성될 지 모릅니다. 이런 경우 BaseDirectory 속성 값을 이용하면 된다는 것입니다. 이 코드는 재 사용성에서 아주 유연한 코드가 될 수 있습니다.

이처럼 어플리리케이션 디렉토리를 기준으로 하면 ClickOnce 어플리케이션이 실제로 설치되는 물리적인 경로는 모르더라도 코드상에서도 파일 시스템상의 파일들을 자유롭게 관리할 수 있게 됩니다.

■ 어플리케이션 시작 디렉토리(Application Startup Directory)

어플리케이션 디렉토리와 비슷한 개념으로 어플리케이션 시작 디렉토리(startup directory)라는 것이 있습니다. 이것은 어플리케이션 디렉토리와 약간 다른 개념입니다. ClickOnce 어플리케이션이 부분적으로만 신뢰할 수 있는 경우라면 프로그램 AppLaunch.exe의 호스팅을 받아서 실행된다고 했습니다. 부분 신뢰 어플리케이션의 시작 프로그램은 AppLaunch.exe이고 실행 어플리케이션은 해당 ClickOnce 실행 프로그램이 되는 것입니다. 이때 이 ClickOnce 어플리케이션의 시작 디렉토리는 AppLaunch.exe가 있는 .NET 프레임워크가 설치된 경로가 됩니다. 그러나 어플리케이션 기본 디렉토리는 ClickOnce 어플리케이션에 대한 경로가 됩니다. 다음을 보면 부분 신뢰 어플리케이션의 시작 디렉토리와 기본 디렉토리가 서로 다르다는 것을 알 수 있습니다.

Application.StartupPath 값

- C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727

AppDomain.CurrentDomain.BaseDirectory 값

- C:\Documents and Settings\ighwang\Local Settings\Apps\2.0\

34EZGTJ6.BGC\57XMAOKQ.7XK\dalb..tion_e6a5b0bc5547f078_0001.0000_d4a576a93baa50b1\

그러나 완전 신뢰 어플리케이션에서는 시작 디렉토리와 어플리케이션의 기본 디렉토리는 동일하게 ClickOnce 어플리케이션을 가리키는 경로가 됩니다.

정리하면 어플리케이션 기본 디렉토리는 현재 어플리케이션의 실행 디렉토리(working directory)이고 시작 디렉토리는 OS 프로세스가 어플리케이션을 시작했던 디렉토리(original, startup directory)라고 할 수 있겠습니다.

부분 신뢰 프로그램에서 AppDomain.CurrentDomain.BaseDirectory 속성을 사용할 수 있으려면 FileIOPermission 권한이 필요합니다.

1.1.4 그룹별 다운로드하기

어떤 경우에는 사용자의 요청에 따라(on-demand), 어플리케이션 구성 파일을 서버측에서 실시간으로 다운로드해서 실행하는 구조를 택해야 할 수도 있습니다. 이런 경우 어플리케이션 구성 파일 속성 편집 창에서 보았던 “다운로드 그룹” 속성을 이용해서 파일을 그룹별로 다운로드해서 사용할 수 있습니다. 다음은 사용자 정의 “다운로드 그룹”을 만드는 절차입니다.

1. Visual Studio에서 배포할 어플리케이션의 프로젝트를 오픈합니다.

2. 프로젝트 디자이너에서 “게시”탭을 선택합니다.

3. “응용 프로그램 파일” 버튼을 클릭해서 구성 파일 속성 편집 창을 띄웁니다.

4. 그룹별로 다운로드하기를 원하는 파일의 “다운로드 그룹” 컬럼의 출력되는 리스트 항목중에서 “(새로 만들기…)”항목을 선택해서 그룹명으로 “My Images”라고 입력합니다.

5. 다른 파일을 선택해서 “다운로드 그룹” 속성을 선택하면 이제 선택 리스트 항목들중의 하나로 “My Images”가 포함되어 있습니다. 이 항목을 선택합니다.

이제 두 파일은 “My Images”그룹으로 묶이게 되었습니다. 이렇게 사용자 정의의 그룹으로 묶인 파일들은 게시를 하게 되면 그 결과물에는 포함됩니다. 그러나 사용자가 자신의 머신에 초기 설치를 하거나 업데이트 버전을 설치해도 사용자 정의 그룹으로 묶인 파일은 다운로드되지 않습니다. 직접 ClickOnce 배포 API를 사용해서 프로그램적으로 다운로드 및 로딩을 구현해야 합니다. 필요한 API들은 네임스페이스 System.Deployment.Application에 포함되어 있습니다.

코드 그룹별 다운로드하기

private void btnGroupDownlad_Click(object sender, EventArgs e)

{

//ClickOnce 어플리케이션인지를 확인합니다.

if (ApplicationDeployment.IsNetworkDeployed)

{

//현재 배포 객체에 대한 참조를 구합니다.

ApplicationDeployment current = ApplicationDeployment.CurrentDeployment;

//원하는 그룹의 파일들이 아직 다운로드되지 않았는지를 확인합니다.

if (!current.IsFileGroupDownloaded("My Images"))

{

//1. 동기적으로 파일을 다운로드하는 코드입니다.

//1.1 아직 다운로드 되지 않았다면 파일을 먼저 다운로드 합니다.

current.DownloadFileGroup("My Images");

//1.2 파일이 다운로드되면 파일을 사용합니다.

UsingMyImages();

// //2. 비동기적으로 파일을 다운로드하는 코드입니다.

// //2.1 파일 다운로드 완료를 나타내는 이벤트에 핸들러를 등록합니다.

// current.DownloadFileGroupCompleted += new

// DownloadFileGroupCompletedEventHandler(current_DownloadFileGroupCompleted);

// //2.2 다운로드를 비동기적으로 시작합니다.

// current.DownloadFileGroupAsync("My Images");

}

else

{

//해당 그룹을 이미 다운로드했다면

UsingMyImages();

}

}

else

{

// ClickOnce 어플리케이션이 아닌 경우

UsingMyImages();

}

}

void current_DownloadFileGroupCompleted(object sender, DownloadFileGroupCompletedEventArgs e)

{

//그룹의 파일들이 비동기적으로 다운로드가 완료되고 나면 파일을 이용합니다.

UsingMyImages();

}

//다운로드된 이미지를 사용합니다.

private void UsingMyImages()

{

string[] arrImages = System.IO.Directory.GetFiles(@".\My Images", "*.bmp");

for(int i = 0; i< arrImages.Length; i++) // string file in arrImages)

{

Bitmap myImage = new Bitmap(arrImages[i]);

PictureBox pbImage = new PictureBox();

pbImage.Image = myImage;

pbImage.Size = new Size(100,100);

pbImage.SizeMode = PictureBoxSizeMode.Zoom;

pbImage.Location = new Point(100*i, 0);

pnlImages.Controls.Add( pbImage);

}

}

샘플 코드는 버튼을 클릭했을 때 “My Images”그룹에 포함된 이미지들을 직접 다운로드해서 로딩하는 코드입니다. 먼저 IsNetworkDeployed 속성을 이용해서 어플리케이션이 ClickOnce로 배포된 어플리케이션인지를 확인합니다. 만약 ClickOnce를 이용해서 배포된 어플리케이션이 아니라면 예를 들어 개발중에 로컬에서 실행시키는 경우에는 배포용 API를 사용해서 이미지를 로딩할 수 없습니다. 이런 경우는 다운로드하는 과정없이 어플리케이션 디렉토리에서 바로 이미지를 로딩해야 합니다.

일단 ClickOnce로 배포된 어플리케이션으로 확인된 경우는 현재 “My Images”그룹의 파일이 다운로드되어 있는지를 확인합니다. 이때 사용하는 API가 IsFileGroupDownloaded() 메소드입니다. 이때 그룹명을 메소드의 인자로 전달합니다. 그룹의 파일 목록이 변경되었다면 게시 페이지에서 다운로드 그룹의 목록을 변경한 다음 파일어플리케이션을 새로운 버전으로 다시 게시해야 합니다. 이렇게 하고 나서 새로운 버전의 어플리케이션을 설치하고 나서 실행시키면 IsFileGroupDownloaded()는 false를 반환합니다.

만약 아직 해당 그룹의 파일이 다운로드되지 않았다면 실제로 서버에 접근해서 파일을 다운로드 합니다. 파일 다운로드는 동기적으로도 가능하고 비동기적으로도 가능합니다. 동기적으로 파일을 다운로드한다면 그룹의 파일을 모두 다운로드해서 해당 파일을 이용한 작업이 모두 끝날때까지 다른 작업 예를 들어 윈도우상에서 프로그램을 이동시킨다거나 하는 작업을 할 수 없게 됩니다. 따라서 동기적인 작업이 꼭 필요한 경우가 아니라면 비동기적으로 파일을 다운로드하는 방식이 권장됩니다. 동기적으로 파일을 다운로드하려면 DownloadFileGroup() 메소드를 그리고 비동기적으로 파일을 다운로드하고 싶다면 DownloadFileGroupAsync() 메소드를 사용합니다.

동기 메소드 DownloadFileGroup()을 이용하면 바로 뒤에서 다운로드된 파일을 사용하는 코드 UsingMyImages()를 호출하면 됩니다. 그러나 비동기 메소드 DownloadFileGroupAsync()를 호출하때는 파일 그룹의 다운로드가 완료되었을 알려주는 이벤트 DownloadFileGroupCompletedEvnetHandler를 이용해야 합니다. 코드에서는 파일 그룹의 다운로드가 완료되면 current_DownloadFileGroupCompleted() 메소드를 호출하도록 설정하고 있습니다.

이제 핸들러 메소드에서 파일을 이용하는 코드를 호출합니다. 다음은 파일을 그룹별로 다운로드하고 이용하는 전체 코드입니다.

1067175491

그림  프로그램적으로 파일 다운로드하기

“My Images 로드” 버튼을 클릭하면 “My Images” 그룹에 포함된 파일을 다운로드해서 첫번째 Panel 컨트롤에 로딩합니다. UsingMyImages() 메소드에서는 다운로드된 이미지 파일들을 Directory.GetFiles() 메소드를 이용해서 접근하고 있습니다. 이 메소드는 파일들이 포함되어 있는 폴더 “My Images”를 현재 디렉토리에 대한 상대 경로로 표현하고 있습니다. 이 현재 디렉토리란 바로 어플리케이션이 실행 되고 있는 디렉토리로서 어플리케이션 디렉토리(Application Directory)로 표현됩니다.

참고로 이미지 파일을 프로젝트에 포함시키면 기본적으로 “빌드 작업”은 “내용”으로 선택됩니다. 그래서 게시를 하면 게시 결과물에 포함됩니다. 그러나 이미지 파일에 대한 “출력 디렉토리로 복사” 속성이 기본적으로 “복사 안 함”으로 선택되어져 있습니다.

1329825048

그림  출력 디렉토리로 복사 속성

이 속성이 “복사 안 함”으로 선택되면 빌드를 해도 파일이 빌드 폴더로 복사되지 않습니다. 따라서 어플리케이션을 개발 당시 로컬에서 실행키시면 어플리케이션 디렉토리에 파일을 찾을 수 없어서 에러가 발생하게 됩니다. 이 속성을 “항상 복사” 선택해서 해당 파일이 빌드 디렉토리로 복사되도록 설정하면 됩니다.

1.1.5 ClickOnce와 NTD 배포 결합

기업에서 사용하는 ERP 시스템은 그 구성 어셈블리 파일의 수가 수백이 되고 그 어셈블리에 포함된 화면들의 수는 모두 합치면 수천개가 되는 경우도 있습니다. 각 화면들은 팀별로 개발이 되고 개발 도중에는 계속해서 화면이 수정되고 어셈블리도 추가, 제거를 반복하게 됩니다. 따라서 이런 경우는 그 어셈블리를 모두 실행 프로젝트에 추가시켜 게시하는 것은 바람직하지 못한 구조라 하겠습니다.

차라리 이런 경우는 내용이 없는 실행 프로그램(EXE) 하나만 작성해서 ClickOnce로 클라이언트 머신에 설치하고 그 내용이 되는 실제 업무 화면이 포함되어 있는 어셈블리는 사용자가 메뉴를 클릭하는 순간 실시간(on-demand 방식)으로 배포 서버에서 다운로드해서 해당 업무 화면을 실행 프로그램의 내용으로 로딩하는 구조로 설계하는 것이 효율적입니다. 업무 개발 팀에서는 언제든지 해당 어셈블리를 수정해서 배포 서버에 있는 이전 버전의 어셈블리를 덮어쓰기만 하면 사용자는 항상 최신 버전의 업무 화면을 볼 수 있게 됩니다.

화면 수가 많은 프로젝트에서는 실행 프로그램에 출력되는 메뉴 목록 자체도 고정시킬 수가없습니다. 데이터베이스에 저장시켜두고 실행프로그램이 로딩될 때 그곳에서 읽어와서 메뉴를 출력시켜 주는 구조가 될 것입니다.

1104107713

그림 7‑12 ClickOnce와 NTD결합 어플리케이션

실행 프로그램(EXE) 자체는 ClickOnce로 배포되어 클라이언트에 설치됩니다. 실행 프로그램이 구동되면서 메뉴 목록을 데이터 소스로부터 조회해서 출력합니다. 최종 사용자는 출력된 메뉴 목록중에서 하나를 선택합니다. 사용자가 메뉴를 선택하면 화면에 해당하는 클래스 및 포함 어셈블리에 대한 정보가 실행 프로그램으로 건네져야 합니다. 프로그램은 메뉴에 해당하는 화면이 포함된 어셈블리를 파일 서버에 요청(On-Demand)해서 다운로드합니다. 그러고 나서 동적으로 해당 화면의 인스턴스를 생성해서 적당한 위치에 출력하게 됩니다.

업무 화면들이 포함된 어셈블리를 동적으로 다운로드할 수 있는 API는 ClickOnce의 배포 API를 사용하지 않습니다. .NET 프레임워의 Assembly 클래스에는 v2.0이전서부터 LoadFrom(), LoadFile()처럼 동적으로 어셈블리를 로딩할 수 있는 메소드들이 제공되고 있었습니다. 이런 메소드를 사용해서 어플리케이션 어셈블리를 다운로드하는 것은 .NET 프레임워크의 NTD(No-Touch Deployment) 메커니즘을 이용합니다.

이런 경우는 실행 프로그램(EXE)는 ClickOnce로 배포하고 사용자가 메뉴를 클릭했을 때 메뉴에 해당하는 업무 화면과 그 화면이 포함된 어셈블리들은 NTD 방식으로 배포하는 혼합된 구조라고 할 수 있습니다.

앞에서 본 “OnDemandDownload” 프로젝트의 실행 폼을 보면 “LoadFrom” 버튼이 있습니다. LoadFrom() 메소드에 전달되는 인자로는 어셈블리에 대한 전체 경로와 인스턴스를 생성하고 싶은 타입명이 필요합니다. 예제 프로젝트에는 “LoadFromAssembly”가 있습니다. 여기서 빌드된 어셈블리를 호출해서 테스트해 볼 수 있습니다.

어셈블리 경로

- D:\ClickOnce원고\최종코드\07\리소스배포\LoadFromAssembly\bin\Debug\LoadFromAssembly.dll

클래스명

- LoadFromAssembly.LoadFromForm

어셈블리경로란에는 LoadFromAssembly.dll에 대한 적절한 경로를 변경해서 입력합니다. 클래스명은 네임스페이스를 포함한 완전한 이름을 입력합니다. 이제 “LoadFrom” 버튼을 클릭하면 다음 코드가 실행됩니다.

코드 동적으로 인스턴스 생성하기- LoadFrom()이용

private void btnLoadFrom_Click(object sender, EventArgs e)

{

//원격 서버상의 어셈블리가 포함된 디렉토리 경로(HTTP, UNC 형식도 가능)

string strAssemblyPath = txtAssemblyPath.Text;

//클래스명( 네임스페이스 포함)

string strClassName = txtClassName.Text;

//어셈블리 로딩

Assembly assembly = Assembly.LoadFrom(strAssemblyPath);

//어셈블리내 타입의 인스턴스 생성

object obj = assembly.CreateInstance(strClassName, true);

// 인스턴스 타입 캐스팅

Form f = obj as Form;

//이후, f 인스턴스를 사용하면 됩니다.

f.Show();

}

어셈블리에 대한 경로는 로컬 디스크 경로뿐만 아니라 원격 배포 서버에 대한 HTTP 형식(http://서버/경로/어셈블리명 ) 또는 UNC형식(\서버\경로\어셈블리명)의 경로도 가능합니다. 그리고 타입명을 입력할때는 타입의 네임스페이스를 포함한 전체명을 입력합니다.

두 인자를 이용해서 동적으로 어셈블리를 로딩하고 타입에 해당하는 인스턴스를 생성한 뒤에는 new연산자를 이용해서 생성한 일반 인스턴스처럼 필요한대로 사용하면 됩니다. 코드에서는 단순히 보여주기만 하고 있습니다.

1130954473

그림 7‑13 LoadFrom()으로 로딩된 폼

LoadFrom()으로 다운로드된 어셈블리는 ClickOnce 캐시에 저장되는 것이 아니라 NTD에 의해 다운로드되는 파일들이 저장되는 별도의 공간이 있습니다. 윈도우 탐색기를 이용하면 다운된 어셈블리를 볼 수 있습니다. “시작->실행”에 “assembly”를 입력하고 실행합니다. 출력되는 파일 탐색기에서 assembly 폴더의 아래에 Download 폴더가 보이는데 이곳을 보면 다운로드된 어셈블리를 볼 수 있습니다.

1098048945

그림 7‑14 NTD 어셈블리 캐시 상태

때로는 이렇게 ClickOnce 배포와 NTD 배포를 적절히 혼합하게 되면 더욱더 유연한 배포 방식으로 구현할 수 있습니다.

1.1.6 플러그인(Plug-In) 어플리케이션

플러그인(Plug-In)은 이미 존재하는 어플리케이션의 기능을 확장하거나 또는 사용자 정의 기능을 추가할 수 있도록 허용하는 어플리케이션에서 자주 사용하는 방법입니다. 어플리케이션을 컴파일할때는 플러그인에 대한 구체적인 정보는 알지 못합니다. 다만 플러그인으로 사용될 컴포넌트의 타입만을 알 수 있습니다. 이런 플러그인 어플리케이션의 구조는 그림과 유사합니다.

1325403059

그림 7‑15 플러그인 어플리케이션 구조

플러그인 어플리케이션이 구동되면서 현재 사용할 수 있는 플러그인 프로그램 목록을 출력시킵니다. 사용자가 그 목록중에서 하나를 선택하면 어플리케이션은 해당 프로그램을 파일 서버에 요청해서 실행시킵니다.

이미 이런 구조를 구현하는 .NET의 두가지 방법에 대해서 앞에서 알아봤습니다. 그룹별로 어셈블리를 다운로드해서 사용하는 예와 ClickOnce와 NTD 배포 방식을 결합해서 어플리케이션을 작성하는 예가 이런 구조였습니다.

두 가지 구현의 구체적인 면에서는 차이가 있습니다. 코드 그룹별로 파일을 다운로드하는 방식은 ClickOnce의 배포 API(IsFileGroupDownloaded(), DownloadFileGroupAsync(), DownloadFileGroupCompletedEvnetHandler)를 이용하고 있고 두 번째 배포 방식의 결합 어플리케이션에서는 .NET 프레임워크에서 제공하는 동적 어셈블리 로딩 메소드(LoadFrom(), Load(), LoadFile())를 이용하고 있습니다. 메소드의 차이뿐만 아니라 프로그램 파일을 로딩하는 방법도 다릅니다. ClickOnce 배포 API를 사용하게 되면 클라이언트 머신에 캐싱된 어셈블리 버전을 사용할 수가 있어서 성능상에 있어서는 더 효과적입니다. 그러나 그룹별로 다운로드하는 경우는 해당 그룹의 파일 목록이 변경되면 어플리케이션의 버전을 변경해서 다시 게시해야 합니다. 그래야 클라이언트에서 IsFileGroupDownloaded()에서 false를 반환해서 다시 그룹의 파일 목록을 다운로드받을 수가 있습니다. 그러나 어셈블리 로딩 메소드는 어플리케이션 자체를 자주 업데이트할 필요가 없습니다. 대신에 변경된 어셈블리만 파일 서버로 복사하기만 하면 됩니다.

1.1.7 어플리케이션 어셈블리 배포

ClickOnce 어플리케이션은 간단히는 하나의 실행 파일과 설정 파일(.exe.config) 그리고 매니페스트 파일들로 구성될 수 있습니다. 그러나 어플리케이션에서 참조하고 있는 다른 어셈블리들이 있을 수 있습니다. 이런 어셈블리들은 “로컬 복사” 속성이 true로 된 상태에서 게시가 되면 기본적으로 게시된 결과 파일에 포함되게 됩니다. 만약 현재 참조하고 있는 어셈블리가 GAC에 등록되어 있다면, 클라이언트 머신에 어플리케이션과 함께 캐싱되는 로컬 어셈블리로 배포할지 아니면 GAC에 등록되는 공용 어셈블리로 배포할지를 결정해야 합니다.

만약 참조되는 어셈블리를 로컬 어셈블리로 배포하겠다고 하면 사전에 클라이언트 머신에 배포해야 하는 복잡한 절차가 생략되겠지만 어떤 경우의 어셈블리는 반드시 GAC에 등록이 되어야 하는 경우도 있을 수 있습니다. 예를 들어 어플리케이션은 부분 신뢰의 어플리케이션으로 배포되더라도 그 참조되는 어셈블리는 AllowPartiallyTrustedCallersAttribute(APTCA)어트리뷰트를 가지고 GAC에 등록되어 있어야 하는 시나리오에서는 반드시 참조되는 어셈블리가 GAC에 등록되어야 합니다. GAC에 등록된 어셈블리는 권한에 제한없이 모든 작업을 할 수 있습니다. 그 중에서 APTCA 어트리뷰트를 갖는 어셈블리들은 부분 신뢰의 환경에서 구동되는 어플리케이션에서도 사용을 할 수가 있습니다. 따라서 APTCA 어트리뷰트를 갖는 어셈블리는 부분 신뢰의 환경에서 구동되는 어플리케이션이 자신에게 주어진 보안 박스 범위를 벗어나는 작업을 할 수 있는 방법이 됩니다.

어떤 경우 상용 컨트롤용 어셈블리도 비슷한 이유 때문에 사전에 미리 클라이언트 머신의 GAC에 설치되어 있어야 하는 경우도 있습니다. 컨트롤의 설명서를 참조해서 로컬 어셈블리로 배포해도 되는 건지 아니면 반드시 GAC로 등록되어야 하는지를 확인할 필요가 있습니다.

사전에 GAC에 등록할 필요가 없다는 점에서 로컬 어셈블리로 배포하는 것이 편리하기는 하지만 그렇다고 이 방식이 꼭 좋은 것만은 아닙니다. 예를 들어 어플리케이션에서 참조하고 있는 상용 컨트롤용 어셈블리와 기타 어셈블리들이 몇 백개가 된다고 해 보겠습니다. 그런 상황에서 서버에 게시된 어플리케이션의 버전 번호가 변경되어서 업데이트를 수행해야 한다고 했을 때 클라이언트측 어셈블리와 서버측 어셈블리의 버전을 비교 확인하는 작업에서 무시못할 정도의 시간이 걸립니다. 만약 어플리케이션의 업데이트가 자주 일어나야 하는 경우라면 차라리 상용 컨트롤용 어셈블리들은 최초 한번만 클라이언트의 GAC에 등록하고 배포 목록에서는 제외하는 것이 바람직할 수도 있습니다. 참조 어셈블리를 배포 목록에서 제외하려면 “로컬 복사” 속성을 false로 설정합니다.

정리하면 사전에 GAC에 등록해야 하는 어셈블리들이 있다면 부트스트래퍼용 패키지를 작성해서 사전 필수 프로그램으로서 ClickOnce 어플리케이션이 시작하기 전에 클라이언트 머신에 설치되도록 해야 합니다.

Posted by dalbong2

ClickOnce 어플리케이션은 여러 종류의 파일로 구성됩니다. 이 연재에서는 ClickOnce가 이런 파일을 관리하기 위해서 어떤 서비스를 제공하는지 그리고 프로그램적으로 어떻게 관리할 수 있는지 등에 대해서 알아봅니다. 보통 ClickOnce 어플리케이션은 다음과 같은 파일로 구성됩니다.

- 어플리케이션 어셈블리 파일과 데이터 파일들

- 어플리케이션 매니페스트 파일( .exe.manifest) : 구성 파일과 필요한 권한 정보 포함

- 배포 매니페스트(.application) : 업데이트 정책, 업데이트 위치 포함

그리고 앞의 파일들을 서명하기 위해서 필요한 인증서가 있지만 인증서는 어플리케이션에서 관리할 수 있는 파일이 아니기에 파일 관리 대상에서는 제외됩니다. 

ClickOnce에 포함될 수 있는 파일을 종류별로 보자면 상황에 따라서 불특정 타입의 파일들이 포함될 수 있습니다. 이미지 파일, XML 파일, 상용 컨트롤 파일 등 어플리케이션마다 전혀 다른 종류의 파일들이 포함될 수 있습니다.

ClickOnce가 이런 어플리케이션 구성 파일들을 클라이언트 머신으로 다운로드해서 어느 디렉토리로 캐싱하느냐는 미리 결정할 수 없습니다. 우리가 사전에 알 수 있는 것은 사용자 프로파일 디렉토리 하위로 다운된다는 것 뿐입니다.

이런 상황에서 클라이언트에 설치된 후 동적으로 파일을 읽어들이거나 또는 어셈블리를 동적으로 로딩시키는 기능을 어플리케이션에 추가하려고 한다면 어떻게 해야 할까요. 어플리케이션의 완성도나 유연성을 높이려고 한다면 디렉토리에 캐싱되어 있는 데이터 파일을 직접 읽어들여야 하는 경우도 있을 수 있고 때로는 어셈블리를 동적으로 로딩해야 하는 상황도 자주 발생하게 됩니다.

ClickOnce는 이런 경우에 대처할 수 있는 방안들을 제공하고 있고 이런 해결책을 습득하는 것이 이 연재의 목표입니다. 디렉토리에 저장되어 있는 파일에 직접 접근하기 위해서는 ClickOnce가 파일을 어떤 디렉토리 구조로 관리하는지를 먼저 이해하고 그런 다음 파일에 접근할 수 있도록 어떤 API들을 제공하는지를 확인할 필요가 있습니다.

어셈블리 파일들을 포함하여 리소스, 데이터 파일을 얼마나 자유자재로 다룰 수 있느냐는 .NET 어플리케이션의 고급스러움을 결정하는데 중요한 요소가 되는 경우가 많습니다. ClickOnce 어플리케이션은 .NET의 파일 관리 기술을 그대로 사용할 수 있으면서도 ClickOnce 어플리케이션에서만 사용할 수 있는 기능도 있습니다. .NET과 ClickOnce에서 제공하는 파일 관리 개념과 서비스를 충분히 이해하고 익힐 수 있도록 해야할 것입니다.

1.1 구성 파일 속성 편집

게시 탭 페이지의 "응용 프로그램 파일..." 버튼을 클릭하게 되면 ClickOnce로 배포될 파일 목록을 편집할 수 있는 창이 뜨게 됩니다. 기본적으로 실행 프로젝트에 포함된 파일들이 포함되어 있습니다. 선택적으로 파일들을 배포 목록에서 제거하도록 설정할 수도 있습니다. 그러나 이곳에서 배포할 파일을 추가할 수는 없습니다. ClickOnce 배포에 파일을 추가하는 방법에 대해서는 별도로 설명합니다.

 1378124003

그림 어플리케이션 구성 파일 편집 창

1.1.1 게시 상태” 속성

“응용 프로그램 파일”창을 띄우면 게시할 프로젝트에 참조되거나 포함된 파일 목록들이 기본적으로 출력됩니다. 각 파일은 “게시 상태”와 “다운로드 그룹”이라는 속성을 설정할 수 있습니다.

ClickOnce는 게시 상태라는 속성값에 따라서 해당 파일을 데이터 파일로 간주할 것인가 또는 일반 파일로 간주할 것인가를 결정합니다. ClickOnce에서는 데이터 파일과 다른 일반 파일을 클라이언트에서 저장하는 장소를 달리하고 있고 접근할 수 있는 방법도 다르게 제공하고 있습니다. 따라서 이 컬럼 값을 결정하는 일은 중요한 작업입니다. 이 컬럼값으로 선택할 수 있는 값은 몇가지 더 있습니다.

- 포함/포함(자동)

- 데이터파일/데이터 파일(자동)

- 필수구성요소

- 제외

Visual Studio는 파일의 확장자를 기반으로 해서 해당 파일의 기본적인 게시 상태를 결정합니다. 예를 들어 .xml 파일은 게시 상태값을 자동적으로 “데이터 파일”로 설정합니다. “(자동)”이라는 접미사가 붙은 값은 이처럼 Visual Studio가 확장자를 기반으로해서 기본적으로 선택했음을 의미합니다. 자동으로 설정된 값을 다른 값으로 변경할 수도 있습니다. 예를 들어 .txt 파일은 기본적으로 “포함(자동)”으로 설정되지만 이 파일을 데이터 파일로 설정할 수도 있습니다.

“필수구성요소”라는 값은 게시에 포함되는 파일이 참조에 의한 어셈블리인 경우에 선택할 수 있는 값중의 하나로 나타납니다. 만약 기본적으로 선택되어 있는 “포함(자동)”을 “필수 구성 요소”로 변경하면 “이 어셈블리는 클라이언트 머신의 GAC(Global Assembly Cache)에 등록이 되어 있을 것입니다”라는 표현이 되고 해당 어셈블리는 배포에서 제외됩니다.

마지막으로 “제외”를 선택하면 말 그대로 해당 파일은 배포에서 제외됩니다. 해당 파일이 서버에 존재하고 그것을 동적으로 로딩해야 하는 경우에 이런 선택을 할 수 있을 것입니다.

구성 파일 편집 창을 보면 “다운로드 그룹”이라는 컬럼도 있습니다. 이 속성 값을 사용해서 어플리케이션을 구성하는 파일들을 그룹별로 나눌수가 있습니다. 그래서 나중에 프로그램적인 방법으로 파일들을 그룹 단위로 다운로드할 수가 있습니다.

파일을 배포 목록에 포함시켰을 때 기본적으로 게시 상태가 “포함” 또는 “데이터”로 되어 있는 파일은 기본적으로 “(필수)”라는 그룹으로 분리됩니다. 이렇게 “(필수)”로 분리된 파일은 어플리케이션이 클라이언트에서 설치되거나 또는 업데이트될 때 자동으로 다운로드됩니다. 게시 상태가 “데이터 파일”로 되어 있는 것은 그룹을 변경할 수 없습니다. “포함”으로 되어 있는 파일들만 그룹명을 변경할 수 있습니다.

사용자 정의의 다운로드 그룹명을 부여한 파일들은 초기 설치나 업데이트의 과정에서 다운로드되지 않습니다. 뒤에서 설명할 다운로드 API들을 사용해서 프로그램적으로 직접 다운로드해야 합니다. 파일을 그룹으로 분류하고 사용자의 요청에 따라서 실시간으로 파일들을 그룹별로 다운로드하는 방법은 뒤에서 다룹니다.

1.1.2 파일 그룹핑하기

ClickOnce로 배포될 파일들을 그룹별로 분리하는 파일의 그룹을 분리하는 방법은 “포함”으로 설정된 파일의 다운로드 그룹의 컬럼을 선택하면 “(새로 만들기…)”라는 항목이 있습니다. 직접 컬럼 값으로 그룹 이름을 나타내는 텍스트를 입력하면 됩니다.

 1207714558

그림 다운로드 그룹 추가

새로 추가한 다운로드 그룹명은 컬럼의 드롭다운 리스트에 출력되어 선택할 수 있게 됩니다. 이렇게 하면 같은 그룹 이름을 갖는 파일들끼리 분리할 수가 있게 됩니다.

이렇게 “다운로드 그룹” 속성을 “(필수)”가 아닌 사용자 정의 값으로 설정한 파일들은 앞에서 말한 대로 어플리케이션을 초기 설치하거나 업데이트 설치할때는 다운로드되지 않습니다. 대신에 프로그램적으로 직접 다운로드해야 합니다.

1.1.3 ClickOnce 구성 파일 분류

ClickOnce에서는 어플리케이션을 구성하는 파일중에서 데이터 파일(data files)은 일반 구성 파일과는 다르게 취급합니다. 앞에서 말한것처럼 특정 확장자의 파일을 데이터 파일로 확정해서 분류하는 것은 아닙니다. 같은 파일을 데이터 파일로 취급할 수 있지만 다른 어플리케이션에서는 일반 파일로 분류할 수 있습니다.

흔히 사람들이 말할때는 어플리케이션의 상태 정보를 가지고 있거나 또는 값이 변경될 수 있는 정보를 가지고 있으면서 어플리케이션이 종료되고 다시 시작될때에도 그 값을 여전히 보존하고 있는 파일들을 데이터 파일로 취급합니다. 그러나 ClickOnce 입장에서는 게시를 할 때 게시 상태 컬럼값이 “데이터 파일”로 설정된 것만을 데이터 파일로 간주해서 별도의 관리 메커니즘을 사용합니다.

이 연재에서도 어플리케이션을 구성하는 파일을 게시 상태값에 따라 두 부류로 구분해서 설명합니다.

- 어플리케이션 파일

- 데이터 파일

특별한 부연 설명없이 “어플리케이션 구성 파일”이라고 하면 데이터 파일을 제외한 일반 구성 파일이라는 의미로 사용하겠습니다.

Posted by dalbong2

■ 이미 배포한 애플리케이션의 경우

.NETv2.0기반의 ClickOnce 애플리케이션에서는 인증서가 만료된 경우 만료 기간을 연장하여 업데이트를 할 수 없습니다. 만료 기간을 연장하면 전혀 새로운 인증서가 되어 버립니다. 따라서 인증서가 만료되면 기존의 ClickOnce 애플리케이션을 프로그램추가/제거에서 삭제하고 다시 설치해야 합니다.
구글링을 해보면 이를 해결하기 위한 노력이 있습니다. 그중에서 다음 링크는 그동안 봐 왔던 해결책중에서 개인적으로 제일 맘에 듭니다.

ClickOnce and Expiring Code Signing Certificates
http://www.jamesharte.com/blog/?p=11

모든 사용자들이 프로그램 추가/제거에서 애플리케이션을 삭제하고 다시 설치하는 작업이 필요없도록 하기 위한 방안을 설명하고 있습니다.

다음 링크도 참고할 수 있습니다.
http://support.microsoft.com/Default.aspx?kbid=925521
http://www.may.be/renewcert/
http://blogs.msdn.com/danielma/archive/2007/03/19/clickonce-and-expired-certificates.aspx

■ 앞으로 배포할 애플리케이션의 경우

이미 배포한 애플리케이션은 어쩔 수 없다지만, .NET 2.0과 Visual Studio 2005를 이용해서 개발한 애플리케이션을 배포할 경우라면 좋은 방법이 있습니다. 테스트 인증서를 만드는 컴의 현재 시간을 아주 먼 훗날로 세팅합니다. 예를 들어 2099년 정도로. 그런 다음 테스트 인증서를 만들면 그 인증서의 만료 날짜는 2100년이 됩니다.  관련 웹 페이지 문서 링크입니다.
ClickOnce Expired Certificate

Posted by dalbong2