여름의 정점을 지나가고 있는 듯 합니다. 날씨가 무진장 덥네요,,
장마가 끝나면 폭염이라는데,, ㅡ,.ㅡ;;;
식상한 멘트이긴 하겠지만, 이런 날일 수록 정말 건강이 중요한 것 같습니다.
덥다고 에어컨 바람만 쐬면 냉방병 걸리고, 밤에 잘때도 너무 에어컨 틀어놓으면 감기 걸리니깐,,
아프지 않는게 최고죠~ 특히, 저 같이 혼자 자취하는 자취남들은,, ^^
아무쪼록 건강하세요~
이번 아티클의 주제는 Windows Service를 이용한 호스팅입니다.
윈도우즈 서비스에 대해서 모르는 분이 계실까요?
네~ 계실 수 있죠,, 그래서 간략하게 윈도우즈 서비스에 대해 설명을 하고 넘어가도록 하겠습니다.
What is Windows Service?
NT 서비스라고 알려져 있기도 한 윈도우즈 서비스는 자체의 Windows 세션에서 실행되며, 윈도우즈가 구동을 하고 있는 동안 계속 동작을 해야하는 이러한 작업이 필요할 때, 윈도우즈 서비스의 형태로 구현하여 사용할 수 있습니다. 또한, 윈도우즈 서비스는 윈도우즈의 시작과 함께 자동으로 시작할 수 있기 때문에, 서버가 동작함과 동시에 항상 동작해야하는 응용 프로그램이라면 윈도우즈 서비스로 구현하는 것을 고려해 볼 필요가 있을 듯 합니다.
윈도우즈 서비스의 경우는 따로 UI를 가지고 있지 않기 때문에, 윈도우즈 사용자 또는 관리자가 윈도우즈 서비스의 구동 상태를 상세하게 확인할 수는 없습니다. 다만, 윈도우즈에 기본으로 제공되는 서비스 제어 관리자를 통해 서비스의 시작, 중지, 일시정지 등을 컨트롤 할 수 있습니다. (아래 그림은 서비스 제어 관리자의 모습입니다.)
그림을 보니 윈도우즈 서비스가 어떠한 것들을 말하는 것인지 알겠죠? ^^ (역시, 백문이 불여일견,, 엣헴~)
Windows 서비스 응용프로그램에 대한 소개와 개발 방법에 대해 좀 더 자세한 정보를 원하시는 분은 다음 링크를 참고하시기 바랍니다.
이제 본격적으로 이 윈도우즈 서비스를 이용하여 WCF 서비스를 호스팅하는 방법에 대해 적어보겠습니다.
고고씽~
Windows Service를 이용한 WCF 서비스 호스팅
WCF 서비스를 호스팅하는 방법에는 IIS 호스팅, WAS 호스팅, 그리고 셀프 호스팅으로 나뉘어진다고 얘기했었습니다.
그럼, Windows Service를 이용한 호스팅은 어디에 속할까요?
네~!! 당연히 셀프 호스팅에 속합니다. 그리고, 셀프 호스팅에 속한다는 말은 직접 ServiceHost 클래스를 이용하여 호스팅을 구현 해야 한다는 말이기도 합니다.
결국, 여기서 제가 하고 싶은 말은 이것입니다. "Windows Service를 이용하여 WCF 서비스를 호스팅하기 위해서는 ServiceHost 클래스를 이용하여 서비스 호스팅하는 부분을 직접 구현해야 한다."
WCF 서비스를 호스팅하기 위한 특별한 코드가 필요한 것은 아닙니다. 여타의 다른 윈도우즈 서비스를 개발하는 것과 같이 개발을 하고, WCF 서비스를 호스팅하는 코드만 추가를 해주면 된다는 것입니다~!!
아마, 윈도우즈 서비스를 한번이라도 개발 해 보신 분은 어렵지 않게 개발을 할 수 있을 것 같습니다.
우선, 윈도우즈 서비스 응용프로그램을 만들기 위해 Visual Studio에서 새 프로젝트를 생성할 때, "Windows 서비스" 템플릿을 선택합니다.
프로젝트를 생성하면 Service1.cs 와 Program.cs 파일이 생성되어 있음을 확인할 수 있습니다.
Program.cs 파일은 이 응용 프로그램의 진입점을 가지고 있으며, 실제 서비스가 동작을 할 때의 코드는 Service1.cs 파일에 구현을 하면 됩니다.
그리고, Service1.cs 파일을 확인하면 Service1 클래스가 선언되어 있으며, 이 클래스는 ServiceBase 클래스를 상속 받는 것을 확인할 수 있습니다.
그리고, Service1 클래스에는 기본적으로 OnStart와 OnStop 메소드가 재 정의(override)되어 있는데 메소드의 이름으로 짐작할 수 있겠지만, 각각 서비스가 시작할 때 와 서비스가 멈췄을 때의 동작을 수행하는 메소드입니다.
이 메소드 외에, OnContinue, OnPause, OnShutdown 메소드를 재정의하여 서비스를 일시정지에서 다시 시작했을 때, 서비스를 일시정지 했을 때, 그리고 컴퓨터가 종료될 때의 동작을 구현할 수 있습니다.
이번 아티클의 주제는 Windows 서비스를 이용한 WCF 서비스의 호스팅이니 만큼, Windows 서비스 구현에 대한 자세한 내용은 앞서 걸어놓은 MSDN의 링크로 대신하고 넘어가겠습니다.
(Windows 서비스 응용 프로그램의 구현에 대한 자세한 내용을 모르셔도 아래 내용을 따라 하시면, 아마 무사히 WCF 서비스를 호스팅 할 수 있으실겁니다. )
음,, 일단 우리가 만들 Windows 서비스의 이름을 먼저 변경해보도록 하겠습니다. 이름을 변경하지 않아도 별 상관은 없지만 우리가 만든 서비스란 것을 알아보기 위해서 변경하는게 낫겠죠~ 서비스의 이름을 변경하는 것은 어렵지 않습니다. Service1 클래스의 생성자에서 바꿔줘도 되고, Service1.Designer.cs 파일을 확인하면 Service1 의 partial 클래스가 정의되어 있는데, 이곳에 위치한 InitializeComponent 메소드 내에서 변경해줘도 됩니다. 저는 InitializeComponent 메소드 내에서 다음과 같은 코드로 서비스의 이름을 지정 해주었습니다.
privatevoid InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = "RuAA WCF Service"; // 서비스의 이름 변경
}
이제 본격적으로 WCF 서비스를 호스팅 해보도록 하겠습니다.
우선, WCF 서비스에서 사용할 ServiceContract와 DataContract 들을 선언해주어야 겠죠. 다음과 같은 인터페이스와 클래스들을 선언해주었습니다.
// ServiceContract 정의
[ServiceContract]
publicinterfaceIProductService
{
[OperationContract]
Product GetProductInfo(int id);
}
// DataContract 정의
[DataContract]
publicclassProduct
{
[DataMember]
publicint ID;
[DataMember]
publicstring Name;
[DataMember]
publicstring Company;
[DataMember]
publicint Price;
}
// 서비스 구현
publicclassProductService : IProductService
{
List<Product> productList;
public ProductService()
{
productList = newList<Product>();
productList.Add(newProduct {
ID = 1,
Name = "ABC Chocolate",
Company = "RuAA Inc.",
Price = 5300
});
}
publicProduct GetProductInfo(int id)
{
var item = (from p in productList
where p.ID == id
select p).FirstOrDefault();
return item;
}
}
항상 그랬지만, 서비스의 역할은 심플합니다. 복잡한 서비스를 만드는게 목표는 아니잖아요~ ㅎ
이제 이렇게 구현된 서비스를 호스팅하는 일 만을 남겨두었습니다.
앞에서도 밝혔듯이 Windows 서비스에서 WCF 서비스를 호스팅 하는 것은 Self Hosting 에 포함되는 것이기 때문에 ServiceHost 클래스를 직접 구현해야 합니다.
근데 이 코드를 어디에 위치해야 할까요? 예상하신 분들이 분명 계실겁니다.
바로 바로 바로~~!! Service1 클래스의 OnStart 메소드입니다.
또한, 생각해야 할 것이 있습니다.
Windows 서비스가 멈추었을 때, 당연히 WCF 서비스의 호스팅을 멈추어줘야 겠죠. 그래서 OnStop 메소드 내부에 WCF 서비스의 호스팅을 멈추게 하는 코드도 포함이 되어야 할 것입니다.
Service1 클래스를 다음과 같은 코드로 구현해보았습니다.
// ServiceHost 클래스의 인스턴스를 클래스의 멤버로 선언하여 Service1 클래스의 메소드에서 접근이 가능하도록 한다.
privateServiceHost svcHost;
// 윈도우즈 서비스가 시작할 때의 동작을 구현한다.
protectedoverridevoid OnStart(string[] args)
{
// ServiceHost 인스턴스 생성 string baseUrl = "http://10.30.101.84:9090/ProductService";
EventLog.WriteEntry("RuAA Service Stopping", EventLogEntryType.Information);
}
위의 코드에서 그렇게 어려운 점은 보이지 않습니다. Self Hosting을 해보셨던 분이라면 말이죠~
혹시나, Self Hosting을 해보지 못하신 분이 있다면, 첫 WCF 만들기 아티클을 참고해주시기 바랍니다.
각 주요 코드에 주석도 남겨놨으니 따로 긴 설명은 필요없을 듯 합니다. ^^
이제, 서비스의 구현은 모두 끝이 났습니다. 하지만, 우리가 만든 이 Windows 서비스를 컴퓨터에 설치하기 위해서는 설치 관리자가 필요합니다. (설치 관리자에 대한 자세한 설명은 이곳으로~)
Service1.cs 파일의 디자이너 보기에서 마우스 우측 클릭한 후 나타나는 메뉴에서 "설치 관리자 추가"를 선택합니다.
그러면 프로젝트에 ProjectInstaller.cs 라는 파일이 생기는데, 이 클래스의 디자인 뷰를 확인하면, serviceProcessInstaller1, serviceInstaller1 이라는 이름의 컨트롤들이 포함되어 있는 것을 확인할 수 있습니다.
이 컨트롤들의 속성을 다음 그림과 같이 수정해보겠습니다.
이제서야, 정말 Windows 서비스를 설치할 모든 준비가 끝이 났습니다.
이 Windows 서비스를 설치하기 위하여 Visual Studio 명령 프롬프트를 실행시킵니다. 이때, 관리자 권한으로 실행시켜 주셔야 합니다. 그렇지 않으면 Windows 서비스가 제대로 설치가 되지 않는 경우가 있더라구요~ ㅎ
그리고, 이 프로젝트 폴더의 bin/Debug 폴더로 이동한 후에 다음 그림과 같이, Installutil 이란 명령어를 이용하여 우리가 만든 Windows 서비스를 설치합니다. (Windows 서비스 설치에 대한 자세한 설명은 이곳으로~!!)
참고로, 설치 파일은 프로젝트를 빌드한 후에 생성된 exe 파일입니다.
위의 그림처럼 "트랜잭트 설치가 완료되었습니다." 란 메세지가 떨어졌다면 아무 에러없이 Windows 서비스가 설치되었다는 말입니다. 그럼, 서비스 제어 관리자에서 확인해 보도록 하겠습니다.
네,, Windows 서비스가 올라와있는 것을 볼 수 있네요~ 그럼 시작 버튼을 클릭하여 Windows 서비스를 시작할 수 있습니다. 그리고, Windows 서비스가 시작하면서 우리가 구현한 WCF 서비스가 호스팅되겠죠,, ㅎㅎ
그럼, 이 WCF 서비스가 제대로 호스팅 되고 있는 것인지 확인을 해보아야 할겁니다,,
역시나, 지금까지 그래왔듯이 콘솔 어플리케이션을 이용하여 확인해보도록 하겠습니다.
솔루션에 콘솔 어플리케이션 프로젝트를 추가하고, 이 프로젝트에 서비스 참조를 시켜주었습니다. 이때 너무나도 당연하겠지만, 서비스의 주소는 ServiceHost 인스턴스에 추가 시켜준 엔드 포인트의 주소를 넣어주셔야 합니다.ㅎ
위의 그림처럼, 서비스를 제대로 찾으면~ 모든 것이 OK!!! 입니다. ㅎㅎ
이 서비스를 이용한 콘솔 어플리케이션 코드는 생략해도 되겠죠?? ㅎㅎ (이번 글이 너무 길어진 것 같아,, ^^;;;;)
그래서~~~ 결과 화면만 보여드리겠습니다 ㅎㅎ
후아~ 결과가 잘 나오는 군요,, ^^
이로써, 이번 포스팅도 무사히(?) 끝을 낼 수 있게 되었습니다. ㅎㅎㅎ
이번 포스팅은 Windows 서비스에 대한 설명과 구현에 대한 내용을 함께 적다보니 조금 길어진 것 같습니다. 물론 한번이라도 Windows 서비스 응용 프로그램을 구현해보신 분이라면 아는 내용들이겠지만, 혹시나 모르시는 분도 있을 것 같아 자세한 내용까지 설명드리지 못했지만 최소한의 내용을 포함시켰습니다.
조금 내용이 길어졌지만 끝까지 읽어주신 분들께 심심한 감사의 인사를 드리며, 저는 이만 퇴근(?)하겠습니다 ㅎㅎ
안녕하세요. Visual C# MVP 남정현입니다. Hello Windows Azure와는 별도로, Windows Azure Platform 및 Windows Azure 관련 커뮤니티 소식, Labs 프로젝트, 신 기술을 집중적으로 조명하는 Article을 비정기적으로 "Windows Azure Update" 시리즈를 통하여 소개할 예정입니다. 많은 관심 부탁드립니다.
Windows Azure Platform에서 가장 많은 관심과 아쉬움의 대상이었던 SQL Azure에 대한 사용자들의 갈증을 풀어줄 시원한 도구가 하나 등장했습니다. 바로 Codename: Houston이 그 주인공인데요, 간단히 요약하면 Silverlight로 제공되는, 웹 브라우저에서 쉽게 접속하여 사용할 수 있는 SQL Azure Database 관리 도구입니다. 일단 접속 화면부터 살펴보기로 할까요?
SQL Management Studio Express를 이용하는 것과 유사한 화면을 보실 수 있습니다. 접속할 SQL Azure 서버의 호스트 이름을 찾아서 넣고, 접근할 데이터베이스의 이름을 입력한 후, 관리자 ID와 비밀 번호를 넣고 Connect 버튼을 클릭하면 아래와 같이 접속 중임을 표시하는 화면이 나타납니다.
잠시 더 기다리면 멋진 초기화면이 나타납니다. 이런 스타일의 화면을 웬지 오랫만에 보는것 같군요. :-)
접속한 데이터베이스의 상태를 한눈에 파악할 수 있는 Dashboard가 Start Page에 나타납니다. 화면에서 알 수 있듯이, 탭 페이지 방식으로 여러 화면을 구성할 수 있도록 되어있습니다. 그리고 우리가 평소에 사용하던 Microsoft Access의 버전 2007 이후부터 도입된 Ribbon UI를 사용한다는 것 또한 한눈에 들어옵니다. Dashboard에 또 다른 정보들이 어떤 것이 나타나는지 잠시 살펴볼까요?
사용량 정보가 상세하게 나타납니다. 그리고, 다소 식상하게 비칠지도 모르는 Help Links가 한 면을 차지하고 있군요. 현재 공개된 CTP 버전에서는 이 정도의 기능을 제공하는 것으로 나타나지만 좀 더 많은 사용자들의 의견이 더해짐에 따라서 매우 유용한 기능이 앞으로 이곳에 추가될 것 같습니다. :-)
네. 그렇습니다. 정말 그리웠던 기능이죠. 시각적으로 테이블의 스키마를 편집할 수 있다는 사실은 정말 중요할 수 있습니다. SQL Management Studio Express를 이용해서 접속했어도 사용할 수 없었던 그 기능이 드디어 SQL Azure와 Codename: Houston의 만남으로 가능하게 된 것입니다. 그리고 이미 만들어진 테이블이라 할지라도 매우 편리하게 테이블의 이름을 아래 그림과 같이 수정할 수 있습니다.
그리고 Column을 추가하는 것도 무척 쉽습니다. 하단의 "+ Column" 버튼을 클릭하는 것 만으로도 손쉽게 추가가 됩니다. 아래 그림과 같이 새 Column이 추가되었다는 것도 시각적으로 잘 보여집니다.
그러면 이제 데이터 조회 기능을 살펴보도록 하겠습니다.
당연한 기능이지만 Codename: Houston에서 이러한 화면을 보고 있으니 더욱 놀랍게 비추어지는것 같습니다. :-) 데이터 집합 말고도, 서버로부터 직접 전달받은 메시지도 Messages 탭을 클릭하면 아래와 같이 잘 보여집니다.
아래의 두 Screenshot은 View와 Stored Procedure를 생성하는 편집 화면입니다. 참고로 Stored Procedure는 매개 변수를 시각적으로 선택하고 관리할 수 있으므로 Stored Procedure의 본문만을 집중하여 편집하면 됩니다.
만약 저장되지 않은 상태에서 오른쪽 상단의 Logout 버튼을 클릭할 경우 아래와 같이 경고 메시지도 나타나게 됩니다.
여기까지, 간단했지만 초고속으로 살펴본 Codename: Houston의 모습이었습니다. 사용해보고 싶으시다구요? https://www.sqlazurelabs.com/houston.aspx 페이지를 방문하시면 Codename: Houston을 무료로 사용해보실 수 있습니다. 별도의 가입 절차나 등록 절차가 없으니 자유롭게 이용해보시기 바랍니다. 참고로, 아직은 Out-of-browser Application Type으로는 이용하실 수 없으니 Google Chrome의 Application Link 생성 기능을 이용하여 URL 자체를 창으로 띄울 수 있도록 구성하시면 더욱 편리하게 이용하실 수 있을 것입니다.
- 주최 : 한국 Visual Studio 공식 팀
- 일시 : 2010년 8월 28일토요일오후 1:30~5시
- 장소 : 웹타임교육센터
- 참가비 : 무료
세미나 아젠다
Native 트랙
.NET 트랙
Enterprise 트랙
14:00 ~ 14:50
Visual Studio 2010 : C++0x와 Windows 7
최성기
그것이 알고싶다 - C# 4.0의 변화, 그 진실은 무엇인가. 희망인가? 또 다른 혼란인가?
강보람 C# MVP
VS Team Foundation Server 2010 의 새로운 변화
김병진 Team System MVP
15:00 ~ 15:50
비주얼 스튜디오 2010 의 Concurrency Runtime 을 이용한 멀티 코어 제대로 활용하기
임준환
좋은 프레임워크 있으면 소개시켜줘 - ASP.NET MVC
박세식
소프트웨어품질향상을위한다양한테스트기법
엄준일 Team System MVP
16:00 ~ 16:50
DirectX11 을 기다리며...
조진현
Beginnig WCF
오태겸
SharePoint 2010 Enterprise 솔루션 개발
정홍주 SQL Server MVP
발표 내용 소개
Native 트랙
Visual Studio 2010 : C++0x와 Windows 7 그동안 .NET 영역으로 적잖이 편중되었던 Visual Studio의 버전업에 비해 이번 2010 버전에서는 Native Code 개발환경에서도 많은 변화가 찾아왔다. C++0x 표준 반영에 의한 문법의 변화, 새로운 라이브러리 제공(Concurrency Runtime Library), Windows 7의 최신 기능들을 제어하기 위한 SDK의 업데이트 등이 그것이다. 본 세션을 통해 C++의 문법적인 변화와 Windows 7 기능 구현을 위한 SDK의 업데이트 사항들을 정리해본다.
비주얼 스튜디오 2010 의 Concurrency Runtime 을 이용한 멀티 코어 제대로 활용하기 요즘 가정의 PC 에 멀티 코어 프로세서가 많이 보급되어 있습니다. 하지만 실제로 PC 에 설치된 코어들을 모두 사용하는 애플리케이션들은 많지 않습니다. 이렇게 낭비되는 자원을 C++ 개발자가 쉽게 사용할 수 있도록 도와주는 Concurrency Runtime 을 비주얼 스튜디오 2010에서 제공합니다. 이 Concurrency Runtime 을 어떻게 시작해야할지 알아보겠습니다.
DirectX11 을 기다리며...
조금씩 정보가 공개되면서 많은 변화를 예고하고 있는 DirectX11 에 대해서 살펴 볼 것입니다.특히나 Tessellation, DirectCompute, Multi-threading 을 위한 기본 개념과 작업들에 대해서 체크해 볼 것입니다.
.NET 트랙
그것이 알고싶다 - C# 4.0의 변화, 그 진실은 무엇인가. 희망인가? 또 다른 혼란인가? PDC 2008에 울려퍼진 C# 4.0의 소식. 그 소식을 듣고 많은 사람들은 기대와 혼란을 가지게 되었다. C#은 분명히 정적 언어인데, 동적 언어에나 있을 법한 기능을 추가한다니? 이제와서 뒷북일 수도 있는 C# 4.0의 변화에 대한 진실, 그 마지막 시리즈가 이제 시작된다. :)
좋은 프레임워크 있으면 소개시켜줘 - ASP.NET MVC 그동안 아주 미묘하게 아쉬웠던 ASP.NET. 가려운 곳을 긁어줄 대안의 프레임워크가 나타났다.웹 개발자들 한테 참~ 좋은데, 웹 개발자들 한테 정말 좋은데, 이걸 말로 그냥 할 수 없어서,이번 기회에 소개한다.
Beginnig WCF WCF는 서비스 지향 프로그래밍을 위해 마이크로소프트에서 개발 및 지원하는 기반 기술이며, 기존의 .NET 웹 서비스에 비해 유연성과 확장성이 뛰어나 최근 많은 관심을 받고 있습니다.본 세션에서는 WCF가 무엇인지? 어떤 장점이 있는지? 그리고, WCF 를 이용하기 위해선 무엇이 필요한지? 에 대해 함께 알아보고, 마지막으로, WCF의 활용 예를 알아보도록 하겠습니다.
Enterprise 트랙
VS Team Foundation Server 2010 의 새로운 변화
Visual Studio Team Foundation Server 2010의 혁신적인 변화와 개선 부분, 프로젝트 및 형상관리와 Agile의 Scrum 을 이용한 방법론을 알아보고, 단지 소스 체크인/아웃만 하는 Visual Source Safe에서 업그레이드 하는 방법에 대하여 알아봅니다.
소프트웨어품질향상을위한다양한테스트기법
소프트웨어는 개발 및 릴리즈 과정까지 수 많은 과정을겪는데, 소프트웨어가 점진적으로 진화함에 따라 결함의 발생률이증가합니다. 이를개선하기위한테스트기법중단위테스트, WhiteBox 테스트, 화면테스트, 성능테스트, 부하테스트등다양한테스트기법을알아봅니다.
SharePoint 2010 Enterprise 솔루션 개발
SharePoint 2010은 기업 협업 플랫폼으로 개발자들은 VS 2010을 이용하여 더 생산성 있고 효과적인 SharePoint 2010 개발을 진행할 수 있습니다.본 세션에서는 SharePoint 2010 개발에 대한 가장 필요한 내용을 구체적으로 알아보며 이를 통해 가장 많은 요구사항에 대한 실무 솔루션을 구성하는 방법에 대한 내용을 알아보겠습니다.
발표자 소개
Native 트랙
최성기 / Visual Studio 공식 팀 엔씨소프트에서 온라인 게임 서버를 개발하고 있으며, 비주얼 스튜디오 2010 공식 팀 블로그 (http://vsts2010.net) 에서 MFC와 윈도우7 카테고리를 맡아 스터디를 하고 있다. 최근 UX 시장의 핫이슈인 ‘멀티터치’에 대해 많은 관심을 갖고 있다.
임준환 / Visual Studio 공식 팀
Visual Studio 2010 공식 팀 블로그( http://vsts2010.net ) 에서 C++, 게임 관련 필자로 활동하고 있다.
조진현 / Visual Studio 공식 팀
현재클라이언트게임프로그래머로써재직중입니다. Visual Studio 2010 공식팀블로그(http://vsts2010.net) 에서 DirectX11 부분에서활동중입니다.
.NET 트랙
강보람 / Visual Studio 공식 팀 시삽 / Microsoft C# MVP
Visaul Studio 공식팀의 닷넷 파트 시삽을 맡고 있으며, Visual C# MVP이다. MSDN 주간 세미나, Techdays 2009, 2010 Spring, REMIX 10에 참여했으며, '그것이 알고싶다'를 2004년 부터 거의 빼놓지 않고 다 본 경력의 소유자이다. 개인블로그 '워너비의 소프트웨어 팩토리'(http://blog.naver.com/netscout82)를 운영중이며, 프로그래밍과 전혀 상관없는 이야기를 쓰고 있다.
박세식 / Visual Studio 공식 팀
아직까지는 꿈많은 유부남 청년이다. 아이가 생기면 시간이 없다는 말에 몸서리 치면서 노력중이다. Visual Studio 공식 팀 블로그에서 ASP.NET MVC 관련 포스팅을 하고 있고, 개인블로그 sses's blog(http://sses.tistory.com)를 운영중이다.
오태겸 / Visual Studio 공식 팀
오태겸, 현재 Hostway 에서 근무하고 있으며, 개인 블로그(http://ruaa.tistory.com)와 Visual Studio 2010 공식 팀 블로그(http://vsts2010.net)에서 WCF 카테고리를 통해 있는 지식, 없는 지식 총 동원해가며, WCF에 관한 포스팅을 하고 있다.
Enterprise 트랙
김병진 / Visual Studio 공식 팀 시삽 / Microsoft Team System MVP / MCT
김병진 MCT/Microsoft MVP로 Visual Studio 2010 팀 블로그(http://vsts2010.net)에서 활동하고 있으며, ALM 교육과 컨설팅을 통해 Microsoft 의 기술과 플랫폼기반의 개발과 설계 관련하여 강의과 컨설팅을하고 있으며, 우리나라 소프트웨어 공학의 발전을 위해 열심히 노력하고 있습니다.
엄준일 / Visual Studio 공식 팀 대표 시삽 / Microsoft Team System MVP
엄준일 Microsoft Team System MVP 로 활동 하고 있으며, 개인 블로그(http://blog.powerumc.kr) 와 트위터(@powerumc) 를 통해 .NET 기술을 전파하고 있다. 그리고 Visual Studio 2010 공식 팀 블로그(http://vsts2010.net) 의 대표 시삽으로 팀 블로그와 트위터(@vsts2010) 를 운영하고 있다.
정홍주 / Visual Studio 공식 팀 / Microsoft SQL Server MVP
웹타임 교육센터에서 SQL, .NET 강의와 .NET, SharePoint 컨설팅을 하고 있다.Microsoft SQL Server MVP 로 활동 하고 있으며 데브피아의 SQL Server 2005 시샵이다.SharePoint 2010 책을 집필하고 SharePoint 2010 관련 동영상과 미니클립을 서비스하고 있으며 현재 Visual Studio 2010 공식 팀 블로그(http://vsts2010.net) 에서 SharePoint 2010 관련 블로깅을 하고 있다. 향후 SharePoint 2010 개발 관련 여러 내용을 Open Source 할 예정이다.
- 서비스 로케이션과 DI(Dependency Injection) 지원
- .NET Framework 4 유효성검사 속성과 IValidatableObject를 지원
- New IClientValidatable Interface - 새로운 액션 타입이 추가되었죠: HttpNotFoundResult(404 에러), HttpStatusCodeResult
계속 살펴보겠습니다.^^ 관련 글 팍팍 진행하도록 하겠습니다. 물론 mvc 2 얘기가 끝난 것이 아니라 병행하면서요 ㅡ,.ㅡ;;
지난 글 unbounded_buffer 에 이어 또 다른 message block 인 overwrite_buffer 와 single_assignment 에 대해서 알아보도록 하겠습니다.
Message block 들은 특징들이 모두 다르기 때문에 지난 unbounded_buffer 을 생각하시면 이해가 어려울 수 있습니다. message block 하나 하나의 쓰임새가 다르므로 새로운 것을 알아본다고 생각하시는 것이 좋을 것 같습니다.
overwrite_buffer< _Type >
지금부터 설명드릴 overwrite_buffer 은 unbounded_buffer 와는 달리 하나의 변수라고 생각하시면 이해하기 쉬울 것입니다.
Concurrency runtime 을 사용하지 않고, 스레드 간의 상태나 정보를 공유하려면 전역 변수나 힙( heap ) 에 할당된 변수에 락( lock ) 을 걸어 사용해야 합니다.
overwrite_buffer 는 방금 언급한 번거로운 작업들을 알아서 해줍니다. 내부에서 힙에 메모리를 할당하고, 접근 시 락을 겁니다. 하지만 사용하는 우리는 그런 것들을 신경 쓰지 않고 마치 지역 변수처럼 사용할 수 있습니다.
unbounded_buffer 는 외부에서 message 를 받아가면 내부에서 해당 message 가 제거되는 반면에, overwrite_buffer 는 제거되지 않습니다. 또한 하나의 변수와도 같기 때문에 외부에서 message 를 보내면 이 전의 message 를 덮어쓰고 새 message 가 저장됩니다.
결국 overwrite_buffer 는 단 하나의 message 만을 갖게 됩니다.
그럼 overwrite_buffer 의 멤버 함수에 대해서 알아보도록 하겠습니다.
멤버 함수
생성자와 소멸자를 제외한 public 인 멤버 함수들입니다.
bool has_value() const
현재 message 를 가지고 있는지 반환합니다.
어떠한 message 도 갖지 않을 경우에 false 를 반환합니다. 만약, 한번이라도 overwrite_buffer 에 message 가 전달된다면 그 후부터는 true 를 반환합니다. overwrite_buffer 는 외부에서 message 를 받아가도 내부의 message 가 제거되지 않기 때문입니다.
Message 를 갖고 있지 않을 때, 외부에서 동기 함수인 receive() 를 사용해 message 를 얻기를 원한다면 overwrite_buffer 에 message 가 들어올 때까지 기다립니다. message 가 제거되지 않기 때문에 한번이라도 overwrite_buffer 가 message 를 받으면 receive() 가 기다리는 일은 없을 것입니다.
_Type value();
현재 가지고 있는지 message 를 반환합니다.
내부적으로 동기 전달 함수인 receive() 를 사용하므로 message 를 가지고 있지 않다면 message 를 갖게 될 때까지 기다립니다. 만약 이 때, has_value() 를 호출했다면 false 를 반환할 것입니다.
Message 가 제거되지 않기 때문에 전달 함수를 이용해 message 를 받아갈 경우, 복사본이 전달됩니다.
예제
overwrite_buffer 의 간단한 예제를 구현해보도록 하겠습니다.
시나리오
네트워크 지연 시간을 갱신하고, 출력하는 프로그램을 작성할 것입니다.
네트워크 지연 시간을 갱신하는 역할을 하는 agent 와 갱신된 정보를 출력하는 agent 가 하나의 overwrite_buffer 를 공유하여 사용하는 예제입니다.
코드
#include
#include
#include
using namespace std;
using namespace Concurrency;
// 지연 시간을 얻어오는 agent.
class PingUpdater
: public agent
{
public:
PingUpdater( const array< unsigned int, 5 >& delayTimeSource, ITarget< unsigned int >& targetBlock )
: delayTimeSource( delayTimeSource )
, targetBlock( targetBlock ) { }
protected:
// 2초마다 지연 시간을 얻어 옴.
void run()
{
while( true )
{
asend( this->targetBlock, this->GetDelayTime() );
Concurrency::wait( 2000 );
}
this->done();
}
// 지연 시간을 시뮬레이션하는 함수.
unsigned int GetDelayTime()
{
static unsigned int index = 0;
unsigned int delayTime = this->delayTimeSource[ index ];
if( index + 1 < this->delayTimeSource.size() )
++index;
else
index = 0;
return delayTime;
}
private:
const array< unsigned int, 5 >& delayTimeSource;
ITarget< unsigned int >& targetBlock;
};
// 지연 시간을 출력하는 agent.
class PingDisplayer
: public agent
{
public:
PingDisplayer( ISource< unsigned int >& sourceBlock )
: sourceBlock( sourceBlock ) { }
protected:
// 1초마다 지연 시간을 출력한다.
void run()
{
while( true )
{
this->Display( receive( this->sourceBlock ) );
Concurrency::wait( 1000 );
}
this->done();
}
// 지연 시간을 출력하는 함수.
void Display( unsigned int delayTime )
{
wcout << L"current delay time: " << delayTime << endl;
}
private:
ISource< unsigned int >& sourceBlock;
};
int main()
{
// 네트워크 지연 시간의 시뮬레이션 정보.
array< unsigned int, 5 > delayTimeSource = { 210, 211, 261, 246, 223 };
// 공유 버퍼
overwrite_buffer< unsigned int > delayTimeBuffer;
// 네트워크 지연 시간을 갱신하는 agent 와 출력하는 agent.
PingUpdater updater( delayTimeSource, delayTimeBuffer );
PingDisplayer displayer( delayTimeBuffer );
// agent 시작.
updater.start();
displayer.start();
// agent 의 작업이 모두 끝날 때까지 대기.
agent* waitingAgents[2] = { &updater, &displayer };
agent::wait_for_all( 2, waitingAgents );
}
[ 코드1. overwrite_buffer 를 이용한 네트워크 지연 시간 갱신 및 출력 예제 ]
PingUpdater 클래스는 agent 클래스로 네트워크 지연 시간을 갱신하는 역할을 합니다. 2초에 한 번씩 시뮬레이션을 위해 준비된 정보를 순회하며 얻어와서 overwrite_buffer 에 전달합니다.
PingDisplayer 클래스도 agent 클래스로 갱신된 네트워크 지연 시간을 화면에 출력하는 역할을 합니다. 1초에 한번씩 overwrite_buffer 로부터 갱신된 정보를 가져와서 화면에 출력합니다.
예제에서 사용된 Concurrency::wait() 는 Win32 API 의 Sleep() 과 같은 역할을 합니다. agent::wait() 과 혼동하지 않길 바랍니다.
위 코드를 보시면 굉장히 직관적이고, 간단하게 멀티 스레드 프로그래밍을 할 수 있다는 것을 알 수 있을 것입니다.
[ 그림1. overwrite_buffer 를 이용한 네트워크 지연 시간 갱신 및 출력 예제 ]
single_assignment< _Type >
single_assignment 는 위에서 설명한 overwrite_buffer 와 거의 흡사합니다.
단지 다른 점이 있다면 message 를 한번만 받을 수 있다는 것입니다. 만약 두 번 이상 보낸 다면 두 번째부터는 무시됩니다.
멤버 함수 또한 거의 같지만, 다른 점을 알아보겠습니다.
멤버 함수
생성자와 소멸자를 제외한 public 인 함수들입니다.
bool has_value() const
위에서 설명한 overwrite_buffer 의 has_value() 와 같습니다.
Message 를 단 한번도 받지 않았다면 false 를 반환하고, 받았다면 true 를 반환합니다.
_Type const & value()
overwrite_buffer 의 value() 와 같은 기능을 합니다.
하지만 값을 반환하지 않고 const 참조를 반환한다는 것이 다릅니다.
overwrite_buffer 의 value() 와 마찬가지로 message 를 갖고 있지 않다면 message 를 갖게 될 때까지 기다립니다.
마치는 글
이번 글에서는 overwrite_buffer 와 single_assignment 에 대해서 알아보았습니다.
single_assignment 는 overwrite_buffer 와 거의 흡사하기 때문에 예제는 생략하였습니다.
지난 글에서 본 unbounded_buffer 와는 분명히 쓰임새가 다르므로 특징을 잘 파악해두시면 좋을 것입니다.
다음 글에서 또 다른 message block 을 소개해드릴 것입니다. 그 message block 또한 쓰임새가 분명히 다르므로 Asynchronous Agents Library 의 활용도가 굉장히 넓다는 것을 아시게 될 것입니다.
주요 변경 사항으로 소개되 내용에서, unique_ptr에 대해서 다음과 같이 설명하고 있습니다.
"auto_ptr 클래스보다 더 안전한 스마트 포인터형인 unique_ptr 클래스의 구현에도 Rvalue reference가 사용되었습니다. unique_ptr 클래스는 move는 할 수 있지만 copy는 불가능하며, safety에 영향을 미치지 않으면서 강한 소유 의미(strict ownership semantics)를 구현했습니다. 또한, unique_ptr 클래스는 rvalue references가 구현된 container들과 잘 동작합니다."
unique_ptr in MSDN Library
MSDN Library에 소개된unique_ptr class에 관한 설명입니다.
- 소유하는 객체에 대한 포인터를 저장한다. 해당 객체는 다른 unique_ptr에 의해서 소유될 수 없다. 해당 객체는 unique_ptr이 해제될 때에 해제된다. 다음은 MSDN에 게시되어 있는 class 정의 코드입니다.
왜 auto_ptr은 deprecation이 되었을까? C++ 0x에서 auto_ptr은 deprecation으로 결정되었습니다. C++ 0x에서는 최소한 auto_ptr과 같은 효율을 가지며 move semantics를 지워하는 unique_ptr을 추가하게 됩니다. 그렇다면 auto_ptr은 왜 deprecation이 되었을까요? 그 이유에 대해서 설명드리겠습니다.
역사적으로 이전 버전의 C++ 표준에서, 반복된 수정과 패치를 통해서 주요 확장에서 auto_ptr은 안정성을 확보했습니다. 하지만 설계상의 심각한 결함을 극복하지는 못했습니다. 대표적으로 generic 알고리즘과 auto_ptr을 함께 사용할 때에 문제가 되었습니다. generic 알고리즘에서는 복사 연산 문법은 정말로 copy 연산이 일어난다는 것을 가정합니다. 하지만 auto_ptr에서 복사 연산자는 실제로 move와 같이 동작합니다. 이런 근본적인 차이 때문에 알고리즘의 구현에 따라서 원하는 결과를 얻지 못 할 수도 있었습니다.
정렬 알고리즘을 통해서 예를 들겠습니다. 버블 정렬이나 선택 정렬에서는 하나의 지역변수에 한 원소를 골라서 복사를 하는 방식을 사용합니다. 이런 경우에는 완벽하게 유효한 구현이 됩니다. 하지만 빠른 정렬의 경우를 생각해보면, 구현 중에 다음과 같은 코드가 있을 겁니다.
value_type pivot = *mid_point;
이 경우에 알고리즘상 복사 연산자에서 실제로 복사가 일어남을 전제하는 알고리즘인 겁니다. 하지만 auto_ptr은 복사 표현을 통해서 move를 구현했기 때문에 이런 구현에서는 sort()의 결과가 안전할 것인지 보장 할 수가 없었습니다.
수년간의 논의 끝에, C++ 표준 위원회는 auto_ptr은 deprecated로 결정했습니다. 하지만 auto_ptr의 다른 모든 장점을 수용할 수 있으며 더 안전하고 명확한 인터페이스를 C++ 0x에 추가 될 필요가 있었고, 그 대채자가 unique_ptr 입니다. unique_ptr은 복사 생성자를 private으로 선언합니다. 따라서 generic 알고리즘을 unique_ptr과 같이 사용하여 호출할 때에, 모든 generic 코드는 컴파일 타임에 에러가 나거나 그렇지 않다면 완벽하게 유효한 결과를 내게 됩니다.
다음 글에서는 unique_ptr의 예제 코드 위주로 auto_ptr과의 차이점과 개선점을 살펴보도록 하겠습니다.
1. Visual Web Developer 2010 Express (또는 Visual Studio 2010)를 권한 상승 시킨 상태에서 시작하도록 합니다. 아래의 그림을 참고하세요. 사용자 계정 컨트롤을 사용하고 있을 경우 별도의 경고 대화 상자가 나타날 수 있으며 실행하도록 선택하시면 됩니다.
2. Visual Web Developer 2010 Express가 실행되면 초기 화면에서 새 프로젝트 만들기 링크를 클릭합니다.
3. 아래에 표시된 대화 상자에서 왼쪽의 개발 범주를 Cloud로 선택하면 사용 가능한 프로젝트 템플릿 중에서 "Windows Azure 클라우드 서비스"가 나타납니다. 이 항목을 클릭하고, 프로젝트 이름을 원하는 이름 (여기서는 TwistBook이라고 하겠습니다.)을 지정한 후, "솔루션용 디렉터리 만들기"에 체크하고 "확인" 버튼을 클릭합니다.
특별히 솔루션용 디렉터리 만들기에 체크가 되어있는지를 확인하는 이유는, 이 옵션이 Windows Azure Tools로 생성되는 프로젝트의 특성 상 한 솔루션 안에 다수의 프로젝트가 만들어지기 때문에 이를 정확하게 분류하기 위하여 사용하는 옵션으로, 사용하도록 맞추어져있을 때 좀 더 소스 코드 관리가 편리하기 때문입니다.
4. 배포할 Windows Azure 응용프로그램 내에 배치될 Role의 종류와 유형을 설정하는 추가 프로젝트 마법사가 아래와 같이 나타납니다. (만약 아래 화면 대신 HTML 페이지로 안내 페이지가 나타나는 경우 Azure Tools가 올바르게 설치되지 않은 상태입니다.)
5. .NET Framework 4 역할 그룹과 클라우드 서비스 솔루션 그룹 사이의 두 개의 버튼을 이용하여 실제로 제작할 프로젝트의 유형을 설정하고 프로젝트의 이름까지 정할 수 있습니다. 우선 ASP.NET MVC 2 웹 역할 프로젝트 한 개와 작업자 역할 프로젝트 한 개를 추가하겠습니다.
6. 이제 각 프로젝트의 정확한 이름을 설정하기 위하여, 클라우드 서비스 솔루션 그룹 안에 추가된 프로젝트 중 MvcWebRole1 프로젝트 항목을 선택하면 연필 모양의 아이콘이 이름 옆에 나타납니다. 이를 클릭하면 아래와 같이 이름을 바꿀 수 있도록 편집 영역이 나타납니다. 이 예제에서는 다음과 같이 이름을 정하였습니다.
이름을 편집하고 나면 아래와 같은 화면이 되어있을 것입니다. 확인 버튼을 눌러 프로젝트를 생성합니다.
7. 프로젝트의 생성을 진행하다보면 Visual Studio 2010 Professional 이상의 버전에서는 다른 ASP.NET MVC 2 프로젝트와 마찬가지로 테스트 프로젝트를 만들것인지를 물어보는 대화 상자가 나타납니다. 빠른 설명과 간결한 진행을 위하여 테스트 프로젝트를 생성하지 않는 방향으로 이 예제에서는 진행하도록 하겠습니다. (필요하신 분들께서는 생성하셔도 됩니다.) Visual Web Developer 2010 Express Edition에서는 이러한 대화 상자가 따로 나타나지 않습니다.
8. 프로젝트 생성이 끝나면 아래와 같이 솔루션 탐색기에 총 3개의 프로젝트가 열거됩니다.
TwistBook 프로젝트는 Cloud Application 전체를 총괄하는 프로젝트이며, Cloud 환경에서 하나의 Application으로 분류됩니다. 이 프로젝트 안에 Web Role과 Worker Role이 다수 연결되는 구조로 되어있으며, 나중에 Cloud Service Package 파일 (CSPKG)로 컴파일될 때 이 프로젝트가 기준이 됩니다.
TwistBook.WebRole 프로젝트는 ASP.NET MVC 2를 사용하도록 프로젝트가 구성되어있으며 여기에 기본적인 트위터 스타일의 방명록 UI를 표시하거나 인증된 사용자로부터 메시지를 입력받아 Worker Role에게 처리를 위임하는 등의 작업을 수행하도록 코드를 구성할 것입니다.
그리고 TwistBook.LinkProcessor 프로젝트는 Web Role과는 따로 실행되는 개별적인 Role 인스턴스로서, Web Role에서 받아들이는 메시지 중 이미지 파일을 twitpic.com에 게시하여 짧은 URL을 받아온다거나, 본문에 있는 긴 URL을 짧게 만들어 받아오는것과 같이 처리량이 많이 몰렸을 경우 병목 현상을 일으킬 수 있는 기능만을 전담하도록 코드를 구성할 것입니다.
자료 구조 만들기
1. 클라우드 환경 내부 및 외부에서 기준이 될 모델 자료 구조를 만들기 위하여, 별도의 클래스 라이브러리를 작성하도록 하겠습니다. 솔루션 탐색기에서 솔루션 항목을 오른쪽 버튼으로 클릭하고 아래 그림처럼 새 프로젝트 추가 메뉴를 클릭합니다.
2. 일반적인 클래스 라이브러리 프로젝트를 하나 만듭니다. 이름은 TwistBook.DataModel로 지정하고, Cloud 환경 위에서 사용하도록 현재 지정된 .NET Framework 4와 동일한 빌드 타겟이 지정되어있는지 확인한 후 프로젝트를 생성합니다.
3. TwistBook.DataModel 프로젝트에 Windows Azure Table Storage에서 사용할 Data Context 클래스를 만들도록 하겠습니다. [각주:1]
Data Context 클래스를 만들기 위해서는 Windows Azure SDK에서 제공하는 클래스 라이브러리 파일들을 TwistBook.DataModel 프로젝트 참조에 포함시켜야 합니다. 아래 그림과 같이 솔루션 탐색기에서 TwistBook.DataModel 프로젝트 아래의 참조 항목을 오른쪽 버튼으로 클릭하고 "참조 추가" 메뉴를 클릭합니다.
4. 참조 추가 대화 상자가 나타나면, .NET 탭을 클릭합니다. 이 과정에서 비동기적으로 Visual Studio가 관리하는 디렉터리 목록 내에 있는 모든 어셈블리들을 조사하여 실시간으로 리스트 박스에 추가합니다. 이 샘플에서 필요로 하는 SDK의 라이브러리가 목록에 나타나기까지 조금 시간이 걸릴 수 있으며 시스템마다 차이가 있을 수 있지만 약 1분 이내에 나타납니다.
5. 나타난 항목들 중에서 다음의 항목들을 찾아 키보드의 Ctrl 키를 누른채로 하나씩 클릭하면, 아래 그림과 같이 여러 대상을 선택하고 참조로 추가할 수 있습니다.
6. 아래 그림과 같이 참조 목록이 구성되어있으면 준비가 다 된것입니다. 이제 본격적으로 코드 작성을 시작해 보도록 하겠습니다. :-)
7. 기본으로 만들어진 클래스가 담겨있는 Class1.cs 파일의 내용을 아래와 같이 작성합니다. 코드에서 핵심이 되는 부분을 굵게 표시하였습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using Microsoft.WindowsAzure.StorageClient; [각주:2]
namespace TwistBook.DataModel
{
public class TwistModel : TableServiceEntity [각주:3]
{
public TwistModel()
{ DateTime current = DateTime.Now;
PartitionKey = current.ToString("yyyyMMdd");
RowKey = current.ToString("hhmmss"); [각주:4]
}
public string WriterName { get; set; }
public string MessageBody { get; set; }
public DateTime WrittenDate { get; set; }
public string ImageUrl { get; set; } [각주:5]
}
}
8. Class1.cs 파일의 이름을 클래스 이름과 동일하게 설정합니다. Windows 탐색기를 열지 않고, 아래의 그림에서처럼 솔루션 탐색기에서 직접 이름을 바꿀 수 있으며, Class1.cs 파일을 TwistModel.cs 파일로 이름을 변경합니다.
9. TwistBook.DataModel 프로젝트에 Windows Azure Table Storage에서 사용할 Data Context 클래스를 만들도록 하겠습니다. 새 항목을 프로젝트에 추가하기 위하여 아래 그림과 같이 솔루션 탐색기에서 TwistBook.DataModel 프로젝트 항목을 오른쪽 버튼으로 클릭하면 "추가" - "새 항목 추가" 메뉴가 나타나는데 이를 클릭하시면 됩니다.
10. 새 항목 추가 대화 상자에서 설치된 템플릿 영역에서 "Visual C# 템플릿"을 선택하고, 우측 목록에서 "클래스"를 선택합니다. 그리고 이름에 새로 추가할 클래스의 이름을 지정한 후 "추가" 버튼을 클릭합니다. 9단계와 10단계를 거쳐서 다음의 파일들을 추가로 생성합니다.
* TwistDataServiceContext.cs
* TwistDataSource.cs
11. 솔루션 탐색기 내의 TwistBook.DataModel 프로젝트 항목 아래에 다음 그림과 같이 구성이 되어있으면 정상적으로 추가가 된 것입니다.
12. 이제 TwistDataServiceContext.cs 파일을 열어서 다음과 같이 코드를 작성합니다. 코드에서 중요한 부분은 굵은 글씨로 표현하였고 여기에 따른 부가적인 설명을 각주로 붙였습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient; [각주:6]
public IQueryable<TwistModel> TwistModel [각주:9]
{
get { return this.CreateQuery<TwistModel>(TwistModelName); } [각주:10]
}
}
}
13. 이어서 TwistDataSource.cs 파일을 열어서 다음과 같이 코드를 작성합니다. 코드에서 중요한 부분은 굵은 글씨로 표현하였고 이에 따른 부가적인 설명을 각주로 붙였습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using Microsoft.WindowsAzure;
using System.Data.Services.Client;
using Microsoft.WindowsAzure.StorageClient; [각주:11]
namespace TwistBook.DataModel
{
public class TwistDataSource
{
private static CloudStorageAccount storageAccount;
private TwistDataServiceContext serviceContext;
static TwistDataSource()
{
// 중요: 실제로 응용프로그램을 Cloud 환경에 배포할 때에는
// Cloud Project 내의 다른 환경 설정 문자열을 이용하도록
// 호출을 변경해야 합니다. storageAccount = CloudStorageAccount.DevelopmentStorageAccount; [각주:12]
다음 Article의 내용을 올리기 전에, ASP.NET MVC 2에 대한 이해를 돕기 위하여 간단한 단락 하나를 구성하였습니다. ASP.NET MVC 2는 Microsoft의 최신 웹 기술이 적용된 프레임워크로 Windows Azure 개발 환경에서 뿐만 아니라 일반적인 웹 사이트 개발에도 얼마든지 활용될 수 있는 유용한 프레임워크입니다.
TwistBook.WebRole 프로젝트의 노드를 솔루션 탐색기에서 살펴보면 아래와 같은 구성이 나타납니다. 이 구성은 전형적인 ASP.NET MVC 응용프로그램이며, 고전적인 웹 프로그래밍 모델에서와는 달리 직접 aspx 페이지를 부르지 않고 알기 쉬운 주소를 기반으로하는 것이 특징입니다. ASP.NET MVC 응용프로그램을 처음 접하시는 분들을 위하여 디렉터리 구조에 대한 설명을 잠시 말씀드립니다.
App_Data: ASP.NET 응용프로그램이 데이터베이스에 연결하기 위하여 필요한 각종 코드 및 데이터베이스 연결 설정 파일들을 보관하는 디렉터리이며, ASP.NET 2.0부터 존재해왔던 디렉터리입니다. 예외적으로 이 디렉터리에는 Microsoft Access 파일 (*.mdb 또는 *.accdb)이나 소규모 웹 사이트를 위한 Embedding 가능한 SQL 데이터베이스 파일 (*.mdf 및 *.ldf)이 배치되기도 합니다.
Content: ASP.NET MVC 응용프로그램 전반에 걸쳐서 사용되는 공통적인 클라이언트측 구성 요소 (가령 CSS 스타일 시트, XSLT 스타일 시트, 이미지 파일, 오디오 파일 등)가 이 디렉터리에 저장됩니다. 이 디렉터리에 저장된 파일들은 중간 처리기에 의하여 해석되지 않는 고유한 경로를 유지할 수 있습니다.
[중요] Controllers: ASP.NET MVC 응용프로그램에서 "C"를 대표하는 구성 요소가 저장되는 디렉터리이며 백그라운드에서 웹 페이지를 그리거나, 웹 브라우저로부터 받아온 정보를 해석하거나, 가공하거나, 처리하는 제어 코드를 이곳에 배치합니다.
[중요] Models: ASP.NET MVC 응용프로그램에서 "M"을 대표하는 구성 요소가 저장되는 디렉터리이며 주로 Controller 간의 통신, 데이터베이스와의 통신, 클라이언트로의 통신 등에서 기본 단위가 되는 데이터나 모델을 표현하는 클래스 코드를 이곳에 배치합니다. 이곳에 배치되는 코드에는 로직이 포함되지 않는 것을 원칙으로하며, 이곳에 배치되는 클래스들의 성격을 일반적으로는 POCO [각주:22] - 또는 - PONO [각주:23] (http://en.wikipedia.org/wiki/Plain_Old_CLR_Object)로 이해하면 쉽습니다.
Scripts: Content 디렉터리와 유사한 성격의 디렉터리이지만 특별히 JavaScript 라이브러리들을 위하여 할당된 디렉터리로, ASP.NET MVC 2는 오픈 소스 기반의 JavaScript Framework인 jQuery를 기본으로 제공합니다. 만약 jQuery Plugin을 개발하였거나 사용하고자 하는 다른 Plugin이 있을 경우 - 또는 - jQuery 이외의 다른 JavaScript 라이브러리 (예: 네이버 jindo, script.aculo.us, Moo Tools, Google Web Toolkit, extJS, Dojo Toolkit, prototype, Yahoo! UI 등)를 이곳에 추가하면 됩니다.
[중요] Views: ASP.NET MVC 응용프로그램에서 "V"를 대표하는 구성 요소가 저장되는 디렉터리이며 주로 Controller에 어떤 데이터를 전달할 것인지를 사용자에게 대화형으로 묻거나, Controller에 의하여 발생한 출력 결과를 사용자에게 대화형으로 전달할 때 사용하는 컨텐츠 파일들이 여기에 저장됩니다. ASP.NET MVC 2에서는 Web Forms와 Script Tag Expression을 기반으로 하는 뷰 엔진을 기초로 합니다. [각주:24]
[중요] Global.asax: ASP.NET MVC에서 매우 중요한 구성 요소로 지금 작업하는 ASP.NET 응용프로그램이 ASP.NET MVC 엔진에 의하여 처리되어야 함을 지정하고 초기 설정을 구성하는 코드가 여기에 포함되어있습니다. 이 파일이 누락되거나 내용이 잘못되어있을 경우 ASP.NET MVC 응용프로그램으로서 동작하지 않음을 유의해야 합니다. 추가적으로 사이트 내에 다른 영역을 구성하거나, 다른 주소 패턴을 확장해야 할 경우에도 이 파일에 내용을 추가해야 설정이 적용됩니다.
[중요] Web.config: ASP.NET 응용프로그램의 환경 설정 파일로 역시 이 파일의 내용에 문제가 있거나 누락되어있을 경우 ASP.NET MVC 응용프로그램이 올바르게 동작하지 않을 수 있음을 주의해야 합니다.
[중요] WebRole.cs: ASP.NET 응용프로그램과는 무관하나, Windows Azure 환경에서 Web Role이 초기에 기동될 때 필요한 설정을 포함하고 있으며, 클라우드 컴퓨팅 환경에서의 실질적인 진입점이 됩니다. 이 클래스가 없을 경우 응용프로그램 실행에 문제가 있을 수 있습니다.
다음 시간에는
다음 시간에는 ASP.NET MVC 2 기반의 Web Role을 작성하고, 테이블 스토리지에서 실제로 데이터를 조회하거나 추가, 변경, 삭제하는 예시를 들어보도록 하겠습니다. 긴 강좌 읽어주셔서 감사하며, 즐거운 여름 휴가 되십시오. 감사합니다. :-)
강좌에 대한 고칠 부분, 의견, 제안 등은 남정현의 클라우드 & 닷넷 블로그 (http://www.rkttu.com/), 트위터 (@rkttu), 전자 메일 (rkttu nospam rkttu dot com)을 통하여 항상 받고 있습니다. 언제든 의견 주시면 감사하겠습니다. :-)
이 샘플에서 Windows Azure Storage를 이용하는 방향으로 설명이 되어있지만, 실제로 여러분이 개발할 Windows Azure 서비스에서는 SQL Azure나 다른 곳에 배치되어있을 고가용성의 관계형 데이터베이스 시스템 (예: SQL Server 2008 R2)을 이용하는 것이 더 좋을 수 있습니다. [본문으로]
Windows Azure SDK와 함께 제공되는 Table Storage를 위한 API가 포함되어있는 네임스페이스입니다. [본문으로]
TableServiceEntity 클래스를 상속받도록 자료 구조를 만들어야 SDK를 이용하여 Table Storage에 데이터를 저장하거나 가져올 수 있습니다. [본문으로]
TableServiceEntity 클래스의 기능을 적용하기 위하여 동일한 시그니처를 가진 생성자를 하나 만듭니다.
Table Storage의 접근 효율성을 위하여, 테이블은 여러 개의 파티션으로 구분됩니다. 이 샘플에서 파티션의 분리 단위로 "날짜"를 사용하였습니다.
그리고 파티션 내에서 각각의 Entity가 고유한 의미를 가질 수 있게 하기 위하여 Row Key를 사용하여 구분합니다.
Partiton Key와 Row Key가 더해져서 테이블 내에서는 이 Entity가 "유일할 수 있다"는 특성을 보장합니다. [본문으로]
WriterName Property는 작성자의 이름, MessageBody Property는 메시지 본문, WrittenDate Property는 작성한 날짜와 시간을, ImageUrl Property는 같이 첨부하는 사진의 URL을 보관하는 목적으로 사용됩니다.
C# 3.0 이후로 지원되는 단축 Property Getter/Setter 선언으로 별도의 private 멤버 변수를 배치하지 않고 이와 같이 단순한 코드를 만들 수 있습니다. [본문으로]
Windows Azure SDK와 함께 제공되는 Table Storage를 위한 API가 포함되어있는 네임스페이스입니다. [본문으로]
LINQ를 이용하여 손쉽게 데이터를 가져오거나 설정할 수 있도록 LINQ의 설정을 확장해주는 기본 추상 클래스입니다. [본문으로]
CSCFG 파일 상의 정보를 표현하는 객체인 CloudStorageAccount를 생성자에서 인자로 받아 이 객체를 초기화하고, Windows Azure Storage와의 연결을 초기화합니다. [본문으로]
지연 실행을 목적으로 하는 질의 객체를 생성합니다. 지연 실행이란, 각각의 요소를 다룰 필요가 있을 때 식을 계산하고 평가하는 방식으로, 전체의 내용을 미리 메모리에 읽어들여서 처리하는 것과는 차이가 있습니다.
이 객체는 앞서 우리가 정의한 TwistModel 클래스를 트랜잭션의 단위로 사용하도록 SDK 내의 프레임워크에서 생성됩니다. [본문으로]
주의: 엔티티 클래스의 이름, ServiceContext에서 노출하는 프로퍼티의 이름, CreateQuery 메서드에 전달하는 테이블 명의 이름이 모두 같아야 혼선없이 올바르게 동작할 수 있음을 보증할 수 있습니다. [본문으로]
LINQ to Azure Table Storage를 활용하는데에 필요한 클래스 및 원격 데이터 액세스에 필요한 클래스들이 굵게 강조 표시한 3개의 네임스페이스 안에 모두 포함되어있습니다. [본문으로]
현재는 실제 Windows Azure Storage 계정을 지정하지 않고 Local Development Storage 계정을 대신 지정합니다. [본문으로]
테이블 모델을 생성할 때 사용할 기준이 될 DataContext 클래스를 선택하고, 생성을 요청합니다. 이 때 접속할 대상 스토리지의 HTTP 주소와 자격 증명 정보도 한꺼번에 지정합니다.
또한, 이 작업은 TwistDataSource 클래스를 프레임워크에서 로드할 때 한 번만 발생할 수 있도록 유도하기 위하여 정적 생성자에 정의하였습니다. 테이블의 초기 구조를 할당하는 작업은 자주 일어날 필요가 없는 작업이기 때문에 성능 상의 이득을 위하여 이와 같이 작성합니다. [본문으로]
서비스 객체를 초기화합니다. 앞에서 가져온 계정 정보를 사용하여 복원된 계정 정보 객체를 사용하여 초기화하고 있다는 점을 확인하십시오. [본문으로]
혹시 있을지 모르는 장애에 대해 좀 더 완벽한 대비를 위하여, 재시도 정책을 설정할 수 있습니다. (이 부분은 매우 중요한 개념입니다.) 첫 번째 인자에는 재시도 횟수, 그리고 두 번째 인자에는 재시도 간격을 TimeSpan 객체를 이용하여 지정할 수 있습니다. 여기서는 처음 실패가 발생한 시점을 기준으로 3회 더 시도하며 각 시도 간격은 1초로 정합니다. [본문으로]
전형적인 LINQ to Entity 서비스와 마찬가지로 AddObject를 이용하여 객체의 참조를 기반으로 새로운 데이터를 추가할 수 있습니다. [본문으로]
트랜잭션 개념을 기본적으로 사용하므로 SaveChanges 메서드는 삽입, 변경, 삭제 작업이 있은 직후에는 반드시 병행되어야 합니다. [본문으로]
Windows Azure Table Storage에 전송할 Query를 지연된 실행을 위한 객체로 초기화합니다. 이 문장이 실행되었다고해서 곧바로 데이터가 수집되는 것은 아닙니다. [본문으로]
Windows Azure Table Storage에 실제로 Query를 전송할 클라이언트 객체를 초기화하고, 이 객체의 재시도 정책도 추가로 정의할 수 있습니다. 여기서도 3회 재시도, 매 시도마다 1초 간격을 두기로 설정합니다. [본문으로]
Table Storage에서 객체를 삭제할 때에는 삭제를 위하여 검색할 대상을 지정해야 하며 이 때 사용하는 것이 AttachTo 메서드입니다. 이 메서드를 이용하여 어떤 테이블에서 어떠한 유형의 데이터를 검색할 것인지를 서비스 객체에 지정합니다. [본문으로]
Plain Old CLR Object (Plain Old Java Object; POJO를 응용한 줄임말) [본문으로]
Plain Old .NET Framework Object (Plain Old Java Object; POJO를 응용한 줄임말, POCO와 동일한 의미의 다른말) [본문으로]
이 글을 작성하는 시점에서 ASP.NET MVC 3에서 기본으로 채택될 예정인 Razor View Engine이 새로 발표되었습니다. Razor View Engine은 Web Form을 대체하는 ASP.NET MVC 전용의 View Engine 시스템을 뜻합니다. [본문으로]
닷넷기반위에서 게임프로그래밍을 하기 위해서 기존네이티브코드로된 엔진을 매나지드방식으로 불러오는 방법을 생각해볼수있습니다.
저는 오거,일리히트,네뷸러,판다3D등의 공개엔진중에서 일리히트엔진을 선택했는데요.
가장 중요한 이유로 유연성이 가장 좋은 엔진을 선택 했습니다. 오거도 멀티 플랫폼이지않느냐 반문하실수도있겠는데요. 제가 말하는건 멀티 플랫폼뿐만 아닙니다. 언어적으로도 유연해야된다고 봅니다. 일리히트를 표현 하는데 언어는 중요치 않습니다. c++로 구현된 예가 있을뿐 c++기반은 아니기 때문입니다.(영어로 쓰여진 성서책이있을뿐 성서가 영어는 아니죠.) ,irrlicht 는 단순한 게임엔진이 아닌 게임엔진 구조 설계 철학이라고 이해 하시면 됩니다.
이런 철학은 닷넷과 궁합이 잘맞아 떨어집니다.
일리히트엔진 공식 프로잭트 싸이트는 http://irrlicht.sourceforge.net 입니다. 여기서 정식 버전(1.7.1)을 릴리즈하고있으며 현제는 1.8버전을 준비중에 있습니다. 여러 언어와 바인딩한 예가있는데요. 그중에서 닷넷기반 으로 바인딩하는 방법에 대해서는 일리히트 포럼에 가셔셔 먼저 irrlicht lime을 검색하시면 관련 정보를 얻으실수있습니다.
첫 번째 방법에서 std::string을 사용한 이유는 다름이 아니고 메모리 확보 때문입니다.
마샬링을 통해서 char*와 wchar_t*에 메모리 주소를 저장합니다. 문자열 그 자체를 복사하는 것이 아닙니다. 그래서 변환한 문자열을
저장할 메모리 주소를 확보하고 사용 후에는 해제를 해야 합니다. 메모리 확보와 해제를 위해서 marshal_context를 사용합니다.
marshal_context는 변환에 필요한 메모리를 확보하고, 스코프를 벗어날
때 메모리를 해제합니다.
const char*
s2;
const
wchar_t* s3;
{
marshal_context ctx;
s2 = ctx.marshal_as<const
char*>(s0);
s3 = ctx.marshal_as<const
wchar_t*>(s0);
}
String^을 C/C++ 문자열로 변환할 때는 std::string + marshal_as 나 marshal_context 둘
중 하나를 선택하여 사용합니다.
사실 필자는 SOA(Services Oriented Architectures) 에 대해서 심도 있게 알고 있는 전문가는 아닙니다. 전문가는 아니지만 WCF=SOA 에 대해 제가 알고 있는 작은 부분으로 WCF=SOA 를 오해하거나 잘못 알고 계신 분들이 있는 것 같아, 이 부분을 바로 잡아 보려고 합니다.
우선 SOA(Services Oriented Architectures) 는 직역하면 "서비스 지향 아키텍처"라고 부릅니다. 서비스 지향 아키텍처는 서비스를 제공하는 벤더(Vender) 또는 프로바이더(Provider) 를 통해 유연한 서비스를 제공하거나 제공 받는 아키텍처를 말합니다. 그리고 이 SOA 와 가장 연관 깊은 것이 바로 XML 웹 서비스(Web Services) 입니다.
XML 웹 서비스는 특정 벤더나 프로바이더를 통해 서비스를 제공 받고, 표준으로 채택됨으로써(http://www.w3.org/2002/ws/) 플랫폼 간의 상호 운용성을 높인 기술입니다. 어떤 운영체제와 플랫폼이든 이 메시지를 이해할 수 있는 텍스트(Plain Text)로 이루어진 XML 과 WSDL, XSD 스키마를 사용하여 해석되는 것입니다. 이것을 가리켜 SOAP 프로토콜이라고 합니다. (사실 Microsoft 만큼 SOAP 을 잘 구현한 벤더는 없습니다. 유효성 검사를 해 보면 정말 엉망으로 구현하는 곳이 의외로 많습니다)
그리고 .NET Framework 3.0 부터 WCF(Windows Communication Foundation) 이라는 기술이 출연하게 됩니다. 그리고 많은 사람들은 .NET 리모팅, XML 웹 서비스, TCP/UDP(UDP는 .NET 4.0 에 포함됨) 등을 통합한 프레임워크가 WCF 라고 이해하고 있습니다. 더불어 SOAP 뿐만 아니라 JSON, REST 방식의 통신 레이어 행위(Behavior)를 지원함으로써 "서비스 지향 아키텍처"라고 말하기도 합니다.
필자는 이러한 SOA 에 대해서 작은 지식을 가지고 있지만, WCF 와 SOA 와 연관 관계를 지어 대화를 하게 될 때 매우 서먹하기도 합니다. 왜냐하면 바라보는 기술의 관점이 틀리기 때문에 말입니다.
일단 용어를 명확히 하자면, SOA 는 어떤 명확한 구현 또는 산출물의 기술이 아닌 이상적인 아키텍처 측면을 의미합니다. 그리고 이상적인 아키텍처를 이야기 할 때 양자간의 관점이 틀릴 경우, 사실 그만큼 대화하기 힘든 것도 없었던 것 같습니다.
그렇다면, 여기에서 한 가지 궁금한 것이 생깁니다. XML 웹 서비스, WCF 둘 다 서비스 지향적인 아키텍처인가요?
먼저 일부 WCF 를 가리켜 SOA 라고 인지하는 분들의 경우, REST나 JSON 같은 경량화된 데이터 포멧(Data Format) 을 지원하기 때문에 서비스 지향적이라고 말합니다. 이것은 즉, 클라이언트가 웹 브라우저든 어떤 형태건 간에 선택적인 데이터 통신이 가능하다는 의미입니다. WCF 에서는 아주 간단하게 SOAP 데이터를 REST 방식이나 JSON 방식으로 쉽게 전환할 수 있죠.
또, 어떤 사람들은 여러 가지 통신 프로토콜을 통합하였기 때문에 서비스 지향적이라고 말합니다. .NET 리모팅이나 XML 웹 서비스, MSMQ, COM, COM+ 등을 통합했다는 이유로 말입니다.
그럼, 다시 질문하자면, XML 웹 서비스는 JSON 과 REST 를 지원하지 못하는 것일까요?
답변은 XML 웹 서비스는 이 과정을 .NET Framework 에서 기본적으로 제공하지 않을 뿐이지, 충분히 가능합니다. XML 웹 서비스는 통신 레이어 이전에 직렬화 및 역직렬화(Serialization/Unserialization) 과정을 거치는데, 이 과정에서 SOAP 을 REST 또는 JSON 으로 직렬화/역직렬화 하면 가능합니다.
...
그렇다면 도대체 뭐가 서비스 지향적인 아키텍처인가?
일반적으로 일부 사람들이 이해하는 WCF 는 어떠한 방법으로도 서비스 지향적인 아키텍처를 제공하지 않습니다. 왜냐하면 WCF 든 XML 웹 서비스든 그 내부를 이해하지 못하고 사용한다면, 통신 프로토콜만 다를 뿐, 똑같은 XML 웹 서비스에 지나지 않기 때문입니다.
필자는 서비스 지향 아키텍처에 WCF 에 한 표를 던져 주고 싶습니다. WCF 는 기본적인 환경에서 서비스 지향적인 프로그래밍을 제공해 주지는 않지만, 이미 WCF 프레임워크의 내부는 서비스 지향적인 아키텍처로 설계가 되었기 때문입니다.
즉, ESB(Enterprise Services Bus) 는 서비스 가상화(Services Virtualization) 을 통해 중앙 집약적인 정책을 수립하여 적용하는 것입니다. 서비스의 제공자와 서비스의 소유자간의 명확한 계약(Contracts) 을 통해서 정책적, 중앙 집약적인 서비스가 가능한 것이 ESB 의 모티브이기도 합니다. 바로 서비스 가상화는 이 두 사이의 명확한 계약 관계가 성립됨으로써 가능한 것입니다.
또한, 서비스 가상화를 통해서 서비스를 재조합(Recomposition) 이 가능한 것 또한 ESB 의 특징이기도 합니다. 서비스의 제공자와 서비스의 소유자 간의 명확한 계약 관계가 성립됨으로써 서비스의 제공자를 다른 서비스로의 전이, 신규 서비스 창출, 서비스의 조합이라는 것이 가능합니다.
필자는 이러한 ESB 에 좀 더 가까운 개발 프레임워크를 만들기 위해 아래와 같은 프레임워크를 개발한 적이 있습니다. (아래는 관련 업계에 재직 당시에 개발했던 프레임워크입니다)
첫 번째로, 명확하게 서비스(Services) 와 서비스의 계약(Services Contracts or Interface) 를 구분함으로써 서비스의 소유자와 서비스의 제공자를 구분하였고, Dynamic Proxy 기법을 통해서 동적인 서비스적인 측면을 지향하였습니다.
다시 말하면, WCF 는 SOA 를 위한 인프라스트럭처(Infrastructure) 를 제공할 뿐이지, XML 웹 서비스와 같이 '서비스 참조' 기능만 이용한다면 그것은 그냥 XML 웹 서비스와 크게 다를 바가 없다는 의미입니다.
좀 더 서비스 지향적인 서비스를 위해...
이미 .NET Framework 는 더 이상 연습하고 학습하는 것만으로 습득할 수 있는 기술이 아닙니다. 마치 수십 수백 권의 "백과 사전" 수천 페이지가 바로 .NET Framework 입니다. 그 백과 사전이 바로 MSDN Documents Library 입니다.
.NET 기술을 올바르게 사용하는 것은 매우 쉽습니다. 왜냐하면 MSDN 은 너무나도 자세한 부분과 샘플까지 제공하기 때문입니다. 더 중요한 것은 기술을 올바르게 이해하고 활용하는 것이지, 남용을 한다면 .NET 기술은 그저 그런 기술 밖에 되지 않습니다. 마치 .NET vs Java 를 도마 위에 올려 놓고 100분 토론을 하는 것처럼 말이죠.
필자는 아마도 "서비스 지향적인 아키텍처"가 아니라 "서비스 지향적인 서비스"에 좀 더 초점을 맞추고 .NET 기술이 나아갈 방향을 고민해야 할 시점이 아닌가 생각합니다.
제가 처음 했던 포스팅에서 , Twitter.Profile 을 호출해서 트윗 위젯을 간단히 구성하는
것을 소개해 드렸습니다.
이는 WebMatrix에서 제공하는 헬퍼로써 ,
Microsoft.WebPages.Helpers.dll 안에 포함되어 있습니다.
아마 Razor 가 Visual Studio 에 정식적으로 포함될 때 이
helper 가 포함되면 , 개발자분들이 좀더 활용할수 있을거 같습니다. 해당 헬퍼는 서드 파티 벤더의 위젯을 설치할수 있는 것
외에도 analytics , 파일 업로드 , 아바타 , 메일등 폭넓은 기능을 제공하므로 꼭 한번 둘러보시기 바랍니다.
지난 아티클에서는 간단하게 Razor 를 이용해서 프로그램을 구동하는 방법에 대해 알아보았습니다. 그런데 이 아티클을 읽으신 개발자 분들은 아마도 , 자신이 만든 메서드를 Razor 로 꺼내서 사용하는 방법에 대해 궁금하셨을 겁니다.(저는 궁금했습니다.. 개발자니까!) 그래서 이번시간에는 간단하게 자신이 만든 객체를 활용해서 메서드를 재 활용해보는 방법을 알아보도록 하겠습니다.
일단 WebMatrix에서 프로젝트를 생성하고 클래스파일을 생성합시다. 클래스 파일은 기본적으로 노출되어 있지 않으므로 , FileType 에서 All 을 선택해야 합니다. 이곳에서 cs 파일을 선택한후 이름을 선택하고 , OK 버튼을 눌러서 파일을 생성합시다.
이제 해당 메서드를 렌더링 해서 보여줄 html 페이지를 만들 차례 입니다. 같은 순서대로 Test.cshtml 파일을 생성합시다.
메서드의 재활용을 위해서 저는 3개의 메서드를 static 으로 생성하였습니다.
//문자열반환!
publicstaticstring
GetHelloString()
{
return"Hello Ciel!";
}
//문자열의길이반환(1개의변수받음)
publicstaticstring
GetStringLength(string orgStr)
{
return
orgStr.Length.ToString();
}
//문자열의길이 * int 변수 , (2개의변수받음)
publicstaticstring
GetStringLengthMix(string orgStr , int mulVal)
{
return (orgStr.Length
* mulVal).ToString();
}
그후에 cshtml 에 razor 코드를 삽입하고 웹 페이지를 구동 시켜보도록 하겠습니다.
처음 코드를 구동시키면 대부분 이 에러를 보시게 될 겁니다. 이 에러를 이해하기 위해서는 이 webmatrix의 웹 프로젝트의 성격을 이해하셔야 합니다….만 . . . 사실 그닥 특별한 건 없습니다. 이 웹 메트릭스는 Visual Studio 에서 생성되는 프로젝트중 website 프로젝트의 형태를 띄고 있습니다. 그렇기 때문에 모든 컴파일 되는 cs 파일은 App_Code 폴더 안에 삽입되야 합니다.
1~3 번 메서드는 일반적인 C#코드를 재 호출하는것으로써 특이할 부분이 없지만 , 마지막 부분에서 처음 부분에 변수명을 명시해줌으로써 Json 형태로 변수를 선언해 줄 수 있다는 부분이 다소 흥미로운 부분이 될 거 같습니다.
Summary 이번 포스팅에서는 간단하게 cs코드에서 메서드를 선언하고 그 메서드를 호출하는 법을 살펴보았습니다. 사실 이정도만 하더라도 , 대부분의 작업을 대체 하는데는 무리가 없을겁니다. 다음 포스팅에서는 처음 포스팅에서 언급했던 부분인 webmatrix 헬퍼에 대해서 알아보도록 하겠습니다.
지난 회 차에 여러 가지 문제로 .NET 스마트클라이언트가 가진 문제점을 살펴 보았습니다. 그 중, 주된 이슈는 이미 로드된 어셈블리는 업데이트/갱신이 불가능하다는 것과, 메모리의 사용률이 지속적으로 증가한다는 문제입니다. 이러한 문제는 사내 정책적인 서버를 도입하여 해결 가능하지만, 대부분의 조직과 기업은 이러한 정책 서버를 도입하지 않는 것으로 알고 있습니다.
이미 얘기 했다시피, 평소에도 .NET 에서 이러한 문제를 가지고 고민을 했었지만, 최근 이러한 문제가 이슈가 되었을 때 더 이상 필자 또한 방관할 수 없었습니다. 왜냐하면 "안된다!" 라는 것 자체가 .NET 의 많은 매리트를 배제한다는 의미가 될 수 있기 때문입니다. 이러한 문제로 '목숨거는' 고객이라면 차라리 '지금은 곤란하다. 조금만 기다려달라' 라는 답이 훨씬 나아 보입니다. 물론 이런 문제가 가능하다는 전제 조건으로 말입니다.
문제 해결 방안
일단, 몇 날 몇 일을 고민하며 생각한 끝에 아래와 같은 아키텍처링을 하게 되었습니다. 물론 최선의 방법도, 최적의 방법도 아니지만, 문제가 된다면 저에게 피드백을 주시기 바랍니다. 저 또한 짧은 지식으로 이러한 고민을 하게 되었으니 저도 많이 답답하네요^^;
위의 아키텍처링은 논리적인 아키텍처입니다. 이 방법을 통해 이전 아티클을 통해 골치 아픈 .NET 어플리케이션의 메모리 릭(Memory Leak) 을 해결할 수 있을 것으로 기대합니다.
어플리케이션과 AppDomain 의 분리
.NET 어플리케이션은 기본적으로 하나의 프로세스(Process) 를 차지하게 됩니다. 그것이 독립 프로세스든, IEHost.DLL 또는 IEExec.EXE 든 간에 말이죠. 이 독립 프로세스는 독립적인 하나의 어셈블리에서 관장하게 됩니다. 기본적인 이 부분의 컨셉은 어플리케이션의 재시작을 방지하기 위한 방법이기도 합니다.
기존의 어플리케이션의 프로세스와 AppDomain 을 분리함으로써 최소한으로 AppDomain 이 안전하게 언로드될 수 있는 환경을 제공하는 것입니다. 그리고 위해 AppDomain Manager 는 이것을 관장하는 최상위 Manager Layer 가 됩니다.
MVVM 으로 구현부와의 분리
MVVM(Model View ViewModel) 패턴의 가장 큰 특징은 View 와 ViewModel 을 분리한 것입니다. 이것을 분리함으로써 View 와 ViewModel 의 종속 관계를 완전히 해결하고, ViewModel 은 격리된 AppDomain 으로 제한함으로써 언제든지 AppDomain 이 언로드될 수 있게 합니다.
이 부분을 구현하기 가장 이상적인 환경은 바로 WPF(Windows Presentation Foundation) 이 되겠네요.
Views 의 교체
MVVM 으로 구현부와의 분리를 통해 당연히 Views 는 언제든지 교체가 가능합니다. 서버/로컬에서 Views 가 교체된다면 ViewModels 을 언로드하고 새로운 Isolated AppDomain 을 생성하여 View 와 ViewModel 간에 연결하는 방법입니다.
특히 이 통신 구간은 View 와 ViewModel 간의 Interface Contract 를 통해 크리티컬한 자원의 관리를 최소화하는 것에 있습니다. 이로써 이미 로드된 사용자 화면과 어셈블리라도 서버/로컬의 갱신이 있다면 언제든지 갈아치울 수 있는 구간입니다. 이 부분이 앞서 얘기한 .NET 스마트클라이언트의 문제를 해결할 수 있는 핵심 구간입니다.
업데이트 기능을 재작성
이 아키텍처링의 가장 큰 문제지만, .NET 스마트클라이언트의 NTD(No Touch Deployment) 기능을 그대로 사용할 수 없습니다. .NET 의 NTD 는 이미 실행되는 AppDomain 에 어셈블리를 로드하기 때문에 .NET 의 NTD 를 그대로 사용한다면 이 아키텍처링을 적용할 수 없습니다.
NTD 기능 뿐만 아니라, ClickOnce 의 자동 버전 감지 기능도 사용할 수 없습니다. ClickOnce 는 주기적으로 서버의 Application Manifest 를 확인하는 과정으로 새로운 버전을 감지하고 업데이트하는데, 이 기능을 그대로 사용한다면 위의 아키텍처링은 사실상 무의미하고, 결국 메모리 사용 증가는 해결할 수 없기도 합니다.
제한 사항
하지만 필자가 제안한 .NET 스마트클라이언트의 문제를 해결하기 위한 방법은 제한적인 방법으로 수행이 가능합니다. 물론 모든 경우라도 제안이 가능한 방법이라면 좋겠지만, .NET 의 기본 아키텍처가 해결하지 못한 이상, 필자 또한 제한적인 방법으로 .NET 스마트클라이언트의 문제점을 해결할 수 있습니다.
그 제한적인 방법의 한계는 아래와 같습니다.
개발 표준을 완벽하게 MVVM 기반으로 개발되어야 한다.
MVVM 패턴으로 완벽하게 분리가 되어야 한다.
WPF 를 사용할 경우 MVVM 패턴으로만 작성되어야 한다.
윈도우 폼(Windows Forms) 또는 ActiveX 컨트롤 일 경우, MVVM 로 작성할 수 없다.
이 경우, View와 ViewModels 를 분리하도록 별도 프레임워크 개발이 필요하다.
Marshaling 을 통한 통신
Marshaling 은 AppDomain 간의 원격 통신을 해야 한다.
원격 통신으로 인한 성능 저하
WPF 개발
Binding Expression 을 확장한 Binding Marshaling Expression(단지, 예임) 으로 바인딩을 해야 한다.
원격 바인딩으로 성능 저하 예상
결론
필자는 .NET 어플리케이션이 업데이트될 경우 왜 반드시 최적의 방법이 어플리케이션 쉘을 재시작하느냐에 시작한 고민으로부터 시작됩니다. .NET 아키텍처를 이해못하는 것은 아니지만, 고객은 언제나 더 향상된 방법을 제안합니다. 그리고 필자는 그런 고민을 극복하고자 제안한 방법입니다.
물론 위의 아키텍처링을 효율적인 면과 성능적인 면을 더 자세히 테스트해 보아야 하겠지만, 분명한 것은 끊임없이 고객의 요청은 진화하지 퇴화하지는 않을 거라고 생각합니다.
예전에 필자는 위와 같은 문제를 문의할 때, ".NET 에서는 안된다" 라고 답했습니다. 맞아요. 안됩니다. 하지만 문득, '안되면 되게 해야지!' 라는 생각이 들더군요. 짧은 소견이지만 잘못된 부분이 있으면 언제든지 피드백 주시기 바랍니다.
.NET 에서 윈도우 어플리케이션을 개발해 본 독자라면 한번 쯤은 .NET 스마트클라이언트라는 용어를 많이 들어보았을 것입니다. 스마트클라이언트는 배포(Deployment), 플랫폼 독립 모델을 제공함으로써 다양한 클라이언트를 지원하는 것이 특징입니다.
예전에 필자가 UX 라는 주제로 쓴 포스트 중 "당신이 생각하는 UX 란?" 에서도 언급하였듯이, .NET 스마트클라이언트는 X-Internet 이라는 트랜드로 기술적인 부분을 초점으로 마케팅한 용어로 발전하였습니다. 이와 반대로 RIA(Rich Internet Application) 는 UX(User eXperience) 초점에서 마케팅한 용어라고 보셔도 좋습니다.
사전 지식
하지만 .NET 스마트클라이언트는 사실상 매번 나오는 이슈가 있습니다. 아니, 이것은 .NET 스마트클라이언트의 문제라기 보다는 .NET 자체의 아키텍처와 관련된 문제이기도 합니다.
결혼부터 말하자면, .NET 어플리케이션은 로드된 어셈블리(Loaded Assemblies) 는 언로드(Unload) 가 되지 않습니다. 간단하게 아래와 같이 .NET 어플리케이션의 모델을 보면 알 수 있습니다. .NET 어플리케이션은 하나의 AppDomain(Application Domain) 을 갖는 것을 알 수 있습니다.
AppDomain 은 어플리케이션 간의 CAS(Code Access Security) 라는 임계 영역에 존재하게 됩니다. 말 그대로 CAS(Code Access Security) 이 CAS는 어플리케이션간의 엑세스를 제한함으로써 신뢰할 수 없는 코드나 어플리케이션은 사용자의 컴퓨터에서 실행할 수 없도록 한 보안 모델입니다.
즉, 이메일이나 인터넷, 사용자 그룹 및 권한 등 신원이 확인되지 않은 어플리케이션을 실행했을 때, 악의적인 목적으로 사용자의 로컬 자원을 엑세스할 수 없도록 제한하는 모델이라고 보시면 됩니다.
이 코드 보안 모델은 .NET 의 어떤 어플리케이션이든 모두 이 보안 정책 안에 있다고 보시면 됩니다. ASP.NET 도 마찬가지로 아래와 같이 AppDomain 의 임계 영역 안에서 어플리케이션이 동작하게 됩니다. AppDomain 이 하나의 웹 어플리케이션을 동작하게 하고, HttpRuntime 에 의해 HttpContext 가 관리됩니다. 그리고 각각의 요청에 의해 HttpContext 는 별도의 스레드(Thread) 로 사용자의 요청을 응답하게 되는 구조라고 보시면 됩니다.
예를 들어, 아래와 같은 코드 보안을 위한 선언적인 방법을 이용하여 악의적으로 사용될 수 있는 코드 쓰기, 수정 등을 할 수 없도록 합니다. 어셈블리, 클래스, 구조체, 생성자에서 사용할 수 있습니다. 물론 사용자가 이 보안 수준을 변경할 수 도 있지요.
문제 1
여태까지 이것을 말하기 위해 설명을 한 것입니다. 바로 .NET 어플리케이션은 어셈블리를 로드할 수 는 있지만, 언로드할 수 는 없습니다.
그러니까 더 자세하게 얘기하면, 아무리 가비지 컬렉션(Garbage Collection) 을 호출하고 CLR Runtime(Common Language Runtime) 이 이것을 대신 수행해 준다고 해도, 로드된 어셈블리 자체는 이 대상에서 예외라는 것입니다. 결론은 .NET 어플리케이션을 오래 쓰면 쓸 수록 메모리 사용이 증가할 가능성이 있습니다.
플러그인 모델(Plugin Model) 기반의 어플리케이션도 확장 기능이 많아지면 많아질 수록 메모리 점유율이 높아지고, 특히 엔터프라이즈 기업용 어플리케이션은 반드시 피해갈 수 없는 문제이기도 합니다.
개인적으로 플러그인 모델과 엔터프라이즈 어플리케이션의 중간 영역이라고 생각되는 Visual Studio 를 한 1주일 정도 닫지 않고 써보셨나요? 쓰지 못할 정도는 아니지만, 괜히 버벅되고 느려지는 현상이 나타나게 된답니다.^^; 이런 현상은 Visual Studio 뿐만이 아니라 .NET 으로 작성된 모든 어플리케이션은 모두 영향을 받게 됩니다.
그 이유는, .NET 은 로드된 AppDomain 의 어셈블리를 언로드할 수 있는 방법을 제공해 주지 않습니다. AppDomain 이 참조하는 관계는 기본적으로 로컬 자원의 어셈블리를 참조하겠지만, 코드 베이스(Code Base-코드의 출처) 가 인트라넷이나 인터넷이라면 그 코드 베이스로부터 어셈블리를 다운로드 하게 됩니다.
문제 2
결론부터 말하면, .NET 어플리케이션은 참조 또는 다운로드한 어셈블리는 다운로드 캐시(Download Cache) 에 보관하게 됩니다. 어셈블리를 참조 또는 다운로드하는 판정 조건은 어셈블리의 버전, 토큰 등 복잡한 과정을 거치기 때문에, 제대로 된 정책을 갖고 있지 않는다면, 이미 다운로드된 어셈블리는 다운로드 캐시로부터 어셈블리를 재사용합니다.
그렇기 때문에, 다운로드된 어셈블리는 File Lock(파일 잠김)이 발생하므로, 동일한 파일 이름의 어셈블리를 다운로드 받는 것은 불가능 합니다. 하지만 해결책이 없는 것은 아닙니다. Assembly.Load 시리즈의 메서드에는 byte[] 로 읽을 수 있는 오버로드된 메서드가 존재하기 때문입니다.
즉, 아래와 같이 File Lock 을 방지할 수 있습니다. 하지만 어셈블리는 로드할 수 있으나, 기존의 로드된 어셈블리를 갈아치우지는 못합니다.
결국, 하나의 어플리케이션을 오래 사용하면 할수록 메모리의 점유율을 증가할 수 있게 될 가능성이 큽니다. 특히 엔터프라이즈 기업용 어플리케이션은 단위 업무별로 적절한 파일 크기, 업무간의 연간 관계 등을 고려하여 어셈블리를 모듈화하는데, 사실상 메모리 사용률 증가의 문제는 여전히 해결할 수 없는 문제입니다. 그 이유는, 앞서 말했듯이 어셈블리를 언로드할 수 있는 방법은 AppDomain 을 언로드하는 것이고, AppDomain 을 언로드하면 메인 어플리케이션을 재시작해야 된다는 문제입니다.
문제 3
이 섹션은 문제 2와 연관된 정책적인 문제입니다. 다운로드된 어셈블리는 다시 다운로드 받을 수 없기 때문에 선행적으로 몇 가지 정책적인 강제가 필요할 수 밖에 없습니다.
어플리케이션 쉘(Shell)
어플리케이션 쉘이 업데이트되면 어플리케이션을 재시작 해야 한다.
어플리케이션 실행 중 단위 어셈블리
단위 어셈블리가 한 번 다운로드되면 서버/로컬의 어셈블리가 갱신되도 다운로드 받지 못한다.
단위 어셈블리가 다운로드 되고 서버/로컬 어셈블리가 갱신되어도 알림 받을 수 없다.
이럴 경우, 어플리케이션 쉘을 서버에서 갱신하여 업데이트 알림을 받을 수 있고, 어플리케이션을 재시작 해야한다.
즉, 어떠한 경우라도 갱신된 어플리케이션을 적용하기 위해서는 메인 어플리케이션 쉘을 재시작해야 한다는 결론을 얻을 수 있습니다.
이유의 요지는, IEHost.DLL 과 IEExec.EXE 파일이 .NET Framework 2.0 으로 강력한 이름의 서명이 되었다는 것입니다. 이것은 즉, IEHost.DLL 과 IEExec.EXE 를 통하는 .NET 스마트클라이언트의 경우 GAC(Global Assembly Cache) 를 통해 활성화가 되는데, .NET Framework 4.0 의 스마트클라이언트 어플리케이션은 어셈블리 리디렉트(Assembly Redirect)를 통하지 않고서는 이것을 활성화할 수 있는 방법이 없습니다. 어셈블리 리디렉트를 통한다고 하더라도 Dependency Assemblies 는 .NET Framework 2.0 을 바라보기 때문에 .NET Framework 4.0 의 기능을 사용한다면 절대 불가능하기도 합니다.
하지만 .NET 어셈블리의 바이트 코드 조작을 통해서 가능하긴 합니다.
IEHost.DLL, IEExec.exe 의 바이트 코드를 수정하여 강력한 서명을 지운다
IEHost.DLL, IEExec.exe 의 바이트 코드를 수정하여 .NET 4.0 으로 저장한다
GAC(Global Assembly Cache) 에서 IEHost.DLL 과 IEExec.EXE 를 제거한다.
어셈블리의 바이트 코드 조작은 Mono 프레임워크를 통해서 아주 쉽게 할 수 있습니다. 하지만 IEHost.DLL 과 IEExec.EXE 를 사용하는 모든 사용자 클라이언트를 해킹하는 무자비한 방법입니다. 하지만 된다는 것만으로도 만족한다면 이 방법이 최선의 방법이 될 것 같네요.
.NET 스마트클라이언트의 고찰
.NET 스마트클라이언트는 .NET 엔터프라이즈 어플리케이션에 많은 기여를 하였습니다. 그리고 .NET 스마트클라이언트를 사용하는 기업 또는 인트라넷 환경은 매우 많기도 합니다.
필자 또한 얼마 전에 이러한 고민으로 Microsoft 의 의뢰를 받은 적이 있습니다. 그리고 개인적으로 아주 많이 고민했습니다.
왜냐하면 자바의 클래스 로드(Class Loader) 는 .NET 의 스마트클라이언트와 유사한 점이 굉장히 많습니다. 하지만 다른 점이 하나 있다면, 자바의 클래스 로더는 GC(Garbage Collection) 의 대상이 된다는 것이죠. 다시 말하면, 어플리케이션의 재시작 없이 마음만 먹으면 메모리 사용률이 증가하지 않도록 아키텍처링이 가능하다는 것입니다.
필자가 결론적으로, .NET 의 AppDomain 과 자바의 클래스 로더는 각기 특색은 있지만, 어느 것이 정답인지는 모르겠습니다. 다만, 고객이 어플리케이션의 재시작 없이 어플리케이션 업데이트/갱신이 가능해야 한다는 전제 조건이라면 자바의 클래스 로더가 장점이긴 합니다.
하지만, 필자는 이 문제로 몇 일 동안 고민했습니다. 왜냐하면 세상에는 불가능한 것이 없다라는 것이 필자의 신념이기도 하며, 어떤 문제든 최선의 방법이라는 것이 존재한다고 믿습니다. 그리고 결국 "빙고" 를 찾았습니다. ^^
다음 회 차에서는 .NET 스마트클라이언트의 이러한 문제를 개선할 수 있는 방법을 알아보도록 하겠습니다.
댓글을 달아 주세요