요즈음 클라우드 서비스들을 이용하다보면, Windows 서버 운영체제를 통해서 확장성있는 클라우드를 만들고자 하는 경우가 자주 있습니다. 일반적인 웹 사이트를 구축할 때에도 마찬가지이고, 당연히 KT UCLOUD나 Amazon과 같은 환경에서도 같은 노력이 뒷받침이 되어야 하지요. 그리고 제가 주 전공으로 하고 있는 Windows Azure 역시, 첫 배포 때에는 간과하기 쉬운 점이 바로 로드 밸런싱 환경이라는 점입니다.

이러한 로드밸런싱 환경을 만들때에는, 이전에 구축해본 경험이 없는 관리자가 개발자 입장에서는 상당히 어려운 문제에 봉착하게 될 가능성이 많습니다. 특히 요즈음 웹 환경에서는 당연하게 사용하는 세션이나 쿠키에 관련된 설정들이 로드밸런싱 환경에서 기대했던 것과 다르게 동작해서 좌절하는 경험을 많이들 하실텐데요, 제가 오늘 블로그에 올리는 것은 ASP.NET에 관한, 그리고 IIS 7에 관한 내용입니다. (PHP나 JSP 개발자분들께서도 공감하실 수 있는 부분이 있을 것입니다.)

로드밸런싱 환경을 잘 알고 구축할 수 있다면, 앞으로 나오게될 어떤 종류의 클라우드 서비스이든 관계없이 문제를 정확하게 해결할 수 있을 것입니다. 사실 클라우드 기반의 웹 서비스는 달리 표현하면, 기본 골자는 로드밸런싱에 기반을 두고 있는 것이고, 그 이후의 확장성 전략을 클라우드 솔루션으로 채우는 것과 같다고 말할 수 있습니다. (어떤 뼈대를 사용할 것인지는 전적으로 여러분들의 선택에 달린 것입니다.)

로드 밸런싱 환경이란?

로드 밸런싱 기술 자체는 상당히 오래된 것입니다. 이름에서 알 수 있듯이, 몰려오는 트래픽을 내부적으로 분산하여 특정 서버 컴퓨터로 연결이 몰려 서비스가 사용 불가 상태로 빠지는 것을 "지연"시키거나 "완화"시키는 것에 목적이 있습니다. 로드 밸런싱의 기술적 개념도는 다음과 같습니다. (이미지 출처: http://msdn.microsoft.com/en-us/library/ff650667.aspx)

다양한 상황에서 로드밸런싱이 쓰이겠지만 가장 일반적으로는 웹 환경에서 많이 쓰입니다. 연결을 오래 유지할 필요가 없으면서도, 짧은 시간 내에 빠른 연결 회전을 보이는 웹 프로토콜에서 가장 중요한 것은 바로 신속성인데, 분산 처리를 하지 않는 경우에는 필연적으로 서버 컴퓨터가 받아들일 수 있는 동시 연결 한계치에 금방 치닫게 됩니다. 그러나 로드 밸런싱을 정확히 사용하면 이러한 한계치에 치닫게 되는 속도가 로드 밸런싱에 참가하는 컴퓨터의 댓수만큼 반비례하게 됩니다. 그리고 이 때 하나의 웹 사이트를 위한 로드 밸런싱 서비스에 멤버로 참여하는 서버 컴퓨터들을 묶어서 "웹 팜"이라고 정의를 하는 것이지요. 더 일반적으로는 "서버 팜"이라고도 합니다.

잠시 다른 이야기로 넘어가자면, 요즈음 대두되는 클라우드 컴퓨팅은 관리 측면에서 봤을 때, 충분한 대역폭을 보장하는 연결과 매우 뛰어난 성능을 가진 로드 밸런서를 이용하여 연결을 분산하는 작업을 수행하는 것입니다. 그리고 웹 팜 안에 참여하는 컴퓨터의 유형에 있어서는 이전과 다른 점이 하나 있는데, 마치 구름과 같이 수축과 팽창을 자유자재로 한다는 것입니다. 물론 이런 수축과 팽창이 가능함은 내부적으로 가상화 솔루션을 이용했다거나 여기에 대응할 수 있는 알고리즘을 사용했다는 가정이 깔려있는 것입니다.

정말 완벽하고 정확하게 구축했다면, 적은 전원이나 자원 공급으로도 충분히 웹 팜이 유지가 될 수도 있고, 필요하다면 웹 팜의 크기가 엄청나게 커질 수도 있겠지요. 이걸 여러분이 관리하신다면 프라이빗 클라우드, 신뢰할 수 있는 IT 기업이 관리한다면 퍼블릭 클라우드가 된다고 보실 수 있겠습니다. 그러나, 클라우드 컴퓨팅이 만능약처럼 들릴 수 있는 부분이 있지만 정확히 알아야 할 것은 클라우드 컴퓨팅 역시 이 로드 밸런싱을 기초로 만들어지는 것이고, 여러분이 운영할 수 있는 한계에까지 트래픽이 몰리거나, 이런 일을 하는 IT 업체에게 지불할 수 있는 재정의 한계에까지 트래픽이 몰린다면 이것이 여러분이 생각할 수 있는 클라우드의 한계입니다. 무제한이라고 해서 값이 저렴하거나 무료에 수렴하는게 아님을 명확히 이해하고 있어야 합니다.

웹 로드 밸런싱을 위한 이야기

다시 본론으로 돌아와서, 웹을 로드 밸런싱할 수 있으려면 무엇을 검토해야 할까요? 가장 중요한 것은 웹 서버에 참여하는 각각의 컴퓨터 자체에는 "절대로" 컴퓨터의 고유한 정보를 가지고 있으면 안된다는 점입니다. 매우 단순한 이야기같지만 이러한 원칙을 지키지 않도록 설계되어있는 것이 지금 이 시점까지의 서버 컴퓨팅 기술들의 대다수의 원칙입니다. 간단한 예를 들어볼까요?

여러분이 일상적으로 사용하는, 웹을 통한 파일 업로드 기능을 담당하는 간단한 웹 앱이 있다고 가정해 보겠습니다. 이 웹 앱은 서버가 한 대 일때에는 참 쉽고 빠르게 설치해서 쓸 수 있었습니다. 당연히, 설치를 잘 했다면, 사용자가 웹 페이지를 방문해서 파일을 업로드하면 웹 서버가 그것을 알아보고 파일을 회수해서 하드 디스크 어딘가에 저장하겠지요. 그러나 시간이 지나서 이 웹 앱의 기능을 업그레이드하고 좀 더 많은 사용자들이 파일을 저장하고 다운로드할 수 있도록 만들어보고자 해서 로드 밸런싱 환경을 구축하여 베타 테스트를 시작했습니다. 그런데 어떤 문제들이 생겼을까요?

앞서 이야기한 기술적인 특성때문에, 사용자들은 분명히 조금전까지 파일을 업로드했었는데 페이지를 다시 와서보니 파일이 업로드되지 않은 상태로 페이지가 나와서 혼란스러워합니다. 혹은 파일을 어디로 빼돌린거냐며 분노하는 사람들도 있구요. 그래서 몇 번 F5키를 누르다보면 "어라?"하고 놀라게 됩니다. 조금 전에 업로드했던 파일이 다시 나타나니까요. 그러고나서 그 파일을 다운로드하려고 링크를 클릭하면 이번엔 또 다시 404 오류를 만납니다. 이제 사용자들은 이 서비스에 대해서 대단한 분노와 원성을 쏟아낼 것입니다. 서비스 상태에도 일관성이 없을 뿐 아니라 불안정한것 같다. 믿을 수 없다면서요.

이것이 일선 IT 현장에서 로드 밸런싱이나 클라우드를 처음 접목했을 때 겪는 "가장 흔하고 일반적인 장애"입니다. 더 안타까운 것은, 이것을 신 기술에 의한 책임으로 회피하고 문제시하는 것입니다. 문제의 본질을 정확히 알고 있다면 이렇게 말하는 것이 왜 잘못인지도 금방 알 수 있을 것입니다.

여기서 든 예제처럼, 이 웹 앱의 문제는 단순히 업로드한 파일을 자신의 컴퓨터에 저장하려고 했다는 데에 문제가 있습니다. 로드 밸런싱 멤버로 참여하는 컴퓨터가 자신의 상태를 중요하게 여기면, 다음번에 이어받는 다른 서버 컴퓨터의 입장에서는 이전에 그 컴퓨터가 무엇을 했는지 알 길이 없습니다. 그저, 찾고자 하는 내용이 없음을 이야기하는 수 밖에 없습니다. 이런 상황이 반복되면서 서비스 전체는 들어올때와 나갈때가 전혀 다른, 일관성이 없고 이상한 서비스가 되는 것입니다.

이 문제를 해결하기 위하여 어떻게 수정해야 할까요? 답은 간단합니다. 파일 저장소를 로드 밸런싱 멤버 컴퓨터 내부가 아닌, 여러 멤버 컴퓨터들이 같이 이용할 수 있는 공용 저장소로 바꾸는 것입니다. 가장 간단한 방법은 네트워크 UNC 경로로 이용할 수 있는 스토리지가 있을 수 있습니다.

여기서 궁금한 점이 하나 더 있는데, 그렇다면 로드 밸런싱에 의하여 애써 분산한 서비스가 다시 모이는 것이 아니냐고 반문할 수도 있습니다. 그런데 사실, 생각외로 사용자들이나 웹 크롤러와 같이 인터넷 상에서 발생하는 별 뜻없이 바쁘게 만드는 다양한 유형의 트래픽을 웹 팜 수준에서 한 번은 로드 밸런싱을 해주는 것 만으로도 실제 스토리지에 대한 요구 사항은 획기적으로 감소한다는 점입니다. 거기다, 역할 분담도 정확히 할 수 있으며 스토리지 자체에 대한 요구 사항이 폭증하는 것을 방지하기 위하여 기술적으로는 좀 더 복잡해질 수 있지만 캐싱 기능을 사용할 수도 있습니다. 이렇게 함으로서, 우리가 흔히 잘 아는 클라우드 서비스의 시작을 뗄 수 있게 됩니다.

기술적인 이야기 1 - 세션 처리 방법 바꾸기

그렇다면 IIS와 ASP.NET에서는 이런 이상한 상황을 예방하고 신뢰할 수 있는 서비스를 만들기 위해서 어떤 수정 사항을 반영해야 하는 것일까요? 제가 이제까지 인터넷 상으로 자료 조사를 해왔던 것은 모두 제각기 흩어져있는 정보들이었고 이것을 한 번에 취합할 수 있는 방법을 오늘 블로그 포스팅을 통하여 소개할까 합니다.

기본적으로 ASP.NET은 세션 처리를 IIS 프로세스 안에서 수행하도록 되어있습니다. 가장 동선도 짧고, 신속하게 반응하기 때문입니다. 그러나 로드 밸런싱 환경에서 이는 당연히 "채택하면 안되는" 기법입니다. 이 방법은 web.config 파일 안의 <sessionState> 요소에서 변경할 수 있는 부분으로, <configuration> 요소 아래의 <system.web> 요소 아래에서 없는 경우 새로 지정할 수 있습니다. <sessionState> 요소의 mode 속성의 값을 변경하면 됩니다. 지금 이야기한 부분은 mode 속성이 InProc으로 지정되어있거나, 아무것도 지정되어있지 않을 때 .NET Framework의 글로벌 web.config 설정을 바꾸지 않은 경우 기본으로 지정되는 설정입니다.

IIS 7에서 볼 수 있는 아래 그림과 같은 설정도 이 XML 파일의 수정을 텍스트 에디터 없이 수정하는 것입니다.

로드 밸런싱 환경에서 정상적으로 동작하는 웹 사이트를 만들기 위해서는 mode의 설정 값을 InProc 대신 StateServer나 SQLServer로 바꾸어야 하는데, 양쪽 값 모두 장단점이 있습니다. StateServer의 경우 기본적으로는 꺼져있는 ASP.NET State Service라는 NT 서비스가 제공하는 별도의 서버를 이용하는 방식이고, SQLServer는 이름에서 알 수 있듯이 실제 SQL Server를 사용하여 세션을 구현하는 방식입니다. 데이터베이스 서버의 성능이 세션을 모두 수용할 수 있을만큼 획기적으로 뛰어나거나, 세션 서버가 죽었다가 살아나도 로그아웃 처리가 안되게 한다던가, 혹은 여러 로드 밸런싱 사이트 사이에서 세션 공유를 안전하게 할 방법이 필요하다면 이 모드를 사용할 수 있습니다. 이에 비하여 StateServer는 별도의 SQL 서버 없이도 간편하게 구축할 수 있는 방법을 제공하긴 하지만, 세션 서버가 죽었다 살아날 경우 내용이 없어지는 휘발성 세션입니다.

양쪽 모드 모두 중요한 것은 멤버로 참여하는 웹 서버 컴퓨터 밖에 상태를 보관해야 한다는 것이 키 포인트로, 이것을 지키지 않고 멤버 컴퓨터 안에 이런 설정을 구축하면 전혀 나아지는 것이 없습니다. 그리고 당연한 이야기이지만 멤버 컴퓨터로 참여하는 모든 웹 서버가 같은 설정을 가지고 있어야 합니다.

StateServer와 SQLServer 모드를 구현하는 방법에 대한 자세한 내용은 아래 아티클을 참고하시면 되겠습니다.

http://msdn.microsoft.com/ko-kr/library/ms178586.aspx

기술적인 이야기 2 - ASP.NET 사이트 간에 립싱크 맞추기

세션을 공유하는 것 이외에, ASP.NET은 내부적으로 Machine Key라는 것을 사용합니다. Machine Key의 용도는 ASP.NET 안에서 참 다양한데, 가장 대표적으로는 클라이언트와 서버 사이에 쿠키 정보를 주고 받을 때 암호화하기 위한 수단으로 이용하는 것이 유명한 사례입니다. 쿠키를 이용한 취약점 공격은 웹 세계에서 너무나 당연한 공격 방식 중 하나이기 때문에 ASP.NET은 처음부터 이를 보완하기 위한 전략을 구현하고 있었습니다. 그러나 이것이 지금 와서 로드 밸런싱 환경이 되면서는 또 다른 어려운 문제로 바뀐 것입니다.

이 Machine Key라는 것 역시 서버 컴퓨터마다 고유하게 생성할 뿐 아니라, 매번 연결할 때 마다 다른 값을 생성하여 암호화에 사용합니다. 클라이언트 입장에서야, 서버가 "ABC"라는 쿠키를 주니까 "아 그렇구나. 나중에 돌려주면 서버가 날 알아보겠지?"하며 성실하게 반납합니다. 그런데 로드 밸런싱에 참여하는 A라는 서버 대신 C라는 서버가 이 쿠키를 받아들었을 때는 "이거 내것 아님" 하며 클라이언트에게 퇴짜를 놓습니다. 이것이 문제의 핵심인 것이죠.

이 문제를 해결하기 위해서는 아까전에 이야기한 주제보다 좀 더 많은 노력이 필요합니다. 생각보다, 보안을 완벽하게 유지하기 위하여 ASP.NET이 관리자들에게 요구하는 사항이 까다롭기 때문입니다. 이 Machine Key를 만들기 위해서는 별도의 생성 도구를 사용해야 합니다. 그러나 안타깝게도 이 도구를 구한다거나 만들 수 있으려면 개발자들의 조력이 좀 필요합니다. 그리고 개발자 본인들도 이런 방법을 찾아야 하기때문에 꽤나 귀찮습니다. Codeproject에 가면 이러한 방법을 자세히 설명한 아티클도 있습니다만 간단한 도구도 드리고, 코드 조각도 드리니 프로그램에 넣어 활용하시면 더 편리할 것입니다.

using System;
using System.Text;
using System.Security.Cryptography;

/* 중략 */

        public static string getRandomKey(int bytelength)
        {
            byte[] buff = new byte[bytelength];
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(buff);
            StringBuilder sb = new StringBuilder(bytelength * 2);
            for (int i = 0; i < buff.Length; i++)
                sb.Append(string.Format("{0:X2}", buff[i]));
            return sb.ToString();
        }

        public static string getASPNET20machinekey()
        {
            StringBuilder aspnet20machinekey = new StringBuilder();
            string key64byte = getRandomKey(64);
            string key32byte = getRandomKey(32);
            aspnet20machinekey.Append("<machineKey\n");
            aspnet20machinekey.Append(" validationKey=\"" + key64byte + "\"\n");
            aspnet20machinekey.Append(" decryptionKey=\"" + key32byte + "\"\n");
            aspnet20machinekey.Append(" validation=\"SHA1\" decryption=\"AES\"\n");
            aspnet20machinekey.Append("/>\n");
            return aspnet20machinekey.ToString();
        }

        public static string getASPNET11machinekey()
        {
            StringBuilder aspnet11machinekey = new StringBuilder();
            string key64byte = getRandomKey(64);
            string key24byte = getRandomKey(24);

            aspnet11machinekey.Append("<machineKey");
            aspnet11machinekey.Append(" validationKey=\"" + key64byte + "\"\n");
            aspnet11machinekey.Append(" decryptionKey=\"" + key24byte + "\"\n");
            aspnet11machinekey.Append(" validation=\"SHA1\"\n");
            aspnet11machinekey.Append("/>\n");
            return aspnet11machinekey.ToString();
        }

위의 코드를 사용하여 프로그램을 만들거나 ZIP 파일 안의 프로그램을 이용하여 값을 만들도록 하면 아래와 같은 XML 코드 조각을 얻을 수 있을 것입니다. 이 코드 조각을 각각의 서버에 들어있는 web.config에 지정하거나, 특정한 값만 인용하여 아래의 IIS 7 설정 아이콘에서 볼 수 있는 설정 도구를 통해서 직접 설정할 수도 있습니다.

<machineKey
 validationKey="FACBB6C89C44CB8BB7165FC4639BAA7267B...EF297D815E1BDD40E883E3451628CB95D34309"
 decryptionKey="4E95057676CC8DBA9AB...AACC1121B6B962E5AFA7849B0C82"
 validation="SHA1" decryption="AES"
/>

기술적인 이야기 3 - IIS에서 놓치면 안되는 것

ASP.NET을 가장 먼저 사용할 수 있게 된 웹 서버가 IIS이다보니 발생한 일종의 특성입니다만 여러 포럼에 걸쳐서 잘 언급되지 않는 문제점이 하나 있습니다. 바로 IIS에서 사용하는 사이트 ID 값을 통해서 정해지는 Application Path를 Machine Key와 같이 활용된다는 사실입니다. 웹 사이트 관리를 하다보면 로드 밸런싱에 참여하는 컴퓨터들을 다음과 같이 관리하게 되는 경우가 있습니다.

  • 서버 A에서는 기본 웹 사이트를 먼저 지우고 새 웹 사이트를 만들었다.
  • 서버 B에서는 새 웹 사이트를 먼저 만들고 기본 웹 사이트를 지웠다.

혹은 아래와 같은 경우도 있을 수 있습니다.

  • 서버 C에서는 사이트 A를 만들고 사이트 B를 만들었다.
  • 서버 D에서는 사이트 B를 만들고 사이트 A를 만들었다.

별 차이 없이 생각할 수 있지만, IIS에서는 이 경우 각각의 사이트들에 다른 ID 값을 부과하게 됩니다. 이 경우, 분명히 Machine Key를 동일하게 지정했음에도 불구하고 로드 밸런싱 환경에서 세션 상태가 일관성없게 변하는 문제를 만나게 됩니다. 제가 이번에 고민하게 된 부분도 바로 이 부분이었는데요, 이 문제를 해결하기 위해서는 IIS 7에서 전체 웹 사이트 목록에 나타나는 내용 중 다음의 ID 값이 멤버로 참여하는 웹 서버마다 차이가 있지 않은지 우선 검토해야 합니다.

위에있는 그림에서 빨간색으로 그린 부분이 서버 컴퓨터마다 차이가 있다면 이 값을 수정해주어야 합니다. 이 값을 수정하기 위해서는 수정할 사이트를 클릭하고, 고급 설정 링크를 아래 그림과 같이 클릭합니다.

이제 아래와 같은 팝업 대화 상자가 나타나면 강조 표시한 속성인 ID 값이 멤버로 참여하는 웹 서버 모두 같은 값을 가질 수 있도록 통일시켜줍니다.

확인 버튼을 누른 다음, ID 값이 바뀐 서버 컴퓨터에 한해서 IIS 전체를 재시작해주시거나 사이트 재시작을 시켜주시면 정상적으로 작동하게 될 것입니다.

Windows Azure 환경에서의 고려 사항

오늘 살펴본 내용은 IIS 7과 ASP.NET에 관한 부분이었지만, Windows Azure Platform의 경우에도 비슷한 문제가 있습니다. Windows Azure Platform에 VM Role로 웹 사이트를 게시를 하든, Web Role로 웹 사이트를 게시하든 세션을 사용하게 될 경우 비슷한 문제가 있을 수 있습니다.

다행히, Web Role을 이용한다면 내부적으로 사용하는 IIS에서 여러분이 몇 개의 웹 사이트를 추가적으로 구성하든 관계없이 같은 순서로 같은 ID를 사용하는 웹 사이트를 만들 것이므로 세 번째로 이야기한 ID 값 수정과 같은 작업은 할 필요가 없을 것입니다. 그러나 Machine Key에 대한 설정이나 세션 공유를 위한 설정은 SQL Azure를 이용한다거나, Worker Role에서 ASP.NET State Service 혹은 써드파티의 Session State Server를 이용해야 할 수 있습니다.

물론, 최근에 Windows Azure Platform의 일부로 Windows Azure AppFabric Cache가 새로 출시되기는 하였습니다만 상당히 이용 가격이 비싼 편입니다. (비싼만큼 확실한 성능을 제공합니다.) 로드 밸런싱 환경에서 특별한 문제를 일으키지 않는 일반적인 세션 공유가 필요하시다면 오늘 이야기한 주제를 응용한 Azure Project를 구축해보는 것도 의미가 있을 것입니다.

'Cloud' 카테고리의 다른 글

SQL Azure Data Sync (3) – 고려사항  (0) 2011.09.22
SQL Azure Data Sync (2) – Sync Group  (0) 2011.09.16
SQL Azure Data Sync (1) – 소개와 Agents 구성  (0) 2011.09.09
SQL Azure Sharding 소개  (0) 2011.09.08
SQL Azure Reporting (3)  (1) 2011.08.25

Visual Studio 2010 Service Pack 1에 대한 모든 것

Visual Studio 2010 2011. 4. 14. 17:00 Posted by 알 수 없는 사용자

안녕하세요. 오랫만에 블로그에 글을 올립니다. 지난번 Visual Studio Camp에서 옴니버스 형식의 세미나로 Visual Studio 2010 Service Pack 1에 대하여 말씀을 드렸던 세션이 있는데, 발표 자료와 더불어서 Visual Studio 2010 SP1에 대한 간략한 소개를 위하여 글을 씁니다.


Visual Studio의 새 도움말 시스템

Visual Studio 2010 RTM 버전부터는 새로운 형태의 도움말 시스템이 도입되는데, 로컬 웹 서버를 통하여 도움말 컨텐츠가 제공되는 방식으로 이전의 Visual Studio 2005와 Visual Studio 2008에서 제공되던 방식과 다르게 제공됩니다. Visual Studio 2005와 Visual Studio 2008의 경우 자체 URI Scheme을 Windows Registry에 등록하고 이를 Internet Explorer를 통하여 탐색할 수 있도록 확장하는 방식이었습니다. 그러나 새로운 도움말 컬렉션을 추가하거나 삭제하는 과정에서 시스템 성능에 따라 재배열 시간이 상당히 오래 걸리는 문제가 있어 불편한 점도 있었습니다. 이러한 방식 대신 더 단순하지만 더 유연한 방식으로 바꾸게 된 듯 합니다.

그렇지만 이전 버전에서 제공되던 색인, 검색 기능 등이 웹 사이트 형식으로 바뀌면서 이전에 사용했던 기능들이 사라져서 아쉬운 점도 있었는데 이번 Service Pack 1에서는 다시 Help Browser Software가 부활했습니다. 그래서 로컬 웹 서버로 컨텐츠를 보여주는 것은 동일하지만 Visual Studio를 통해서 컨텐츠를 탐색하면 Help Browser가 별도로 나타납니다.

그리고 이번 도움말 시스템에서의 백미는 인터넷을 통한 업데이트가 가능해졌다는 점입니다. 실제로 설치한 적이 없는 제품이라 할지라도, 그리고 DVD를 통해서만 설치할 수 있는 전체 버전의 MSDN 안에서만 제공되던 컨텐츠까지도 인터넷을 통하여 항상 최신 버전을 다운로드받아 로컬 도움말 컬렉션에 추가하거나 필요하지 않으면 삭제할 수 있습니다.



Silverlight 4에 대한 지원 추가

Visual Studio 2010 SP1을 설치하면 별도로 Silverlight 4에 대한 Tools for Visual Studio를 추가 설치할 필요가 없습니다. Silverlight 4부터는 이전의 WPF보다 작지만 웹이 아닌 데스크탑 및 오프라인 환경에서 잘 동작하는 응용프로그램을 제작할 수 있는 기능이 더 완벽하게 제공됩니다. 이러한 기술 전반은 권한 상승이 적용된 실버라이트 응용프로그램에서 가능한 것이며, 여기에는 파일 입출력이나 로컬 COM 컴포넌트와 연계하는 방안이 포함되어있습니다. 아래의 예제는 권한 상승이 적용된 Silverlight 4 기반 응용프로그램 샘플의 소스 코드이며, 사용자 프로필 디렉터리 내의 "내 그림" 폴더에 있는 이미지들을 열거하고 뷰어를 통하여 보여주는 예제입니다.



위 프로그램의 소스 코드 중 파일 입출력에 대한 소스 코드를 실제로 발췌하면 다음과 같습니다.

private void UpdateFileList()
{
    string targetPath = Environment.GetFolderPath(
        Environment.SpecialFolder.MyPictures);
 
    List<object> content = new List<object>();
    foreach (string eachFile in Directory.EnumerateFiles(targetPath))
    {
        switch (System.IO.Path.GetExtension(eachFile).ToLower())
        {
            case ".jpg":
            case ".jpeg":
            case ".png":
                break;
 
            default:
                continue;
        }
 
        content.Add(eachFile);
    }
    this.fileList.ItemsSource = content;
}
Visual Studio 2010 SP1을 설치한 후 Silverlight 프로젝트를 생성하려고 하면 다음과 같이 대화 상자가 나타나는데 이 때 Silverlight 4를 사용하도록 지정하면 사용이 가능합니다.


IIS Express 7.5에 대한 지원 추가

Visual Studio 2005부터는 Cassini Web Server라고 불리던 ASP.NET Development Server를 통하여 전체 버전의 IIS가 없어도 쉽게 ASP.NET 응용프로그램을 테스트할 수 있는 환경이 제공되었습니다. 그러나 Visual Studio 2008의 등장과 더불어 IIS 역시 대폭 업그레이드되어 Windows Server 2008부터는 완전히 새로워진 아키텍처를 기반으로 하는 IIS 7이 등장하게 됩니다. 이에 따라, 어느 정도 호환성을 보장하기는 하지만 이전의 IIS와는 많이 달라졌기 때문에 Cassini Web Server 만으로는 테스트가 어려운 점이 많았습니다. 통합 IDE의 이점도 확보하고, 전체 버전의 IIS를 사용하지 않으면서도 충분히 모든 기능을 점검해볼 수 있는 방향으로 가기 위하여 IIS Express가 등장하게 됩니다.

IIS Express를 사용하는 것은 실제 IIS를 사용하는 것과 비교했을 때 다음과 같은 장점이 있습니다.

  • ASP.NET Development Server와는 달리 FastCGI 모듈을 호스팅할 수 있으므로 PHP와 같은 FastCGI 지원 웹 언어들을 같은 환경에서 동시에 테스트할 수 있습니다.
  • 웹 프로젝트에서 IIS를 사용하도록 지정한 경우, 관리자 권한을 얻을 수 없는 다른 컴퓨터에서는 웹 프로젝트를 열 수 없는 문제점이 있었으나 IIS Express를 사용하도록 하면 이런 제약이 없습니다.
  • IIS Hosted Core를 사용하므로 전체 버전의 IIS가 없어도 상관이 없으며, IIS Express가 설치되어있지 않은 경우 Visual Studio가 자동으로 이를 감지하여 Web Platform Installer를 호출하여 IIS Express가 설치될 수 있도록 해줍니다.
  • 개별 프로세스 형태로 실행되므로 여러 사람이 사용하는 컴퓨터에서도 시스템 설정을 편집하는 일 없이 안전하게 실행할 수 있습니다.

HTML 5와 CSS 3에 대한 문법 검증 지원

Visual Studio 2010 SP1 및 Visual Web Developer 2010 Express SP1을 설치하면 HTML 5, XHTML 5 및 CSS 3에 대한 지원이 기본으로 내장되어있어 정확한 코딩이 가능합니다.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>HTML5 Test</title>
    <link type="text/css" rel="Stylesheet" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.10/themes/redmond/jquery-ui.css" /> 
    <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.js"></script>
    <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.10/jquery-ui.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $('#test').dialog({ show: "drop", hide: "drop", width: "auto", height: "auto", title: "html 5 rocks!" }).show();
        });
    </script>
</head>
<body>
    <div id="test">
        <video src="demo.mp4" width="700" height="500" id="testVideo" autoplay="autoplay">
            <strong>Your web browser does not support video element.</strong>
        </video>
    </div>
</body>
</html>



위의 그림과 같이 검사할 문법을 지정하여 프로그래밍하면 꼭 지정해야 할 프로퍼티를 검사하여 경고를 띄우거나, 프로퍼티에 포함되어야 할 값의 유형을 자동으로 유추해주어 규칙을 몰라서 잘못 코딩할 가능성을 예방해 줍니다.

그 외에 눈여겨 볼만한 것들

Visual Studio 역시 최근에 급격한 변화를 맞이하고 있습니다. 빠르게 변화하는 기술을 수용하기 위해서 Internet Explorer의 런칭 주기가 짧아진 것과 비슷하게, Visual Studio 역시 자주 새로운 형태의 도구와 프레임워크를 업데이트하고 있으며, 이러한 노력의 일환으로 Express Edition의 가치가 더 높아지고 있습니다.

대표적으로 Visual Studio LightSwitch와 Visual Web Developer Express Edition, 그리고 Visual Studio for Windows Phone 7이 그 예시입니다. 전체 버전의 Visual Studio 제품 구성을 바꾸지 않고 안전하게 테스트해볼 수 있는 방법으로서도, 그리고 실무 개발 환경에서도 유용하게 쓰일 수 있습니다.

그러나 서비스 팩 출시와 더불어서 Express Edition의 경우 한 박자 정도 업데이트가 늦어지는 편입니다. 이 때문에, 먼저 설치한 서비스 팩과 나중에 설치한 RTM 버전의 Express Edition 사이의 버전 차로 인한 충돌 문제가 이슈가 되었던적이 있는데, 이번 버전부터는 그러한 상황이 있을 경우 Visual Studio가 시작되기 전에 해당 문제점을 사용자에게 정확히 알려줍니다. 그 외에, 다양한 도구와 런타임에서 기능 및 성능 향상이 있었습니다.

Hello Windows Azure / Twitter 스타일 방명록 만들기 #2

Cloud 2010. 8. 10. 00:00 Posted by 알 수 없는 사용자

지난번 글 (2010/07/27 - [Cloud Development] - Hello Windows Azure / Twitter 스타일 방명록 만들기 #1)에 이어서 오늘 시간에는 ASP.NET MVC 2를 사용하는 Web Role 위에서 jQuery, jTemplate을 이용하여 기본적인 방명록 UI를 꾸며보고, 별 다른 Worker Role의 구현 없이 Windows Azure Table Storage를 경유하여 방명록의 글을 삽입, 삭제, 변경하는 기능을 구현해보기로 하겠습니다.

시작하기 전에 (2010.08.09 Update)

지난번 코드에서 누락되거나 교정될 필요가 있는 코드를 포함하여 업데이트를 할 부분이 있어 말씀을 전합니다. 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;

namespace TwistBook.DataModel
{
    public class TwistDataSource
    {
        private static CloudStorageAccount storageAccount;
        private TwistDataServiceContext serviceContext;

        static TwistDataSource()
        {
            // 중요: 실제로 응용프로그램을 Cloud 환경에 배포할 때에는
            // Cloud Project 내의 다른 환경 설정 문자열을 이용하도록
            // 호출을 변경해야 합니다.
            storageAccount = CloudStorageAccount.DevelopmentStorageAccount;

            CloudTableClient.CreateTablesFromModel(
                typeof(TwistDataServiceContext),
                storageAccount.TableEndpoint.AbsoluteUri,
                storageAccount.Credentials);
        }

        public TwistDataSource()
        {
            this.serviceContext = new TwistDataServiceContext(storageAccount);
            this.serviceContext.RetryPolicy = RetryPolicies.Retry(
                3, TimeSpan.FromSeconds(1));
        }

        public DataServiceResponse Insert(TwistModel model)
        {
            this.serviceContext.AddObject(
                TwistDataServiceContext.TwistModelName,
                model);

            return this.serviceContext.SaveChangesWithRetries();
        }

        public IEnumerable<TwistModel> Select()
        {
            var results = from eachTwist in this.serviceContext.TwistModel
                          select eachTwist;

            var query = new CloudTableQuery<TwistModel>(
                results as DataServiceQuery<TwistModel>,
                RetryPolicies.Retry(3, TimeSpan.FromSeconds(1)));

            return query.Execute();
        }

        public DataServiceResponse Delete(TwistModel model)
        {
            // 이 부분의 코드가 삭제되었습니다.
            this.serviceContext.DeleteObject(model);
            return this.serviceContext.SaveChanges();
        }

        public DataServiceResponse Update(TwistModel model)
       
{
           
this.serviceContext.UpdateObject(model);
           
return this.serviceContext.SaveChanges();
        }

    }
}

Web Role 완성하기

1. ASP.NET MVC 2 응용프로그램의 특성을 잘 살리기 위하여 AJAX 기술을 활용하는 방식으로 예제를 설명하고자 합니다. 이를 위하여 필요한 것이 jQuery와 jTemplate 라이브러리인데, jQuery의 경우 ASP.NET MVC 2 프로젝트를 만들면 자동으로 아래의 Scripts 디렉터리에 1.4 버전이 번들링되어있으니 별도로 받으실 필요가 없습니다.

자바스크립트 라이브러리들의 경우, 근래 들어서는 4GL 개발 도구들의 영향으로 Debug Version과 Release Version 라이브러리를 각기 개별적으로 제공하는 경우가 늘었습니다. jQuery도 이러한 추세를 잘 따르고 있으며, 위의 화면에서 jquery-1.4.1-vsdoc.js 파일은 Debug 목적 + Visual Web Developer용 Intellisense 지원을 위한 버전이고, jquery-1.4.1.js 파일은 원래의 소스 코드가 있는 그대로 (as-is) 제공되는 버전입니다. 그리고 jquery-1.4.1.min.js 파일은 원래의 소스 코드에서 주석과 공백 제거, 변수명 최소화와 같은 Obfuscation Process를 포함한 Minified Process를 거친 전송에 최적화된 버전입니다.

자바스크립트 전송에 필요한 대역폭을 좀 더 아낄 필요가 있고, 접속하는 브라우저들이 모두 G-ZIP 압축 해제 기능을 지원한다는 점을 확신할 수 있다면, WSFU (Windows Service For Unix)나 Cygwin, GNU for Win32 등을 통해서 액세스할 수 있는 GZIP 압축 유틸리티를 이용하여 Minified Version을 GZIP 파일로 한 번 더 묶어서 이를 다운로드하도록 구성하는 것도 좋은 선택이 될 수 있습니다. WSFU는 http://www.microsoft.com/downloads/details.aspx?FamilyID=896c9688-601b-44f1-81a4-02878ff11778&DisplayLang=en 에서 다운로드 가능합니다.

2. jTemplate은 jQuery를 기반으로 만들어진 플러그인으로 HTML이나 XML 컨텐츠를 지정된 지시자에 맞추어 반복 생성하거나, 내용을 치환하거나, 수식을 계산하는 등의 복잡한 연산 작업을 가능하게 합니다. 특히 JSON (Java Script Object Notation) 기반의 데이터를 내려보내어줄 것이므로 이러한 기능은 필수적입니다. jTemplate은 http://plugins.jquery.com/project/jTemplates 에서 다운로드받으실 수 있고, 압축 파일을 다운로드받으면 아래와 유사한 형태로 나타납니다.

3. jquery-jtemplates.js 파일을 선택하여 ASP.NET MVC 2 프로젝트의 Scripts 디렉터리 아래로 복사합니다. jQuery 라이브러리와 같은 위치에 배치하여 불러오기 쉽도록 만들기 위한 선택입니다.

4. Visual Studio 솔루션 탐색기에서 방금 압축 해제한 jTemplate 라이브러리의 소스 코드를 추가해야 합니다. 솔루션 탐색기에서 Web Role 프로젝트 아래의 Scripts 디렉터리를 아래 그림과 같이 클릭하고 상단 도구 모음의 "모든 파일 표시" 버튼을 클릭하면 아직 등록되지 않은 jTemplate 라이브러리의 파일이 나타납니다.

5. jquery.jtemplates.js 파일을 오른쪽 버튼으로 클릭하고 "프로젝트에 포함" 메뉴를 클릭하면 솔루션의 일부로 편입됩니다. 이 때, jquery.jtemplates.js 파일을 오른쪽 버튼으로 클릭하고 속성 메뉴를 선택하여 나타나는 속성 창에서 빌드 작업이 "내용"으로 선택되어있는지 반드시 확인하여 주세요. "내용"으로 선택되어있지 않은 파일은 실제 배포 때 제외될 수도 있습니다.

6. 이제 마스터 페이지에 jQuery와 jTemplate 라이브러리를 추가해야 합니다. 여기서 마스터 페이지란 페이지 전반에 걸쳐서 기본 바탕이 되는 ASP.NET 사이트 수준의 골격 템플릿입니다. PowerPoint의 마스터 슬라이드와 비슷한 개념으로 이해해도 됩니다. 마스터 페이지는 Views 폴더 아래의 Shared 폴더 아래의 Site.Master 파일이며 아래와 같은 위치에 나타납니다.

7. Site.Master 파일을 열어서 아래와 같이 수정합니다. 원래 내용에서 수정된 부분을 굵게 표시하였으며 자세한 내용은 각주를 참조하여 주십시오.

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="<%= Url.Content("~/Content/Site.css") %>" rel="stylesheet" type="text/css" /> [각주:1]
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery-1.4.1.min.js") %>"></script> [각주:2]
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery-jtemplates.js") %>"></script> [각주:3]

</head>

<body>
    <div class="page">

        <div id="header">
            <div id="title">
                <h1>내 MVC 응용 프로그램</h1>
            </div>
             
            <div id="logindisplay">
                <% Html.RenderPartial("LogOnUserControl"); %>
            </div>
           
            <div id="menucontainer">
           
                <ul id="menu">             
                    <li><%: Html.ActionLink("홈", "Index", "Home")%></li>
                    <li><%: Html.ActionLink("정보", "About", "Home")%></li>
                </ul>
           
            </div>
        </div>

        <div id="main">
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />

            <div id="footer">
            </div>
        </div>
    </div>
</body>
</html>

8. 웹 페이지를 위한 기본 준비는 끝났습니다. 이제 Twitter Style의 방명록을 입력받을 수 있고 보여줄 수 있는 서비스를 만들기 위하여 서비스의 중심이 되는 Controller를 구성해보도록 하겠습니다. 편의를 위하여 HomeController를 편집하도록 하겠습니다. 솔루션 탐색기에서 TwistBook.WebRole 프로젝트의 Controllers 폴더 아래의 HomeController.cs 파일을 아래 그림과 같이 선택하여 엽니다.

9. ASP.NET MVC에서 컨트롤러 내에서 Public 접근자로 노출된 각각의 Method는 이전의 ASP.NET Web Form에 비유하였을 때 개별 처리기 (ASHX 파일)에서 웹 페이지를 결정하여 내보내는 것과 같은 개념으로 최초에 사용자가 페이지에 접근할 때나, 페이지의 FORM 태그로부터 응답이 되돌아온 시점에서 모두 사용이 가능합니다. 이러한 특성을 바탕으로, HomeController는 그 자체로 API의 역할을 수행할 수 있으며 역으로 페이지를 렌더링하기 위한 컨텐츠 단위로서의 역할도 수행이 가능합니다.

HomeController.cs 파일의 내용을 아래와 같이 수정합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using TwistBook.DataModel;

namespace TwistBook.WebRole.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public HomeController()
            : base()
        {
        }

        public ActionResult Index()
        {
            ViewData["Message"] = "Windows Azure 방명록 예제";
            return View("Index"); [각주:4]
        }

        [HttpPost] [각주:5]
        public ActionResult RetrieveMessages()
        {
            var account = CloudStorageAccount.DevelopmentStorageAccount;
            var dataSource = new TwistDataSource();

            var results = from eachItem in dataSource.Select()
                          orderby eachItem.WrittenDate descending
                          select eachItem;

            return Json(results); [각주:6]
        }

        [HttpPost]
        public ActionResult AddMessage(string name, string message, string imageUrl)
        {
            var account = CloudStorageAccount.DevelopmentStorageAccount;
            var dataSource = new TwistDataSource();
            dataSource.Insert(new TwistModel()
            {
                WriterName = name,
                WrittenDate = DateTime.Now,
                MessageBody = message,
                ImageUrl = imageUrl
            }); [각주:7]

            return Index(); [각주:8]
        }

        public ActionResult UpdateMessage(string partitionKey, string rowKey) [각주:9]
        {
            var account = CloudStorageAccount.DevelopmentStorageAccount;
            var dataSource = new TwistDataSource();
            var results = from eachItem in dataSource.Select()
                          where eachItem.PartitionKey == partitionKey
                          where eachItem.RowKey == rowKey
                          select eachItem;

            ViewData["PartitionKey"] = partitionKey;
            ViewData["RowKey"] = rowKey; [각주:10]

            if (results.Count() > 0)
            {
                var result = results.First();
                ViewData["Name"] = result.WriterName;
                ViewData["Message"] = result.MessageBody; [각주:11]
            }

            return View();
        }

        [HttpPost]
        public ActionResult UpdateMessage(string partitionKey, string rowKey, string name, string message, string imageUrl) [각주:12]
        {
            var account = CloudStorageAccount.DevelopmentStorageAccount;
            var dataSource = new TwistDataSource();
            var results = from eachItem in dataSource.Select()
                          where eachItem.PartitionKey == partitionKey
                          where eachItem.RowKey == rowKey
                          select eachItem;

            if (results.Count() > 0)
            {
                var result = results.First();

                if (result != null)
                {
                    result.WriterName = name;
                    result.MessageBody = message;
                    result.WrittenDate = DateTime.Now;
                    result.ImageUrl = imageUrl;
                    dataSource.Update(result);
                    return View("PopupUpdateView"); [각주:13]
                }
                else
                    return View("PopupUpdateFailView");
            }
            else
                return View("PopupUpdateFailView"); [각주:14]
        }

        public ActionResult DeleteMessage(string partitionKey, string rowKey)
        {
            var account = CloudStorageAccount.DevelopmentStorageAccount;
            var dataSource = new TwistDataSource();
            var results = from eachItem in dataSource.Select()
                          where eachItem.PartitionKey == partitionKey
                          where eachItem.RowKey == rowKey
                          select eachItem;

            if (results.Count() > 0)
            {
                dataSource.Delete(results.First());
                return Index(); [각주:15]
            }
            else
                return Index();
        }

        public ActionResult About()
        {
            return View();
        }
    }
}

10. 방명록의 기본 기능을 만들기 위하여 이제 Views 폴더 아래의 Home 폴더 아래의 Index.aspx 파일을 열어서 편집해야 합니다. 아래 그림과 같은 위치에 존재합니다.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    홈 페이지
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <script type="text/javascript">
        $(document).ready(function () {
            $.ajax({
                type: 'POST', [각주:16]
                url: '<%= Url.Action("RetrieveMessages") %>', [각주:17]
                data: '{}',
                contentType: 'application/json; charset=utf-8',
                dataType: 'json', [각주:18]
                success: function (data) {
                    var targetDiv = $('#guestbookList');  [각주:19]
                    targetDiv.setTemplate($('#templateContent').html()); [각주:20]
                    targetDiv.processTemplate(data); [각주:21]
                }
            });
        });
    </script>
   
    <script type="text/html" id="templateContent">
    {#foreach $T as record}
    <div style="padding-bottom: 5px;">
        <img src="{$T.record.ImageUrl}" alt="" style="float: left; width: 100px;" />
        <div style="float: left; margin: 5px 5px 5px 5px;">
            <h3>RT @{$T.record.WriterName} {$T.record.MessageBody}</h3>
            <pre>{$T.record.WrittenDate} via cloud</pre>
            <a href="#" onclick="window.open('<%= Url.Content("~/Home/UpdateMessage") %>?partitionKey={$T.record.PartitionKey}&rowKey={$T.record.RowKey}', 'editWindow', 'location=1,status=1,scrollbars=1,width=300,height=200');">편집</a>
            &nbsp;|&nbsp;
            <a href="<%= Url.Content("~/Home/DeleteMessage") %>?partitionKey={$T.record.PartitionKey}&rowKey={$T.record.RowKey}" target="_self">삭제</a>
        </div>
        <div style="clear: both;"></div>
    </div>
    {#/for}
    </script> [각주:22]

    <h2><%= ViewData["Message"] %></h2>
    <div>
        <div>
            <% using (var form = Html.BeginForm("AddMessage", "Home", FormMethod.Post))
               { %> [각주:23]

               <%: Html.Label("이름") %>
               <%: Html.TextBox("name", "What is your name?") %> [각주:24]
               <br />

               <%: Html.TextArea("message", "Type your message here.", 3, 100, null) %>
               <br /> [각주:25]

               <input type="submit" value="보내기" />&nbsp;<input type="reset" value="초기화" /> [각주:26]
               <br />
            <% } %>
        </div>
        <br /><br />
        <div id="guestbookList"></div> [각주:27]
    </div>
</asp:Content>

11. 방명록 내용을 편집하기 위한 팝업 창을 위한 뷰와, 댓글 편집이 끝난 뒤 취할 동작을 프로그래밍한 자바스크립트 코드를 위한 뷰는 Partial View로 디자인해야 합니다. 이 중에서 우선 방명록 항목 편집을 위한 Partial View를 추가하기 위해, 솔루션 탐색기에서 Views 디렉터리 아래의 Home 디렉터리를 오른쪽 버튼으로 클릭하고, View 추가 메뉴를 아래 그림과 같이 선택합니다.

12. View의 이름은 UpdateMessage로 지정하고, Partial View에 체크하여 아래 대화 상자와 같이 옵션을 구성한 후 확인 버튼을 클릭합니다.

13. UpdateMessage.ascx 파일의 내용을 다음과 같이 작성합니다.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
    <% using (var form = Html.BeginForm("UpdateMessage", "Home", FormMethod.Post))
       { %> [각주:28]

       <%: Html.Hidden("partitionKey", ViewData["PartitionKey"]) %>
       <%: Html.Hidden("rowKey", ViewData["RowKey"]) %> [각주:29]

       <%: Html.Label("이름") %> 
       <%: Html.TextBox("name", (string)ViewData["Name"]) %> [각주:30]
       <br />

       <%: Html.TextArea("message", (string)ViewData["Message"], 3, 100, null) %>[각주:31]
       <br />

       <input type="submit" />&nbsp;<input type="reset" /> [각주:32]
       <br />
    <% } %>
</div>

14. 이어서 솔루션 탐색기에서 Views 디렉터리 아래의 Home 디렉터리를 오른쪽 버튼으로 클릭하고, View 추가 메뉴를 11단계에서와 같이 선택합니다.

15. View의 이름은 PopupUpdateView로 지정하고, Partial View에 체크하여 아래 대화 상자와 같이 옵션을 구성한 후 확인 버튼을 클릭합니다.

16. PopupUpdateView.ascx 파일의 내용을 다음과 같이 작성합니다.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<script type="text/javascript">
    try {
        window.close(); [각주:33]
        if (window.opener && !window.opener.closed) {
            window.opener.location.href = '<%= Url.Content("~/Home/Index") %>'; [각주:34]
        }
    } catch (ex) {
    }
</script>

17. 이어서 솔루션 탐색기에서 Views 디렉터리 아래의 Home 디렉터리를 오른쪽 버튼으로 클릭하고, View 추가 메뉴를 11단계에서와 같이 선택합니다.

18. View의 이름은 PopupUpdateFailView로 지정하고, Partial View에 체크하여 아래 대화 상자와 같이 옵션을 구성한 후 확인 버튼을 클릭합니다.

19. PopupUpdateFailView.ascx 파일의 내용을 다음과 같이 작성합니다.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<h3>업데이트에 실패하였습니다.</h3>
<a href="#" onclick="window.close()">창 닫기</a>

20. 기본적인 방명록 글 남기기와 조회 기능이 올바르게 작동하는지 확인하기 위하여 시뮬레이터를 디버그 모드로 시작해야 합니다. 일반적인 응용프로그램 개발 때와 마찬가지로 F5키를 눌러서 디버그 모드로 시뮬레이터에 패키지를 배포하고 디버거를 연결할 수 있습니다. 이 때, 아래 그림과 같은 오류 메시지가 나타나면 관리자 권한이 아닌 상태에서 Visual Studio를 시작한 것이므로 Visual Studio를 종료한 뒤 "개발 도구 시작하기 및 프로젝트 생성하기" Chapter의 1단계를 참고하여 관리자 모드로 Visual Studio를 다시 시작해야 합니다.

21. 아래의 그림들에서처럼 기능들이 정상적으로 진행된다면 우선 이번 시간에 진행할 기본 기능들에 대한 소개와 작업이 끝난 것입니다.

이번 Article을 작성하면서 발견한 Windows Azure SDK 1.2에 대한 문제 한 가지

좀 더 완성에 가까워질수록 해결될 문제들 중에 한 가지가 될 예정이긴 하겠습니다만 실습하는 도중 불편함이 예상되어 제가 발견한 문제를 블로그 아티클을 통하여 미리 공유하고자 합니다. 간혹 Windows Azure Local Storage의 Table Storage에 아래와 같이 MBCS (Multi-Byte Character Set) 문자가 포함된 데이터를 삽입하려고 할 때 별 다른 까닭없이 HTTP/404 오류가 나타나는 경우가 있습니다.

사용자 코드에서 System.Data.Services.Client.DataServiceRequestException이(가) 처리되지 않았습니다.
  Message=이 요청을 처리하는 동안 오류가 발생했습니다.
  Source=Microsoft.WindowsAzure.StorageClient
  StackTrace:
       위치: Microsoft.WindowsAzure.StorageClient.Tasks.Task`1.get_Result()
       위치: Microsoft.WindowsAzure.StorageClient.Tasks.Task`1.ExecuteAndWait()
       위치: Microsoft.WindowsAzure.StorageClient.TaskImplHelper.ExecuteImplWithRetry[T](Func`2 impl, RetryPolicy policy)
       위치: Microsoft.WindowsAzure.StorageClient.TableServiceContext.SaveChangesWithRetries(SaveChangesOptions options)
       위치: Microsoft.WindowsAzure.StorageClient.TableServiceContext.SaveChangesWithRetries()
       위치: TwistBook.DataModel.TwistDataSource.Insert(TwistModel model) 파일 d:\users\남정현\documents\visual studio 2010\Projects\TwistBook\TwistBook.DataModel\TwistDataSource.cs:줄 42
       위치: TwistBook.WebRole.Controllers.HomeController.AddMessage(String name, String message, String imageUrl) 파일 d:\users\남정현\documents\visual studio 2010\Projects\TwistBook\TwistBook.WebRole\Controllers\HomeController.cs:줄 44
       위치: lambda_method(Closure , ControllerBase , Object[] )
       위치: System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
       위치: System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
       위치: System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
       위치: System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassd.<InvokeActionMethodWithFilters>b__a()
       위치: System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
  InnerException: System.Data.Services.Client.DataServiceClientException
       Message=<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code>InvalidInput</code>
  <message xml:lang="ko-KR">One of the request inputs is not valid.</message>
</error>
       Source=System.Data.Services.Client
       StatusCode=400
       StackTrace:
            위치: System.Data.Services.Client.DataServiceContext.SaveResult.<HandleBatchResponse>d__1e.MoveNext()
       InnerException:



실제 Windows Azure 실행 환경에서는 이러한 현상이 나타나지 않는 것으로 보입니다. 추후, 이러한 문제점을 해결할 수 있는 방안이 발견되면 별도의 업데이트 소식을 통하여 정보가 전달될 수 있도록 하겠습니다. 예제를 기반으로 테스트 패브릭 위에서 테스트하시는 동안에는 Table Storage에 한글, 히라가나, 카타카나, 번체, 간체, 한자 등의 데이터가 들어가지 않는 범위에서 테스트가 필요할 것 같습니다.

다음 시간에는

다음 시간에는 각 Role이 어떤 방법으로 Windows Azure 환경에서 실행되는지, Web Role과 Worker Role이 Cloud Computing 환경에서 상호 작용하고 통신하는 방법을 본격적으로 소개하고, 오늘 만든 Web Role을 어떤 방식으로 수정하게 될 것이고, Worker Role이 어떤 방식으로 데이터를 교환하게 될 것인지를 보여드릴 예정입니다. 그리고 이번 시간에 언급하지 않은 BLOB Storage에 이미지를 저장하고 가져오는 방법에 대해서도 소개하겠습니다. :-)

더운 여름 날씨에 건강 유의하시고, 활기찬 여름 보내시기 바랍니다. 감사합니다.

ps. Windows Azure Cafe (http://cafe.naver.com/wazure) 에서 2010년 8월 14일부터 본격적으로 Offline Study를 진행합니다. Windows Azure Platform의 학습에 관심있으신 개발자 여러분들의 많은 관심과 참여 부탁드리며, 아울러 Visual Studio 2010 공식 팀 블로그에서 Cloud Computing 관련 Article을 집필하실 열정적인 Blogger 여러분도 함께 모시고 있습니다. 이에 관련된 모든 상세한 내용은 Windows Azure Cafe를 통하여 저에게 연락 주시면 상세히 안내해드리겠습니다. 감사합니다. :-)

  1. 기본으로 제공되는 템플릿 코드로부터 수정한 부분으로, 마스터 페이지는 처리 과정 도중에 해석되는 파일이지만 브라우저의 입장에서 서비스를 하는 페이지가 아니기 때문에, 기본으로 제공되는 경로인 ../../Content/Stie.css는 잘못 해석될 가능성이 있습니다. 이를 예방하기 위하여 Inline Expression을 사용하여 Url.Content 메서드로 정확한 경로를 다시 가져오도록 만든 것입니다. [본문으로]
  2. 기본으로 제공되는 템플릿 코드로부터 수정한 부분으로, 마스터 페이지는 처리 과정 도중에 해석되는 파일이지만 브라우저의 입장에서 서비스를 하는 페이지가 아니기 때문에, 기본으로 제공되는 경로인 ../../Content/jquery-1.4.1.min.js는 잘못 해석될 가능성이 있습니다. 이를 예방하기 위하여 Inline Expression을 사용하여 Url.Content 메서드로 정확한 경로를 다시 가져오도록 만든 것입니다. [본문으로]
  3. 이번 시간에 JSON 기반의 데이터를 표현하기 위하여 사용할 jTemplate 라이브러리를 여기에서 지정합니다. 앞의 URL들과 마찬가지의 방법을 사용하여 정확하게 경로가 참조될 수 있도록 만들어줍니다. [본문으로]
  4. 기본 Index 메서드에서는 return View(); 로 호출하였지만, 다른 Controller Method에서 이 메서드를 호출하게 되는 경우, Index View가 아닌 Controller 그 자신의 View를 찾도록 기본 설계가 구성되어있기 때문에, 이를 방지하고 재 사용하기 쉬운 형태로 만들기 위해 특별히 "Index"라는 뷰 이름을 찾도록 명시한 것입니다. [본문으로]
  5. POST 요청에 의해서만 메시지가 JSON 형식으로 내려가도록 구성하기 위한 것으로, 필요에 따라이 Attribute를 누락하고, 아래의 Json 메서드 호출에서 AllowGet 인자를 지정하면 GET 요청에 의해서도 조회 결과가 JSON으로 반환될 수 있습니다. [본문으로]
  6. ActionResult 클래스를 상위 클래스로 두는 JSON Serialization Result 객체를 반환합니다. [본문으로]
  7. 데이터 삽입 직후 Commit 연산까지 한번에 처리하도록 설계된 메서드를 부르는 것입니다. [본문으로]
  8. 중요: 이 함수의 결과로 나타나는 View가 AddMessage가 아니라 Index입니다. [본문으로]
  9. 동일한 메서드에 대한 오버로드이지만, GET 방식으로 호출될 수 있고, 인자를 2개를 받도록 구성되어있으므로 이 버전의 메서드에서는 View를 렌더링하는데 사용됩니다. [본문으로]
  10. ViewData 컬렉션에 Update 동작을 구현하기 위해 필요한 정보를 다시 전달합니다. 나중에 View에서 이 정보를 참조하여 페이지를 렌더링하게 됩니다. [본문으로]
  11. 조회된 결과를 페이지 렌더링을 위하여 ViewData 컬렉션에 보관합니다. [본문으로]
  12. 동일한 버전의 UpdateMessage 메서드에 대한 오버로드이지만, POST 요청에만 동작하도록 설계된 버전의 Controller Method입니다. [본문으로]
  13. 편집을 마친 후, 미리 구성된 PopupUpdateView를 찾아 이동합니다. 이 뷰는 팝업창 형태로 열린 편집 창을 닫고, 팝업 창의 부모 (window.opener)를 새로 고침하도록 디자인된 뷰입니다. [본문으로]
  14. 업데이트에 실패할 경우 보여줄 View를 지정합니다. [본문으로]
  15. 데이터 삭제 후 Index 뷰를 다시 로드하도록 만들었습니다. [본문으로]
  16. XmlHttpRequest 객체를 이용하여 전송할 때 POST 방식으로 요청하는 것을 명시하고 있습니다. [본문으로]
  17. RetrieveMessages Controller Method를 정확히 찾을 수 있도록 전체 경로를 반환하는 함수를 사용하여 스크립트 위에 렌더링합니다. [본문으로]
  18. JSON 방식의 결과 집합이 필요함을 명시하고, JSON 방식으로 데이터를 받아들이도록 구성하고 있습니다. [본문으로]
  19. jTemplate 엔진으로 치환된 내용을 렌더링할 대상 div element를 찾습니다. [본문으로]
  20. 기준이 되는 템플릿 컨텐츠를 로드합니다. 이스케이프 문자로 복잡하게 처리하지 않고 편리하게 다룰 수 있도록 만들기 위하여, JavaScript나 VBScript로 해석되지 않도록 처리한 별도의 SCRIPT element로부터 로드하도록 구성하였습니다. [본문으로]
  21. jTemplate 엔진을 이용하여 주어진 데이터를 통해 렌더링을 시작합니다. [본문으로]
  22. 렌더링에 필요한 템플릿 코드가 이곳에 기술됩니다. 이 부분은 스크립트 태그 안에 있지만 스크립트 해석기에 의하여 처리되지는 않으며, 또한 시각적으로 드러나지도 않습니다. (as-is string으로 해석됩니다.) [본문으로]
  23. 메시지를 추가하기 위한 form 데이터를 구성하고 있습니다. [본문으로]
  24. Controller Method의 name 매개 변수와 이름을 같게 지정합니다. [본문으로]
  25. Controller Method의 message 매개 변수와 이름을 같게 지정합니다. [본문으로]
  26. 전송 버튼과 초기화 버튼이 trigger 역할을 하여 데이터를 전송하거나 리셋하는 역할을 합니다. [본문으로]
  27. 방명록 목록은 이 요소 아래에 rendering 될 것입니다. [본문으로]
  28. UpdateMessage의 POST 전송 대상을 찾아 업데이트 작업을 수행하도록 만듭니다. [본문으로]
  29. 링크에 의하여 GET 방식으로 전달된 매개 변수를 다시 렌더링하여 재사용합니다. [본문으로]
  30. ViewData에 저장된 기존 데이터를 꺼내옵니다. [본문으로]
  31. ViewData에 저장된 기존 데이터를 꺼내옵니다. [본문으로]
  32. 전송 버튼과 초기화 버튼이 trigger 역할을 하여 데이터를 전송하거나 리셋하는 역할을 합니다. [본문으로]
  33. 팝업 창을 닫습니다. [본문으로]
  34. 팝업 부모 창이 유효하다면, 정확한 Index View의 URL을 찾아 다시 로드하도록 만듭니다. [본문으로]

Hello Windows Azure / Windows Azure 개발 환경의 구축

Cloud 2010. 6. 3. 09:00 Posted by 알 수 없는 사용자

지난번 글 (http://vsts2010.net/303)에 이어서 오늘부터는 Windows Azure 기반 응용프로그램을 개발하기 위한 본격적인 실습 위주의 글을 시리즈로 연재하게 되었습니다. Windows Azure 기반의 응용프로그램을 개발하는 방법은 여러 가지가 있을 수 있습니다만 Visual Studio 2010을 활용하여 Windows Azure 기반의 응용프로그램을 개발하는 것에 관한 내용을 중심으로 블로그를 연재할 예정임을 미리 밝혀둡니다.

Windows Azure 기반 응용프로그램 개발을 위한 필요 사양 요약

  • 필요한 운영 체제: Windows Vista, Windows 7, Windows Server 2008, Windows Server 2008 R2를 지원합니다. Windows 2000, Windows XP에서는 개발하실 수 없습니다.
  • 필요한 구성 요소: IIS 7.x (ASP.NET, WCF HTTP 활성화, 정적 컨텐츠, CGI 기능이 필요합니다.)
  • 필요한 개발 도구: Visual Studio 2008 SP1, Visual Web Developer 2008 Express SP1, Visual Studio 2010, Visual Web Developer 2010 Express 중 하나가 필요합니다. 이 글에서는 Visual Web Developer Express 2010을 택하여 설명을 드리겠습니다.
  • 필요한 데이터베이스: SQL Server 2005 Express 이상의 데이터베이스
  • 주의 사항: Visual Studio, Visual Web Developer의 모든 CTP, RC, Beta 버전 및 .NET Framework의 CTP, RC, Beta 버전을 설치 전에 완전히 제거하여 주십시오.
  • 팁: 배포 후 호환성 문제를 최소화하기 위하여, Windows Azure 기반 응용프로그램을 개발할 때에는 64비트 버전의 Windows Vista, 7, Server 2008, Server 2008 R2를 이용하여 개발하시는 것을 권장합니다. 64비트 버전의 Windows를 가지고 있지 않은 경우 32비트 버전의 개발 환경을 이용하더라도 문제는 없습니다.

Visual Web Developer Express 2010으로 시작하는 Windows Azure 개발 환경 구축

Visual Web Developer Express 2010은 학생, 프로그래밍 입문자, 아마추어 개발자 등을 위하여 제공되는 Visual Studio의 무료 버전입니다. 그러나, 상용 목적으로 소프트웨어 개발을 하는 동안에도 자유롭게 제한없이 이용하실 수 있으며, 전체 버전의 Visual Studio 2010을 구입하기 이전에 Visual Studio 2010이나 최신 기술을 빠르게 테스트해보기 위한 목적으로도 이용이 가능합니다. 이번 글에서는 Visual Web Developer Express 2010을 이용하여 개발 환경을 구축하는 과정을 보여드리도록 하겠습니다.

Windows Vista / 7에서 기본 환경 설정하기

1. 시작 메뉴를 클릭하고 프로그램 및 파일 검색란에 아래와 같이 appwiz.cpl을 입력한 후 Enter 키를 누릅니다. Windows Vista를 사용 중이시고, 고전 메뉴를 사용 중이신 경우 키보드의 Windows 키와 R 키를 동시에 눌러 나타나는 실행 대화 상자에서 같은 방법으로 실행할 수 있습니다.

Windows 7에서 실행

Windows Vista에서 실행

2. 아래의 화면에서, Windows 기능 사용/사용 안함 항목을 클릭합니다. 사용자 계정 컨트롤 설정에 따라 최소 한 번 이상 관리자 권한이 필요함을 승인해야 하는 대화 상자가 나타날 수 있습니다.

3. Windows 기능 대화 상자에서, Microsoft .NET Framework 3.5.1 항목 아래의 Windows Communication Foundation HTTP Activation 항목을 체크합니다. 그리고, 아래의 이어지는 그림에서와 같이, 인터넷 정보 서비스 항목 아래의 응용프로그램 개발 기능 아래의 "ASP.NET"과 "CGI", 일반적인 HTTP 기능의 "정적 콘텐츠" 항목을 체크한 후, 확인 버튼을 클릭하여 설정을 적용합니다.

Windows Server 2008 / 2008 R2에서 기본 환경 설정하기

1. 시작 메뉴를 클릭하고 프로그램 및 파일 검색란에 아래와 같이 appwiz.cpl을 입력한 후 Enter 키를 누릅니다. Windows Vista를 사용 중이시고, 고전 메뉴를 사용 중이신 경우 키보드의 Windows 키와 R 키를 동시에 눌러 나타나는 실행 대화 상자에서 같은 방법으로 실행할 수 있습니다.

2. 아래의 화면에서, Windows 기능 사용/사용 안함 항목을 클릭합니다. 이 기능을 이용하여 설정을 변경하기 위해서는 관리자 계정 - 또는 - 대행 계정으로 시스템에 접속한 상태여야 합니다.

3. 서버 관리자 콘솔이 나타나면, 오른쪽 트리뷰에서 기능 항목을 오른쪽 버튼으로 클릭합니다. 그 후 나타나는 팝업 메뉴에서 "기능 추가" 메뉴를 클릭합니다.

4. 기능 추가 마법사에서 .NET Framework 3.5.1 기능 항목 아래의 WCF 활성화 항목의 HTTP 활성화를 체크합니다. 이미 설치되어있는 경우, 체크된 상태로 비활성화된 항목이 보입니다. 다음 버튼을 클릭하여 진행하거나, 이미 설치되어있는 경우 취소 버튼을 클릭하여 마법사를 종료할 수 있습니다. 시스템을 다시 시작해야 할 경우, 다시 시작한 다음 5단계로 이동합니다.

5. 이제 웹 서버로서의 역할을 추가하기 위하여 역할 항목을 오른쪽 버튼으로 클릭하고, 역할 추가 메뉴를 클릭하여 역할 추가 마법사를 시작합니다. 시스템을 다시 시작하도록 권고 받았지만 재시작하지 않았을 경우 재시작을 안내하는 대화 상자가 나타날 수 있습니다.

6. 이어지는 두 화면에서처럼 웹 서버에 대한 역할이 구성되어있는지 확인 후, 웹 서버 역할 항목이 설치되어있지 않은 경우 체크하여 설치를 진행합니다. 이미 설정되어있는 경우 비활성화된 상태로 나타나므로 이 경우에는 마법사를 그냥 종료하면 됩니다.

Visual Web Developer 2010 다운로드 및 설치

Visual Web Developer 2010은 Windows Azure 기반 응용프로그램 개발을 위하여 사용할 수 있는 무료 개발 도구입니다. Visual Web Developer 2008의 경우 Service Pack 1 이상을 포함한 버전을 다운로드하여 사용할 수 있습니다. 더불어서, Silverlight Tools 역시 Visual Web Developer를 기반으로 맞추어진 개발 도구이기 때문에 같이 설치하여 사용하면 Microsoft의 최신 웹 기술의 혜택도 같이 얻을 수 있습니다.

Visual Web Developer 2010 Express를 다운로드하려면 http://www.microsoft.com/web 에서 다운로드 가능한 Web Platform Installer를 설치해야 합니다. 이 프로그램을 설치하고 난 후에 시작 메뉴에 아래 그림과 같이 바로 가기 메뉴가 생성되며, 언제든 이 메뉴를 이용하여 최신 웹 제품이나 널리 사용되는 웹 어플리케이션을 다운로드할 수 있습니다.

Web Platform Installer를 시작하면 아래와 같은 화면이 나타납니다. 여기서, 웹 플랫폼 탭을 선택하고, 도구 섹션으로 이동하여 사용자 지정 링크를 클릭합니다.

아래와 같이 개발자 도구 그룹이 나타나면 여기서 Visual Web Developer 2010 Express 체크 박스를 선택합니다. 필요한 경우, WCF RIA Services Toolkit과 Silverlight Toolkit 등을 같이 설치하여 Windows Azure Web Role 개발 때 같이 적용할 수 있습니다.

설치 버튼을 누르면 이후의 모든 설치 과정을 Web Platform Installer가 대행하여 처리하고 그 결과를 보여줍니다. 설치 과정 중에는 관리자 권한이 필요하며, 사용자 계정 컨트롤이 설정된 시스템에서는 한 번 이상 관리자 권한 요청에 대한 승인이 필요할 수 있습니다.

개발자를 위한 설치: Windows Azure Tools for Visual Studio 설치하기

Windows Azure SDK와 함께 Visual Studio에 연동하기 위한 환경을 구축하려면, Windows Azure Tools for Visual Studio를 선택하여 설치하면 됩니다. 이 설치 패키지 안에 Windows Azure 환경을 Emulation하기 위한 SDK까지 모두 포함되어있으므로 간단히 설치를 완료할 수 있습니다. 이 글을 작성하는 현 시점에서 2010년 2월 버전이 가장 최신의 SDK이며, http://www.microsoft.com/downloads/details.aspx?FamilyID=5664019e-6860-4c33-9843-4eb40b297ab6&displaylang=en 페이지에서 다운로드 가능합니다.

만약 단순히 만들어진 Windows Azure Package를 Local Machine에서 테스트하기 위하여 환경을 구축하고자 하는 경우에는 아래의 "테스트 환경을 위한 설치: Windows Auzre SDK 설치하기" 절을 참조하여 주십시오. 64비트 버전의 Windows Server 2008 환경에서 Cloud Application을 미리 테스트하는 시나리오에서 유용할 수 있습니다.

테스트 환경을 위한 설치: Windows Azure SDK 설치하기

Windows Azure 환경에서는 Windows Server 2008 R2 x64를 기본 운영 체제로 사용합니다. 만약 상호운용성 등의 복잡한 요구 사항을 필요로 하는 클라우드 기반 응용프로그램의 경우, 이 운영 체제와 동일한 환경에서 Windows Azure SDK만을 설치하여 Windows Azure Tools로 제작된 배포용 패키지 파일을 실제로 Windows Azure Cloud Environment에 게시하기 전에 올려보는 것은 문제를 사전에 진단하고, 디버깅을 하는데에 무척 유리한 전략이 될 수 있습니다.

http://www.microsoft.com/downloads/details.aspx?FamilyID=dba6a576-468d-4ef6-877e-b14e3c865d3a&displaylang=en 페이지에서 이 글을 작성하는 현 시점의 최신 버전인 2010년 2월 버전의 SDK를 설치할 수 있습니다.

Windows Azure SDK 설치 후 필요한 추가 구성 확인하기

Windows Azure 환경을 위하여 최적화된 운영 체제와 플랫폼을 이용하기 때문에, RTM 버전으로 출시된 운영 체제와 환경 설정과는 구분되는 점이 몇 가지 있습니다. 아래는 Windows Azure 환경에 특화하기 위하여 변경된 설정을 로컬 / 개발자 컴퓨터에서 재현하기 위한 패치들로 원활한 개발과 테스트를 위해서는 반드시 설치해야 하는 패치들입니다.

다음 시간에는

다음 글에서는 본격적으로 Windows Azure Tools for Visual Studio를 이용하여 프로젝트를 만들고, 샘플 응용프로그램을 만들고 테스트하는 과정을 실습해보도록 하겠습니다. 궁금하신 점이나 어려운 점은 rkttu nospam rkttu dot com - 또는 - twitter at rkttu 에서 이야기하실 수 있습니다.

감사합니다. 즐거운 하루 되십시오. :-)