Search

'ASP.NET'에 해당되는 글 25건

  1. 2011.09.13 ASP.NET과 IIS 7을 위한 로드 밸런싱 전략과 기초적인 이야기, 그리고 Azure Platform 6
  2. 2011.06.17 [Visual Studio 2010 SP1] HTML5, CSS3 지원 1
  3. 2011.06.16 [Visual Studio 2010 SP1] ASP.NET DEPLOYABLE DEPENDENCIES
  4. 2011.06.15 [Visual Studio 2010 SP1] Razor 지원 및 WEB PLATFORM INSTALLER 통합
  5. 2011.06.14 [Visual Studio 2010 SP1] IIS EXPRESS 기능 추가
  6. 2011.01.10 2011년 .NET 개발자의 생존전략 1
  7. 2010.08.16 M, V 그리고 C의 각방생활(12) - 테스팅 그거, 아무나 하나? 1
  8. 2010.08.11 M, V 그리고 C의 각방생활(11) - jqGrid로 데이터 추가,편집,삭제해보기 28
  9. 2010.08.10 Hello Windows Azure / Twitter 스타일 방명록 만들기 #2 1
  10. 2010.07.27 Hello Windows Azure / Twitter 스타일 방명록 만들기 #1
  11. 2010.07.15 Razor in WebMatrix 1
  12. 2010.07.15 M, V 그리고 C의 각방생활(10) - jqGrid를 이용한 paging과 sorting 2
  13. 2010.07.07 ASP.NET 의 WebMatrix & Razor 신 기술 소개
  14. 2010.07.01 M, V 그리고 C의 각방생활(8) - jQuery와 탭메뉴 그리고 파샬뷰
  15. 2010.06.30 M, V 그리고 C의 각방생활(7) - 함께 즐겨요~ jQuery 4
  16. 2010.06.27 M, V 그리고 C의 각방생활(6) - 유효성 검사(2) 5
  17. 2010.05.31 M, V 그리고 C의 각방생활(4) - 유효성 검사 8
  18. 2010.04.01 M, V 그리고 C의 각방생활(3) - 초간단 사이트 만들기(1) 7
  19. 2010.01.14 M, V 그리고 C의 각방생활(2) - ASP.NET MVC와 인사나누기 7
  20. 2009.12.24 M, V 그리고 C의 각방생활(1) - ASP.NET MVC vs ASP.NET WEB FORM 12

요즈음 클라우드 서비스들을 이용하다보면, 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 SP1] HTML5, CSS3 지원

Visual Studio 2010 2011. 6. 17. 08:00 Posted by POWERUMC

처음 Visual Studio 2010 릴리즈 되었을 때는 HTML5 기능이 추가가 되지 않았습니다. 그래서 XML Schema 를 이용하는 방법으로 HTML 텍스트 에디터에서 HTML5 구문을 사용하기도 하였습니다. 하지만 이번 Visual Studio 2010 SP1에는 정식으로 HTML5 인텔리센스와 유효성을 검사할 수 있는 기능이 추가가 되었습니다.

이 기능을 활성화하기 위해서 도구->옵션의 텍스트 에디터->HTML->유효성에서 HTML5 유효성 검사를 지정할 수 있습니다.

HTML5가 지원하는 여러 구문을 인텔리센스에서 자연스럽게 보여줍니다.

더불어 CSS3 를 완벽하게 지원하지는 않지만, 일부분 CSS3를 지원해 줍니다. CSS3 기능은 앞으로 그 기능을 보강할 수 있는 확장 기능으로 Visual Studio Gallery 에서 배포가 되길 기대해봅니다.


배포 가능한 종속성(Deployable Dependencies) 는 이번 Visual Studio 2010 SP1 에서 새롭게 추가된 기능입니다. 웹 응용 프로그램을 서버로 배포하기 위해서는 필수 구성 요소들이 설치가 되어 있어야 하는데, 배포 가능한 종속성 기능을 이용하면 웹 응용 프로그램이 동작에 필요한 일부 컴포넌트를 바로 배포할 수 있도록 도와줍니다.

웹 응용 프로그램에서 마우스 오른쪽 버튼을 클릭하여 컨텍스트 메뉴를 활성화하면 다음과 같은 메뉴 항목이 추가가 되어 있습니다.

메뉴 항목을 선택하면 아래와 같은 창이 나타납니다. 이 창에서는 ASP.NET MVC3 에서 사용하는 Razor 컴포넌트와 SQL Server Compact 를 선택할 수 있습니다.

위와 같이 배포 시 포함할 종속된 어셈블리/컴포넌트를 선택하여 확인 버튼을 클릭하면, 다음과 같이 웹 응용 프로그램 프로젝트에 _bin_deployableAssemblies 폴더가 생성이 되고, 이 하위에 관련된 어셈블리가 추가가 됩니다.

웹 응용 프로그램을 게시를 하게 되면, 위의 _bin_deployableAssemblies 폴더의 어셈블리는 웹 응용 프로그램의 bin 폴더로 배포가 됩니다.

물론, 웹 배포 패키지로 .ZIP 파일로 생성을 하여도 종속성을 추가한 어셈블리는 BIN 폴더에 추가가 되며, 이 패키지를 이용하여 배포할 서버에 컴포넌트의 설치 없이 바로 배포할 수 있습니다.

다만 현재는 여러 가지 배포 어셈블리/컴포넌트를 지원하지 않고 아래의 3개지의 컴포넌트만 배포를 지원해 줍니다.

  • ASP.NET Web Pages / Razor
  • SQL Server Compact 4.0
  • ASP.NET MVC 3

Razor 지원

ASP.NET MVC3 가 릴리즈 되면서 이에 발맞추어 Visual Studio 2010 SP1이 개발 도구에서 ASP.NET MVC3를 지원합니다. 특히 ASP.NET MVC3 Beta 버전에서는 지원하지 않았던, ASP.NET MVC3의 중요한 기능 중의 하나인 Razor View Engine인데, Razor View Engine의 Syntax 및 Intellisence 도 함께 지원합니다.

새로운 프로젝트를 만들 때 ASP.NET MVC3 프로젝트 템플릿에서 Razor 뷰를 선택하면 Razor View Engine을 사용할 수 있습니다.

더불어 일반 ASP.NET MVC3의 ASPX 페이지 또한 새로운 뷰를 추가할 때 Razor 뷰로 추가하면, 기존 ASP.NET MVC3의 Razor Engine을 그대로 사용할 수 있습니다.

Web Platform Installer 통합

Visual Studio 2010과 Web Platform Installer(WPI) 가 통합이 되었습니다. Visual Studio 2010에서 WPI를 바로 실행할 수 있는 툴바가 추가 되었습니다.

WPI를 통해 아래의 최신 제품을 다운로드 받을 수 있습니다.

  • SQL Server Compact 4.0
  • IIS 7.5 express
  • ASP.NET MVC3
  • WebMatrix
  • 기타…


기본적으로 웹 응용 프로그램을 개발할 경우 로컬에서 동작하는 ASP.NET Development Server 가 활성화가 됩니다.

그림 1 로컬 ASP.NET Development Server 가 동작하는 화면

웹을 개발할 때 Visual Studio가 제공하는 로컬에서 동작하는 ASP.NET Development Server 로 충분히 어려움 없이 개발을 할 수 있으나 웹 개발의 여러 가지 상황을 고려해 보면 기능이 충분하지는 않았습니다.

예를 들면, 기존의 로컬에서 동작하는 ASP.NET Development Server는 특정 웹 페이지나 XML 웹 서비스, WCF 서비스가 SSL(Secure Sockets Layer)로 동작한다거나 WCF의 NET.TCP, NET.PIPE 등의 바인딩을 사용할 수 없었습니다.

이런 여러 가지 기능적으로 IIS Express 를 사용할 경우 얻을 수 있는 이점이 많고, 기존 웹 응용 프로그램을 IIS Express에서 동작하도록 변경하기 위한 절차 또한 매우 간단합니다.

IIS Express가 설치되어 있다면, 웹 응용 프로그램에서 마우스 오른쪽 버튼을 클릭하여 IIS Express 사용을 선택하면 즉시 IIS Express 에서 웹 응용 프로그램이 동작하도록 할 수 있습니다.

그리고 다음의 확인 메시지에서 '예'를 클릭하면 바로 IIS Express로 웹 응용 프로그램을 개발할 수 있습니다.

IIS Express는 윈도우의 알림 영역에서 찾을 수 있으며 이 아이콘을 이용하여 여러 개의 호스팅 되고 있는 웹 응용 프로그램을 관리할 수 있습니다.

IIS Express를 사용하여 Visual Studio 2010에서 여러 가지 설정을 즉시 변경해 줄 수 있습니다.

그림 2 IIS Express 설치시 웹 응용 프로그램 속성

그림 3 기존 ASP.NET Development Server 속성

IIS 7과 IIS Express 버전의 비교표:

Area

IIS 7

IIS Express

Shipping mechanism

Ships with the OS.

Ships out-of-band. It is automatically included with WebMatrix but can also be installed separately.

Supported Windows editions

Limited number of Windows Vista and Windows 7 editions

Most editions of Windows Server 2003, 2008 and 2008 R2

All editions of Windows XP, Vista, Windows 7

All editions of Windows Server 2008 and 2008 R2

Supported .NET Framework versions

v2.0 SP1 and above

v2.0 SP1 and above (.NET 4.0 is required).

Supported programming languages

Classic ASP, ASP.NET, and PHP

Classic ASP, ASP.NET, and PHP

Process model

Windows Process Activation Service (WAS) automatically manages configured sites.

User launches and terminates sites.

Hosted WebCore (aka Hostable Web Core) support

Yes

Yes. IIS Express is implemented as a layer over HWC.

Supported protocols

HTTP, FTP, WebDAV, HTTPS, and WCF (including over TCP, Named Pipes, and MSMQ)

HTTP, HTTPS, and WCF over HTTP

Non-admin support

WAS must run with administrator user rights.

A standard user is allowed to complete most tasks.

Multi-developer support

None

Yes. Configuration files, settings, and Web content are maintained on a per-user basis.

Visual Studio support

Yes

VS 2010 SP1 Beta allows IIS Express to be used instead of Cassini. VS 2008 can also be manually configured to use IIS Express.

Runtime extensions

See http://www.iis.net/download/All for a complete list.

URL Rewrite and FastCGI. These extensions are built into IIS Express.

Management tools

IIS Manager, appcmd.exe

Appcmd.exe. Common IIS Express management tasks are also built into WebMatrix and Visual Studio 2010 SP1 Beta.

System tray support

None

Yes

Includes built-in IIS 7x modules for authentication, authorization, compression, etc.

Yes

Yes

IIS Express를 설치하기 위해 WPI(Web Platform Installer) 다운로드 페이지

http://www.microsoft.com/web/gallery/install.aspx?appid=iisexpress

그림 4 Web Platform Installer 초기 설치 화면

그림 5 기본적인 구성 요소인 IIS Express 설치 화면


2011년 .NET 개발자의 생존전략

VSTS 2010 팀 블로그 2011. 1. 10. 08:30 Posted by POWERUMC

최근 2년 동안 다양한 개발 분야의 기술들이 물망에 오르는 굉장히 뜻 깊은 해였습니다. 2년 전이면 Microsoft 강성재 차장과 함께 처음으로 "Visual Studio 한국 공식 팀"을 창설하면서 http://vsts2010.net 이 탄생한 시기이군요. 2008년 12월에 팀이 창설되고, 2009년 1월 5일이 팀 블로그 2주년이 되는 날이었군요.

바로 저희 "Visual Studio 한국 공식 팀" 블로그에서 한홀 한홀 정성스럽게 작성된 포스트들이 2년 여간의 기술 흐름을 대변해 주고 있으며, 그리고 2011년의 기술도 짐작해 볼 수 있는 짧지만 굵은 변화의 흐름과 함께 여기까지 온 것 같습니다.

우리 팀이 함께 해왔던 핵심 키워드의 태그는 무엇이었을까요?

  • Visual Studio 2010
  • .NET 4.0, .NET Framework 4.0
  • ASP.NET MVC
  • C# 4.0
  • C++0x, C++/CLI
  • Parallel Computing
  • WCF
  • Cloud
  • Application Lifecycle Management

   

그리고 위의 태그들에 대해 더 자세히 살펴보더라도 생소한 기술과 이름, 아키텍처, 환경 등이 2년 동안 격변을 일으키며 변화를 해왔다는 사실입니다.

2011년 이전까지는 여러분들에게 선택권이었던 것들이, 이제는 필수가 되어야 한다고 해도 과언이 아닐 겁니다. 비즈니스 요구사항의 단면을 보면 업무적인 요인, 시대적인 배경 등인데, 이 시대적인 배경에는 트랜드+시장+기술+… 이 있을테고요. 그리고 '우리가 이 시대적인 배경 중 '기술'에 한 배를 타고 흐르고 있는가…?' 에 다시 한번 생각해 볼만 합니다.

예전 2010년 6월 1일 REMIX10 세미나에서 여러분에게 말씀 드린 마지막 문구가 다시금 생각이 나네요.

http://www.techdays.co.kr/2010spring/remix10/session3_1.html

   

여러분의 생존전력은 바로 아래에 해답이 있습니다. 여러분들에게 필요한 것, 그리고 그 가능성이 있다고 판단하시면 2011년 생존을 위하여 달려보는 것은 매우 멋진 2011년 한 해가 될 것입니다.

   

.NET 프레임워크

.NET Framework .NET 의 과거와 현재, 그리고 미래
.NET Framework .NET Framework 4.0 의 특징
.NET Framework .NET Framework 4.0 마이그레이션 이슈
.NET Framework .NET 스마트클라이언트 한계 극복 [1]
.NET Framework .NET 스마트클라이언트 한계 극복 [2]
CLR 1. Hello 世界
CLR 2. CLR! CLR! CLR!
CLR 3. MSCorLib & Metadata
CLR 4. Assembly
CLR 5. Assembly - Strongly named assemblies
CLR 6. Assembly - GAC(Global Assembly Cache)
CLR 7. System.Object
CLR 8. System.Object (2)
CLR 닷넷4.0에서 네이티브코드와 매나지드코드의 동거 part 1.
CLR 닷넷4.0에서 네이티브코드와 매나지드코드의 동거 part 2-1.
CLR 닷넷4.0에서 네이티브코드와 매나지드코드의 동거 part 2-2. 네이티브 랩퍼 만들기
Managed Extensibility Framework [MEF] 1. Managed Extensibility Framework 이란?
Managed Extensibility Framework [MEF] 2. Parts 와 Contracts 선언
Managed Extensibility Framework [MEF] 3. Export 선언
Managed Extensibility Framework [MEF] 4. Import 선언
Managed Extensibility Framework [MEF] 5. Catalog 사용
Managed Extensibility Framework [MEF] 6. Lazy Exports
Managed Extensibility Framework [MEF] 7. Exports and Metadata
Managed Extensibility Framework [MEF] 8. Strongly Typed Metadata
Managed Extensibility Framework [MEF] 9. Recomposition
Managed Extensibility Framework [MEF] 10. Querying the CompositionContainer
Managed Extensibility Framework MEF Preview 6 공개
Managed Extensibility Framework MEF 는 Generic Type 을 지원하지 않는다!
Managed Extensibility Framework MEF 에 Generic Type 을 지원하기 위해서..?
Managed Extensibility Framework MEFGeneric 코드 플랙스에 공개합니다.

   

애자일 개발

Agile Development [Better Code]TDD의 개념이 완벽히 녹아 들어간 VSTS 2010
Agile Development [Better Code]Visual Studio 2010 Code Analysis Enhancements - 1.개요
Agile Development [Better Code]Visual Studio 2010 Code Analysis Enhancements - 2. Rule Sets Feature
Agile Development [Better Code]PEX, Automated Whitebox Testing for .NET - 1. 개요
Agile Development [Better Code]Visualize Code Relationships
Agile Development [Testing] TDD (Test-Driven Development-테스트 주도 개발)
Agile Development [Testing] BDD (Behavior-Driven Development?행위 주도 개발)
Agile Development [Testing] Moq.NET (T/B Driven Development)
Agile Development [Better Code]Visual Studio Code Analysis Enhancements - 3. Data Flow Rules and Phoenix Engine
Agile Development 애자일에 대한 고찰
Agile Development [ALM-Test] 1. 왜 단위 테스트를 해야 하는가?
Agile Development [ALM-Test] 2. 한국적인 애자일 모델의 필요성
Agile Development [협업 1] 협업 도구의 통일성과 협업 인프라 관리
Agile Development [ALM-Test] 3. 테스터에 대한 오해와 진실
Agile Development [ALM-Test] 4. 테스터(SDET) 의 역할
Agile Development [ALM-Test] 5. 테스트 계획
Agile Development [ALM-Test] 6. Load Runner vs Visual Studio 2010 테스팅 비교 분석 - http://willstory.tistory.com/4 제공
Agile Development [ALM-Test] 7. TDD vs 계약기반 테스트
Architect Development Architect Development ?
Architect Development 몽당연필과 함께하는 VSTS 2010 모델링 0/4
Architect Development 몽당연필과 함께 하는 VSTS 2010 모델링 1/4
Architect Development Windows Server AppFabric - Velocity 란?
Architect Development WCF=SOA 에 대한 고찰

   

ASP.NET 4.0

ASP.NET 4.0 [ASP.NET 4.0] 1. Core Service - Extensible Output Caching
ASP.NET 4.0 [ASP.NET 4.0] 2. AJAX - Declarative Client Template Rendering
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Dynamic Data(1)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Dynamic Data(2)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Designer & Deployment
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Core Services
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - New Features in the Microsoft Ajax Library
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(1)
ASP.NET 4.0 Razor in WebMatrix
ASP.NET 4.0 Razor in WebMatrix(2) 코드의 재 사용
ASP.NET 4.0 Razor in WebMatrix(3) ? WebMatrix Helper
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(2)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(3)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(4)
ASP.NET 4.0 ASP.NET 4 와 Visual Studio 2010 Web Development Beta 2 Overview - Web Forms(5)
ASP.NET MVC M, V 그리고 C의 각방생활(1) - ASP.NET MVC vs ASP.NET WEB FORM
ASP.NET MVC M, V 그리고 C의 각방생활(2) - ASP.NET MVC와 인사나누기
ASP.NET MVC M, V 그리고 C의 각방생활(3) - 초간단 사이트 만들기(1)
ASP.NET MVC M, V 그리고 C의 각방생활(4) - 유효성 검사
ASP.NET MVC M, V 그리고 C의 각방생활(5) - 초간단 사이트 만들기(2)
ASP.NET MVC M, V 그리고 C의 각방생활(6) - 유효성 검사(2)
ASP.NET MVC M, V 그리고 C의 각방생활(7) - 함께 즐겨요~ jQuery
ASP.NET MVC M, V 그리고 C의 각방생활(8) - jQuery와 탭메뉴 그리고 파샬뷰
ASP.NET MVC M, V 그리고 C의 각방생활(9) - jqGrid 사용해보자
ASP.NET MVC M, V 그리고 C의 각방생활(10) - jqGrid를 이용한 paging과 sorting
ASP.NET MVC ASP.NET MVC 3 Preview 1 이 릴리즈 되었습니다.
ASP.NET MVC M, V 그리고 C의 각방생활(11) - jqGrid로 데이터 추가,편집,삭제해보기
ASP.NET MVC M, V 그리고 C의 각방생활(12) - 테스팅 그거, 아무나 하나?
ASP.NET MVC JailBreak From Controllers and Actions
ASP.NET MVC VSTS2010 에서 Razor 코드 하이라이팅 지원하기

   

C# 4.0

C# [C# 4.0] Named and Optional Parameters
C# [C# 4.0] Duck Typing
C# [C# 4.0] New Extension Method "Zip"
C# [C# 4.0] Generic Covariance And Contra Variance
C# Welcome to Dynamic C#(1) - 첫만남.
C# Welcome to Dynamic C#(2) - Wanna be a polyglot.
C# Welcome to Dynamic C#(3) - 마음이 넒어진 C#
C# Welcome to Dynamic C#(4) - 극과극 비교체험.
C# Welcome to Dynamic C#(5) - Return to Dynamic.
C# Welcome to Dynamic C#(6) - Return to Dynamic (2)
C# Welcome to Dynamic C#(7) - 아낌없이 표현해 주는 나무
C# Welcome to Dynamic C#(8) - DLR이 나무를 사랑하는 이유
C# Welcome to dynamic C# 외전(1) - Generate From Usage.
C# Welcome to dynamic C# 외전(2) - Generic Method.
C# Welcome to dynamic C# 외전(3) - 감시하는 자와 감시당하는 자.
C# Welcome to Dynamic C#(9) - Dynamic Returns Again.
C# Welcome to Dynamic C#(10) - Dynamic Returns Again.(2)
C# Welcome to Dynamic C#(11) - The Phantom of The Dynamic
C# Welcome to Dynamic C#(12) - dynamic은 외로운 아이.
C# Welcome to Dynamic C#(13) - 아직도 가야할 길.
C# Welcome to Dynamic C#(14) - 철지난 만우절에 낚여서 파닥파닥.
C# Welcome to Dynamic C#(15) - A/S for dynamic.
C# Welcome to Dynamic C#(16) - dynamic이라도 이건 안되는 거임.
C# Welcome to Dynamic C#(17) - 필요한 말만 하고 삽시다.
C# Welcome to Dynamic C#(18) - 이름을 붙이면서 벌어진 일들.
C# Welcome to Dynamic C#(19) - 위너 고르기.
C# Welcome to Dynamic C#(20) - 어르신과 대화하는 법.
C# Welcome to Dynamic C#(21) - 인덱스의 힘.
C# Welcome to Asynchronous C#(0) - C#의 전설.
C# Parallel Programming [C# 4.0] Parallel Extension - [1] 병렬 처리
C# Parallel Programming [C# 4.0] Parallel Extension - [2] 병렬 처리 아키텍처
C# Parallel Programming [C# 4.0] Parallel Extension - [3] TPL(Task Parallel Library)
C# Parallel Programming Welcome to Parellel world(1) - Here comes a new challenger!
C# Parallel Programming Welcome to Parallel C#(1) - 굿바이, 그리고 안녕~~?
C# Parallel Programming Welcome to Parallel C#(2) - 계속 되는 개념 찾기.
C# Parallel Programming Welcome to Parallel C#(3) - 작업의 기본.
C# Parallel Programming Welcome to Parallel C#(4) - 작업의 기본 Part 2.
C# Parallel Programming Welcome to Parallel C#(5) - 병렬작업에서 예외가 생기면 어케...?
C# Parallel Programming Welcome to Parallel C#(6) - To be continue...
C# Parallel Programming Welcome to Parallel C#(7) - Excuse me.
C# Parallel Programming Welcome to Parallel C#(8) - 취소 쉽게 하기.
C# Parallel Programming Welcome to Parallel C#(9) - 백지장은 맞들지 말엉.
C# Parallel Programming Welcome to Parallel C#(10) - 이보게, 잠깐 뒤를 돌아보지 않겠나.

   

C++/CLI

C++/CLI C++/CLI는 미운 오리새끼 or 백조
C++/CLI .NET에서의 C++/CLI의 의미
C++/CLI [Step 01] 'C++/CLI가 뭐야?'에 답하기 && 가장 많은 프로그래밍 언어로 만드는 프로그램 만들기
C++/CLI [Step 02-1] 클래스(class), 핸들(^), 그리고 구조체(struct)
C++/CLI [Step.02-2] 클래스(class), 핸들(^), 그리고 구조체(struct)
C++/CLI [step.03] 배열
C++/CLI [Step. 04] nullptr, interior_ptr, pin_ptr
C++/CLI [Step. 05] 관리 코드의 array를 비관리 코드에 포인터로 전달
C++/CLI [Step. 06-1] 관리코드의 문자열과 비관리코드의 문자열 변환
C++/CLI [Step. 06-2] 관리코드의 문자열과 비관리코드의 문자열 변환
C++/CLI [Step. 07] 비관리 클래스에서 관리 클래스를 멤버로, 관리 클래스에서 비관리 클래스를 멤버로
C++/CLI [Step. 08] 프로퍼티 ( property )
C++/CLI [Step. 09] 델리게이트 (delegate)
C++/CLI [Step. 10] 이벤트 ( event )
C++/CLI [Step. 11] 열거형( enum )
C++/CLI [Step. 12] for each
C++/CLI [Step. 13] parameter array
C++/CLI [Step. 15] static 생성자, initonly, literal
C++/CLI [Step. 14] 인터페이스 ( interface )
C++/CLI [Step. 16] array 클래스에 non-CLI 오브젝트 사용
C++/CLI [Step. 17] 델리게이트에 비관리 함수를 할당하기 그리고 다음 예고
C++/CLI [Step. 18] 순수 가상 함수
C++/CLI [Step. 19] char* -> 관리코드, 관리코드 -> char*
C++/CLI [Step. 20] 닷넷에서 HalfNetwork를 사용하자 - 1
C++/CLI [Step. 21] 닷넷에서 HalfNetwork를 사용하자 - 2
C++/CLI [Step. 22] 닷넷에서 HalfNetwork를 사용하자 ? 3
C++/CLI [Step. 23] 닷넷에서 HalfNetwork를 사용하자 ? 4
C++/CLI [Step. 24] 닷넷에서 HalfNetwork를 사용하자 ? 5
C++/CLI [Step. 25] 닷넷에서 HalfNetwork를 사용하자 ? 6(마지막)

   

C++0x

C++0x [VC++] 1. 큰 변화가 기대되는 Visual C++( VC++ )
C++0x [VC++] 2. C++0x의 auto
C++0x [VC++] 3. static_assert
C++0x [VC++] 4. 우측 값 참조( RValue Reference ) - 첫 번째
C++0x [VC++] 5. 우측 값 참조( RValue Reference ) ? 두 번째
C++0x [VC++] 6. 우측 값 참조( RValue Reference ) - 세 번째
C++0x [VC++] 7. 우측 값 참조( RValue Reference ) - 네 번째
C++0x [VC++] 8. 우측 값 참조( RValue Reference ) ? 다섯 번째
C++0x [VC++] 9. Lambda ( 람다 ) - 첫 번째
C++0x [VC++] 11. Lambda - 두 번째
C++0x [VC++] 12. Lambda - 세 번째
C++0x [VC++] 13. Lambda - 네 번째
C++0x [VC++] 14. decltype
C++0x 대용량 파일 조작을 위한 C++0x의 변화
C++0x nullptr
C++0x VC++ 10에 구현된 C++0x의 코어 언어 기능들
C++0x C++0x 관련 책 "Visual C++ 10과 C++0x"
C++0x "Visual C++ 10과 C++0x" pdf 파일
C++0x [Plus C++0x] 람다(Lambda) 이야기 (1)
C++0x [Plus C++0x] 람다(Lambda) 이야기 (2)
C++0x [Plus C++0x] 람다(Lambda) 이야기 (3)
C++0x [Plus C++0x] 람다(Lambda) 이야기 (마지막회)
C++0x [STL] 1. What's new in VC++ 2010?
C++0x [STL] 2. unique_ptr (1/2)
C++0x [STL] 3. unique_ptr (2/2)
C++0x [STL] 4. make_shared
C++0x [STL] 5. 에 추가된 새로운 함수들 (1/5)
C++0x [STL] 6. 에 추가된 새로운 함수들 all_of, any_of, none_of (2/5)
C++0x VS2010에서 nullptr의 알려진 버그
C++0x RValue Reference에 의한 STL의 성능향상 테스트
C++0x [STL] 7. 에 추가된 새로운 함수들 copy_if, copy_n, find_if_not (3/5)
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [0]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [1]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [2]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [3]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [4]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [5]
VC++ 10 Concurrency Runtime C++ 개발자를 위한 병렬 프로그래밍 동영상 [6/7] 완결!
VC++ 10 Concurrency Runtime PPL task를 이용한 피보나치 수 계산
VC++ 10 Concurrency Runtime 인사 및 Multi Core, Multi Thread...그리고 VC++ 10
VC++ 10 Concurrency Runtime Concurrency Runtime
VC++ 10 Concurrency Runtime Parallel Patterns Library (PPL)
VC++ 10 Concurrency Runtime 양보할 줄 아는 Concurrency Runtime의 event
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - Task
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - 병렬 알고리즘
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - parallel_for 알고리즘
VC++ 10 Concurrency Runtime Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - parallel_for_each 알고리즘
VC++ 10 Concurrency Runtime Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 2
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - parallel_invoke
VC++ 10 Concurrency Runtime Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 마지막회
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - combinable
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - task group에서의 병렬 작업 취소 - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - task group에서의 병렬 작업 취소 - 2
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_vector - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_vector - 2
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_queue - 1
VC++ 10 Concurrency Runtime Parallel Patterns Library(PPL) - concurrent_queue - 2
VC++ 10 Concurrency Runtime Concurrency Runtime(ConcRT)의 디버그 모드에서 메모리 leak 문제
VC++ 10 Concurrency Runtime Asynchronous Agents Library 소개
VC++ 10 Concurrency Runtime Asynchronous Agents Library - agent. 1 ( 상태 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? agent. 2 ( 기능 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library - message 전달 함수. 1 ( 전송 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message 전달 함수. 2 ( 수신 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 1. ( 인터페이스 )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 2. ( unbounded_buffer )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 3. ( overwrite_buffer & single_assignment )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 4. ( call )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 5. ( transformer )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 6. ( choice )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 7. ( join & multitype_join )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 8. ( timer )
VC++ 10 Concurrency Runtime Concurrency Runtime ? 동기화 객체 1. ( critical_section & reader_writer_lock )
VC++ 10 Concurrency Runtime Concurrency Runtime ? 동기화 객체 2. ( event )
VC++ 10 Concurrency Runtime Asynchronous Agents Library ? message block 9. ( custom )
VC++ 10 Concurrency Runtime Concurrency Runtime - 만델브로트 프랙탈 ( Mandelbrot Fractal ) 예제
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 1. ( Scheduler )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 2. ( SchedulerPolicy )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 3. ( ScheduleGroup )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 4. ( ScheduleTask )
VC++ 10 Concurrency Runtime Concurrency Runtime ? Task Scheduler 5. ( Context blocking )
Visual C++ 10 About Visual C++ 10
Visual C++ 10 디버깅 모드에서 역어셈블리 코드 보기
Visual C++ 10 Visual C++ 10의 변화
Visual C++ 10 [Upgrade to VC++ 10] _WIN32_WINNT 버전 문제
Visual C++ 10 VS2010 C++ 프로젝트의 디렉토리 설정

   

클라우드 컴퓨팅

Cloud 구름 속의 미래 : Windows® Azure™ Platform [1]
Cloud SQL Azure - CTP1
Cloud SQL Azure 알아보기 (1) - 데이터베이스 개체 생성
Cloud SQL Azure 알아보기(2) ? 데이터베이스 스키마 마이그레이션, 데이터 전송
Cloud 구름 속의 미래 : Windows® Azure™ Platform [2]
Cloud SQL Azure 사용 시 주의점(1) - 방화벽 설정
Cloud SQL Azure 알아보기(3) ?SQL Server 2008 R2 Nov CTP
Cloud SQL Azure 알아보기(4) ? SQL Azure Cloud App
Cloud SQL Azure 알아보기 (5)- SQL Azure 이점과 T-SQL 지원
Cloud [MS@클라우드컨퍼런스] MS 클라우드 기술과 플랫폼
Cloud 클라우드 기반 분산 컴퓨팅을 위한 AppFabric (1) : 아하! App 분산!
Cloud Hello Windows Azure / Windows Azure Platform의 이해
Cloud Hello Windows Azure / Gallery of 'Powered by Windows Azure Platform'
Cloud Hello Windows Azure / Windows Azure 개발 환경의 구축
Cloud Hello Windows Azure / Understanding Windows Azure Development Process
Cloud Hello Windows Azure / Windows Azure Tools for Visual Studio 1.2 출시
Cloud Hello Windows Azure / Windows Azure Platform 최신 소식 업데이트 (종합) [수정]
Cloud Hello Windows Azure / Twitter 스타일 방명록 만들기 #1
Cloud Windows Azure Update: Microsoft Project Code-Named "Houston" CTP 1
Cloud SQL Azure와 Excel 2010의 PowerPivot
Cloud Hello Windows Azure / Twitter 스타일 방명록 만들기 #2
Cloud Windows Azure Update: CloudStorageAccount 클래스 사용 시 주의 사항
Cloud SQL Azure Update: Dynamic Management View
Cloud Hello Windows Azure / Twitter 스타일 방명록 만들기 #3
Cloud Windows Azure Update: myAzureStorage
Cloud SQL Azure 와 SQL Reporting Service
Cloud Windows Azure Update: Windows Azure CDN의 활용
Cloud [작업 중] Windows Azure Update: Adaptive Smooth Streaming with Windows Azure Storage

   

게임 개발

Direct3d Mobile [d3dm 기초] 1. wm6.x 개발환경 세팅
Direct3d Mobile .NET 기반에서 공개소스 게임엔진 포팅하기
DirectX 11 [JumpToDX11-1] 사라진 Direct3D 오브젝트를 찾아서...
DirectX 11 [JumpToDX11-2]DeviceContext...넌 누구냣!!
DirectX 11 [JumpToDX11-3] Feature Level
DirectX 11 [JumpToDX11-4] ID3D11View
DirectX 11 [DX11_#1]D3D Buffer( 1 / 2 )
DirectX 11 [DX11_#2]D3D Buffer( 2 / 2 )
DirectX 11 [DX11_#3]기본적인 설정
DirectX 11 [JumpToDX11-5] 새로운 시대를 여는 DirectX11...
DirectX 11 [JumpToDX11-6] 커맨드(Command)...
DirectX 11 [DX11_#4]텍스트, 버튼 출력
DirectX 11 [JumpToDX11-7] 간편해진 리소스 처리.
DirectX 11 [JumpToDX11-8] Deferred Contexts
DirectX 11 [JumpToDX11-9] Multi-threaded Rendering 을 위한 API.
DirectX 11 [JumpToDX11-10] GPGPU 를 위한 DirectCompute.
DirectX 11 [JumpToDX11-11] DirectCompute 를 위한 한걸음!
DirectX 11 [JumpToDX11-12] DirectCompute 의 절차.
DirectX 11 [JumpToDX11-13] Tessellation 등장.
DirectX 11 [DX11_#5]DirectX11의 활용 사례(1/3)
DirectX 11 [JumpToDX11-14] DirectX9 세대의 테셀레이션( ID3DXPatchMesh 편 )
DirectX 11 [JumpToDX11-15] DirectX9 세대의 테셀레이션( IDirect3DDevice9::DrawXXXPatch편 )
DirectX 11 [알콜코더의 미리 배워보는 DirectX 11 - 입문편] 0. 누구를 위한 연재인가
DirectX 11 [알콜코더의 미리 배워보는 DirectX11-입문편] 1.튜터리얼 01 : 다이렉트 3D 기초 #1
DirectX 11 [알콜코더의 미리 배워보는 DirectX11-입문편] 1.튜터리얼 01 : 다이렉트 3D 기초 #2
DirectX 11 [JumpToDX11-16] DirectX9 세대의 테셀레이션( D3DXTessellateNPatches편 )
DirectX 11 [알콜코더의 미리 배워보는 DX11 ? 입문편] DX11에서 무엇이 추가되었나?
DirectX 11 [JumpToDX11-17] DirectX9 세대의 테셀레이션( ATI 라이브러리편 )
DirectX 11 [발표자료] 예제로 느껴보는 다이렉트X 11의 매력
DirectX 11 [JumpToDX11-18] DirectX11의 테셀레이션 ( 테셀레이션을 위한 하드웨어의 등장편 )
DirectX 11 [알콜코더의 미리 배워보는DX11 입문편] DirectX 11의 특징들
DirectX 11 [알콜코더의 미리배워보는 DX11-입문편] 1. 튜터리얼01 : 디바이스와 스왑체인의 생성

   

F#

F# Welcome to F#(1) - 첫만남.
F# Welcome to F#(2) - 두번째 만남.
F# Welcome to F#(3) - 사소한 탐색전.
F# Welcome to F#(4) - 과거와 배경을 좀 더 알고싶어.
F# Welcome to F#(5) - 아주 조금씩 심화되는 탐색전.
F# Welcome to F#(6) - 비교본능.
F# Welcome to F#(7) - 클리프 행어.
F# Welcome to F#(8) - 은총알과 엄친아.
F# Welcome to F#(9) - 메이져 데뷰.
F# Welcome to F#(10) - 인도음식 카레.....?
F# Welcome to F#(11) - 차별을 권장하는 언어인거임?!?!
F# Welcome to F#(12) - 공동작업 좋치아니항가

   

MFC

MFC [MFC] 리스타트 매니저(Restart Manager) - (1/3) : 기능 소개
MFC [MFC] 리스타트 매니저(Restart Manager) - (2/3) : 사용하기
MFC [MFC] 리스타트 매니저(Restart Manager) - (3/3) : 활용하기
MFC [MFC] 태스크 대화상자(Task Dialog) - (1/3) : 기능 소개
MFC [MFC] 태스크 대화상자(Task Dialog) - (2/3) : 사용하기
MFC [MFC] 태스크 대화상자(Task Dialog) - (3/3) : 활용하기
MFC [MFC] 태스크 대화상자(Task Dialog) - 예제 코드 올립니다.
MFC [MFC/윈도우 7 멀티터치] #2 : 제스처(gesture)를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #3 : 제스처(gesture)를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #4 : WM_TOUCH 메세지를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #5 : WM_TOUCH 메세지를 이용한 구현()
MFC [MFC/윈도우 7 멀티터치] #6 : 예제 코드 올립니다

   

RIA

RIA Expression Blend3 preview - 1.인터페이스
RIA Expression Blend3 preview - 2. Photoshop import
RIA Silverlight 3 & Blend 3 RC 공개!!!
RIA Silverlight 4 Beta 공개
RIA .Net Ria Service + IIS6 + Silverlight 4 Troubleshooting!!
RIA 실버라이트 비하인드 코드에서 바인딩하기.
RIA .Net Ria Service 와 Entities 그리고 Stored Procedure 하다가 생긴일..
RIA 실버라이트 프로그래머가 할 수 있는 최소한의 블랜드 디자이너를 위한 배려

   

SharePoint 2010

SharePoint 2010 Visual Studio 2010 에게 바란다 - SharePoint 14 Development
SharePoint 2010 SharePoint 2010 Overview
SharePoint 2010 SharePoint 2010 개발 환경 구성
SharePoint 2010 SharePoint 2010 개발 환경- Hello World 웹 파트 생성 및 배포하기
SharePoint 2010 SharePoint 2010 Web Part 생성
SharePoint 2010 SharePoint 2010 Visual Web Part
SharePoint 2010 SharePoint 2010 Feature
SharePoint 2010 SharePoint 2010 Event Receiver
SharePoint 2010 SharePoint 2010 데이터 기술
SharePoint 2010 SharePoint 2010 Server Object Model
SharePoint 2010 Visual Studio 2010 출시에 따른 SharePoint Developer Tools
SharePoint 2010 SharePoint 2010 LINQ to SharePoint
SharePoint 2010 Client Object Model - .NET
SharePoint 2010 Client Object Model ? Silverlight (1)
SharePoint 2010 Client Object Model ? Silverlight (2)
SharePoint 2010 Client Object Model - Javascript(1)
SharePoint 2010 Client Object Model - Javascript(2)
SharePoint 2010 Client Object Model ? 정리
SharePoint 2010 SharePoint 2010 개발환경 구축 가이드
SharePoint 2010 REST -.NET
SharePoint 2010 REST ? Silverlight
SharePoint 2010 REST - jQuery
SharePoint 2010 SharePoint 2010 프로젝트 디버깅
SharePoint 2010 SharePoint 2010 Developer Dashboard

   

Team Foundation Server

Team Foundation Server Visual Studio Team System 2010 (CTP10) - 작업 항목 링크
Team Foundation Server TFS 2010 설치 하기
Team Foundation Server TFS 2010 Build Service 설치
Team Foundation Server TFS 2010 설치 과정 중에 TF255040 문제
Team Foundation Server Visual Studio 2010을 활용한 ALM (1-5) - ALM 이란 무엇인가
Team Foundation Server Team Foundation 트러블 슈팅 가이드
Team Foundation Server Visual Studio Team Foundation Server 2010 를 설치해보자
Team Foundation Server Visual Studio Team Foundation Server 2010 설치 전 할일
Team Foundation Server VS TFS 2010 설치편 - 설치전 IIS, .NET 설치
Team Foundation Server VS TFS 2010 설치편 - 설치 시작
Team Foundation Server VS TFS 2010 구성편 - 설치 후 TFS 구성으로 점심 얻어먹기 편
Team Foundation Server VS TFS 2010 사용편 - SourceSafe? 버려~
Team Foundation Server [HowTo] Team Foundation Server 의 로컬 매핑 캐시 제거하기
Team Foundation Server [HowTo] SharePoint 2010 Beta 깨끗하게 제거하기
Team Foundation Server [HowTo] SCVMM 의 Install Virtual Guest Service 작업 중 2941 오류
Team Foundation Server [HowTo] TFS2010 의 Tfs_Analysis 웨어하우스 데이터베이스가 망가졌을 경우
Team Foundation Server [PPT] 테스트와 가상화의 만남 - 테스트 가상화(Lab Management)
Team Foundation Server Team Foundation Server 2010으로 업그레이드, 마이그레이션, 동기화
Team Foundation Server Visual Source Safe 사용자를 위한 TFS2010 시리즈

   

Visual Studio 2010

Visual Studio 2010 Visual Studio Team System 2010 CTP 만료 해결하기
Visual Studio 2010 Visual Studio 2010 의 특징
Visual Studio 2010 Visual Studio 2010 내부 빌드 최신 동영상: C# 4.0 Language + IDE + WPF Shell + Editor
Visual Studio 2010 Visual Studio 2010 & .NET 4.0 참고 자료들
Visual Studio 2010 Visual Studio 2010 Beta 1 설치부터 살펴보기
Visual Studio 2010 멀티 모니터 사용
Visual Studio 2010 Visual Studio 2010 Beta 2 출시
Visual Studio 2010 Visual Studio 2010 Beta 2 설치 미리 보기
Visual Studio 2010 VS 2010 Beta 2 설치 과정에서 Silverlight SDK 문제
Visual Studio 2010 VS2010 베타2의 WPF & Silverlight 디자이너 성능 향상 팁
Visual Studio 2010 VS 2010 기능 소개 01 인텔리 센스 기능의 변화
Visual Studio 2010 Visual Studio 2010과 Blend Preview for .NET 4 통합 문제
Visual Studio 2010 VS 2010 기능 소개 02 - IDE의 기능 추가
Visual Studio 2010 VS 2010 기능 소개 03 - IDE의 변화
Visual Studio 2010 Visual Studio 2010 출시 일정
Visual Studio 2010 VS 2010 기능소개 04 - Visual C#&VB 개발자 IDE Tips & Tricks 첫번째
Visual Studio 2010 VS 2010 기능소개 05 - Visual C#&VB 개발자 IDE Tips & Tricks 두번째
Visual Studio 2010 Visual Studio 2010 RC 공개 임박!
Visual Studio 2010 Visual Studio 2010 RC 공개
Visual Studio 2010 C#에서 IntelliSense가 동작하지 않을 때 문제 해결 방법
Visual Studio 2010 똑똑한 검색을 지원하는 VSTS 2010의 "Navigate To" 검색
Visual Studio 2010 실버라이트4 RC와 블렌드 4 베타 공개
Visual Studio 2010 윈도우폰 7 개발환경 공개
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (2회) - VS IDE
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (3회) - Box Selection
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (4회) - Call Hierarchy
Visual Studio 2010 Visual Studio 2010 출시와 완소 정보 총 정리
Visual Studio 2010 Visual Studio 2010 e-book 무료로 다운로드 하세요
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (5회) - Navigate To
Visual Studio 2010 Visual Studio 2010 RTM 추가 완소 정보
Visual Studio 2010 Visual Studio 2010! 나랑 놀아보자 ? 기본편 (6회) - Generate from Usage
Visual Studio 2010 VS 2010 기능소개 05 - Visual C#&VB 개발자 IDE Tips & Tricks 두번째
Visual Studio 2010 Visual Studio 2010, 2008, 2005 에서 .NET Framework 1.1 개발하기
Visual Studio 2010 Visual Studio 2010, 2008, 2005 에서 .NET Framework 1.1 개발하기
Visual Studio 2010 Just for fun! / Visual Studio Express Edition
Visual Studio 2010 왜 Visual Studio 2010 이여야 하는가?
Visual Studio 2010 Visual Studio 2010 최신 PDF 자료를 MSDN 에서 다운로드 받으세요
Visual Studio 2010 Just for fun! / DreamSpark는 대학생 여러분을 위한 솔루션입니다.
Visual Studio 2010 VS2008 을 VS2010 에서 동시에 개발하기
Visual Studio 2010 VS2008 과 VS2010 동시에 개발하기 : 테스트 프로젝트가 포함 될 경우
Visual Studio 2010 Introducing Visual Studio LightSwitch! - Enjoy your development
Visual Studio 2010 Visual Studio Hotfix List
Visual Studio 2010 곧 다가올 기술, Microsoft Research [1/2]
Visual Studio 2010 곧 다가올 기술, Microsoft Research [2/2]
Visual Studio 2010 Visual Studio 31 (1) - 시작, 그리고 Intellisense
Visual Studio 2010 Visual Studio 31 (2) - Startpage
Visual Studio 2010 Visual Studio 31 (3) - Temp Project
Visual Studio 2010 Visual Studio 31 (4.1) - Visual Studio 2010 Productivity Power Tools, Part 1
VIsual Studio Extensibility [Blueprints] S+S Blueprints
VIsual Studio Extensibility Visual Studio 2010 SDK 와 Readme
VIsual Studio Extensibility Visual Studio 2010 Extension Manager
VIsual Studio Extensibility [VSIX] 1. What is different from before version?
VIsual Studio Extensibility [VSIX] 2-1. How to start VSIX programming
VIsual Studio Extensibility [VSIX] 2-2. How to start VSIX programming
VIsual Studio Extensibility MousePresentationTracker - MEF 세미나 예제
VIsual Studio Extensibility [VSX] 1. Visual Studio Extensibility,, 그 시작
VIsual Studio Extensibility Visual Studio 2010 확장 모델인 VSIX 버그
VIsual Studio Extensibility VSGesture v2.0 for VS2010 is now available for download

   

우리 블로그 소식

VSTS 2010 팀 블로그 Visual Studio Team System 2010 공식 팀 블로그 맴버소개
VSTS 2010 팀 블로그 Visual Studio Team System 2010 팀 블로그 소개
VSTS 2010 팀 블로그 VSTS 2010 팀 블로그/스터디 맴버를 모집합니다.
VSTS 2010 팀 블로그 VSTS 2010 팀 맴버 지원을 마감합니다
VSTS 2010 팀 블로그 Visual Studio Team System 2010 Beta 1 공개
VSTS 2010 팀 블로그 [MSDN 주간 세미나] 발표자료 / .NET Framework와 Visual Studio : 현재와 미래 1, 2
VSTS 2010 팀 블로그 VSTS 2010 팀 3분기 맴버 모집
VSTS 2010 팀 블로그 VSTS 2010 팀 세미나 동영상 - 6월 10일
VSTS 2010 팀 블로그 VSTS 2010 팀 맴버 추가 모집
VSTS 2010 팀 블로그 VSTS 2010 팀 트위터를 오픈하였습니다.
VSTS 2010 팀 블로그 TECH DAY 2009 행사 오픈!!!
VSTS 2010 팀 블로그 VSTS 2010 공식 블로그 Viva 2010팀 멤버 추가 모집 공고
VSTS 2010 팀 블로그 [세미나] 차세대 응용 프로그램 구축 방법 및 사례 소개 세미나
VSTS 2010 팀 블로그 Visual Studio 2010 팀에서 팀원 모집합니다.
VSTS 2010 팀 블로그 한국 Visual Studio 2010 사용자를 위한 트위터 커뮤니케이션
VSTS 2010 팀 블로그 C++ 개발자와 함께하는 Visual Studio 2010
VSTS 2010 팀 블로그 [무료 세미나] ReMIX 10
VSTS 2010 팀 블로그 6월 1일, 대한민국 웹 컨퍼런스의 지존 ReMIX 10가 개최됩니다!
VSTS 2010 팀 블로그 REMIX10 의 VS2010 팀 후기
VSTS 2010 팀 블로그 6월 1일, REMIX10 세미나 세션 공개
VSTS 2010 팀 블로그 [세미나] Visual Studio Camp #1
VSTS 2010 팀 블로그 [세미나 후기] Visual Studio Camp #1
VSTS 2010 팀 블로그 [세미나 발표 자료] Visual Studio Camp #1
VSTS 2010 팀 블로그 [세미나] Visual Studio Seminar #1 / 2010년 9월 28일
VSTS 2010 팀 블로그 9월 13일에 개최하는 KGC에서 강연을 합니다.
VSTS 2010 팀 블로그 KGC10에서의 VS2010 스터디 팀의 활약 모습
VSTS 2010 팀 블로그 [VSKOREA] Visual Studio 2010 정보가 한 눈에…
VSTS 2010 팀 블로그 [세미나 후기] Visual Studio Seminar #1
VSTS 2010 팀 블로그 [세미나 발표 자료] Visual Studio Seminar #1
VSTS 2010 팀 블로그 [후기] C++ & 게임 개발자를 위한 개발 생산성 및 퍼포먼스 향상 전략 세미나

   

WCF

WCF WCF란 무엇인가?
WCF 기본 WCF 프로그래밍 - 첫 WCF 서비스 만들기
WCF 기본 WCF 프로그래밍 - 첫 WCF 서비스 만들기 2
WCF WCF의 기본 - Service Contract
WCF WCF의 기본 - Data Contract
WCF WCF 서비스의 동시성(Concurrency) - 1
WCF WCF 서비스의 동시성(Concurrency) - 2
WCF WCF - Serialization
WCF WCF Hosting - WAS를 이용한 Hosting
WCF 도메인을 여러개 등록했을때 WCF 서비스를 호스팅 할수 없어요 ㅠㅠ
WCF WCF Hosting(2) - ASP.NET 호환성(Compatibility)
WCF WCF Hosting (3) - Windows Service를 이용한 Hosting
WCF WCF Security (1) - SSL을 이용한 전송계층에서의 보안 설정
WCF WCF Security (2) - 전송 계층에서의 메세지 인증 (사용자 지정 인증)
WCF WCF Troubleshooting (1)
WCF WCF Service Configuration Editor
WCF WCF Troubleshooting (2)


정말이지, 테스팅 그거 아무나 하는거 아니죠. 특히 저처럼 게으른 놈은 발을 들여놓기가 무서울때도 있습니다.
고객분들은 빠른 결과물을 얻길 원하시고, 그 고객이 여럿이면 모두가 자기의 일이 우선이니 빨리 좀 해달라고 아우성 거릴때가 많습니다. 가뜩이나 개발로도 벅찬 시간인데, 테스트라뇨.. 에잇!
하지만, 그렇게 작업을 한 후 스테이징(Staging Server - 라이브 서버에 반영하기 전 배포하여 테스트하는 서버입니다^^) 에 적용해놓으면 테스트팀에서는 온갖 방법으로(정말 어처구니 없는 입력값으로 마구 공격(?)해 들어오시죠) 테스트를 한 후 결과물들을 전달해주시죠. 그것 예외처리하는 것으로 인해 또한번의 시간이 소비되고 다시 테스트하고 다시 결과물 받고, 계속 반복되는거죠. 그렇게되면, 처음에 빨리 개발했던 시간이 무용지물이 되어버리는 것이죠.

신입시절에(지금도 신입 짬밥이죠;;) 과장님 한 분이, '야, 어떻게 너는 일을 주면 생각없이 컴퓨터로 바로 달려들어 개발하냐?' 라고 한 말씀 해주셨습니다. 먼저 그림을 그려보라고, 이면지 많잖아. 없어? 내가 줄까? 그러시며,  흐름을 생각하며, 어떤식으로 해야할지 먼저 그림을 그리라고요.
테스팅하는 것도 먼저 그림을 그려보는 작업 같습니다. 이렇게 개발을 하면 원하는 결과값이 나오겠지하며, 테스트와 병행하며 원하는 결과값이 맞게 나오는지 확인해가며 개발을 해나가는 거죠.



단위 테스트 프레임워크를 사용해보자


닷넷 프레임워크를 위한 테스트 프레임워크가 많습니다. 다들 아시는 NUnit, xUnit, MbUnit 등이 있죠. 하지만, 저 게으른 것 다들 아실겁니다. 그래서! 비주얼 스튜디오 단위 테스트를 이용하겠습니다. 위 단위 테스트 사이트들 들어가서 다운받고 설치하고, 에휴~^^;

ASP.NET MVC 프로젝트를 생성하게 되면 아래의 화면과 같이 단위 테스트를 할지 여부를 묻는 대화상자가 나옵니다. 예(Yes)를 선택하면, 프로젝트가 생성될때 테스트 프로젝트도 같이 생성하게 됩니다.


테스트 프로젝트에는 Controllers라는 폴더가 있고, 그 안에는 샘플프로젝트의 Home컨트롤러와 Account컨트롤러를 테스트하기 위한 HomeControllerTest.cs와 AccountControllerTest.cs 가 있습니다. HomeControllerTest를 열어보면 다음과 같습니다.


Microsoft.VisualStudio.TestTools.UnitTesting 네임스페이스가 임포트 되어 있는 것이 보이고, [TestClass], [TestMethod] 가 눈에 띄네요. 주석을 보니,

TestClass : 테스트 메서드가 포함된 클래스를 식별하는데 사용됩니다.
TestMethod : 테스트 메서드를 식별하는데 사용됩니다.

이렇다네요.^^



테스트와 함께 하시겠습니까?


다음은 TelDirController 소스입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace UsingTest.Controllers
{
    public class TelDirController : Controller
    {
        //
        // GET: /TelDir/

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

        //
        // GET: /TelDir/Details/5

        public ActionResult Details(int id)
        {
            return View();
        }       
    }
}

아무것도 처리하지 않은 깨끗한(?) 소스입니다. Index(), Details() 라는 두 액션메쏘드가 있고, 두 메쏘드 모두 View를 리턴하고 있습니다.

그럼, TelDirController를 위한 테스트를 생성해보겠습니다.



테스트 만들기


테스트 프로젝트의 Controllers를 오른쪽 버튼으로 클릭한 후 추가 -> 새 테스트를 선택합니다.


테스트하려는 컨트롤러 이름 뒤에 Test 만 붙여서 이름을 만들겠습니다. TelDirControllerTest 처럼요.^^;


확인 버튼을 클릭하면 다음의 소스가 보이실겁니다^^


지금으로선 불필요한 것들을 모두 닫아놓긴 했는데요, 물론 삭제하셔도 됩니다. 이런 것도 싫다 하시면, Controllers 폴더에서 클래스를 생성한 후 TestClass와 TestMethod 속성, 그리고 UnitTesting 네임스페이스를 임포트시키시면 됩니다.



맛보기 테스트


정말 맛만 보여드리겠습니다.^^;

TelDirController.Details 메쏘드가 정말 "Details" 뷰를 리턴하는지 테스트 해보도록 하겠습니다.
먼저 테스트 메쏘드를 만들어보겠습니다.

[TestMethod]
public void DetailsTest()
{
    var controller = new TelDirController();
    var result = controller.Details(1) as ViewResult;
    Assert.AreEqual("Details", result.ViewName);
}

Assert.AreEqual 은 비교대상의 두 값이 같은지 여부를 나타냅니다. 같으면 성공, 다르면 실패.
ViewResult.ViewName은 컨트롤러에서 리턴받은 뷰명을 나타냅니다.
자~ 테스트를 진행해볼까요?


'솔루션의 모든 테스트 실행' 버튼을 클릭합니다. 단축키는 Ctrl+R,A 라고 친절히 알려주네요^^ 물론 지금은 테스트 케이스가 하나라 이렇게 하지만 모든 테스트 실행 좌측에 있는 버튼인 '현재 컨텍스트의 테스트 실행'은 현재 선택받은 곳, 즉 커서가 위치한 곳의 테스트를 진행합니다.


엥?! 실패하였습니다. 예상 값이 Details 인데 실제값은.. 실제값은.. 없네요;;

TelDirController의 Details 메쏘드를 살펴보죠.

public ActionResult Details(int id)
{
    return View();
}

이렇게 되어 있네요. 잘못된 것은 없어보이는데요. 

Details 뷰를 리턴하려면 두 가지 방법이 있습니다. 위처럼 Details 액션 메쏘드에서 return View() 를 하는 방법(이것은 액션 메쏘드의 이름에서 유추가 됩니다), 다른 하나는 명시적으로 return View("Details") 와 같은 방법입니다.

그럼, 또다른 방법을 택해서 테스트를 돌려보죠.

public ActionResult Details(int id)
{
    return View("Details");
}

결과는


통과~.
이래서, 아~ 테스트시에는 뭐든지 확실하게 명시적으로 표현해줘야하는구나~ 라는 것을 알 수 있습니다.^^



마무리요



이번 포스팅은 간단하게 테스트하는 방법에 대해서 알아봤는데요, 다음은 이 테스팅에 대해서 좀더 자세하게 알아보는 시간을 가져보겠습니다.

맛이 어떠셨나요? 다음에는 더 화끈한 맛을 보여드리도록 하겠습니다^^



참고자료 : http://www.asp.net/mvc/tutorials/creating-unit-tests-for-asp-net-mvc-applications-cs
잊고 계셨을지도 모를 jqGrid 마지막편입니다.
이번 시간은 jqGrid를 이용하여 데이터를 추가, 편집, 삭제해보는 시간을 가져보도록 하겠습니다.


뷰페이지부터 보죠


지난 포스팅에 이어나갑니다. 먼저 가장 중요한 스크립트 부분을 보시면,

var updateDialog = {
                url: '<%= Url.Action("Update", "Home") %>'
                , closeAfterAdd: true
                , closeAfterEdit: true
                , modal: true
                , onclickSubmit: function (params) {
                    var ajaxData = {};
                    var list = $("#list");
                    var selectedRow = list.getGridParam("selrow");
                    rowData = list.getRowData(selectedRow);
                    ajaxData = { dirId: rowData.dirId };
                    return ajaxData;
                }
                , width: "400"
            };
            $.jgrid.nav.addtext = "추가";
            $.jgrid.nav.edittext = "편집";
            $.jgrid.nav.deltext = "삭제";
            $.jgrid.edit.addCaption = "전화번호부 추가";
            $.jgrid.edit.editCaption = "전화번호부 편집";
            $.jgrid.del.caption = "전화번호부 삭제";
            $.jgrid.del.msg = "정말 삭제하실거에요?";
            $("#list").jqGrid({
                url: '<%= Url.Action("EntityGridData", "Home") %>',
                datatype: 'json',
                mtype: 'POST',
                colNames: ['No', '이름', '전화번호', '이메일', '단축다이얼'],
                colModel: [
                  { name: 'dirId', index: 'dirId', width: 40, align: 'center', editable: true, editrules: { edithidden: false }, hidedlg: true, hidden: true },
                  { name: 'name', index: 'name', width: 200, align: 'left', editable: true, edittype: 'text', editrules: { required: true }, formoptions: { elmsuffix: ' *'} },
                  { name: 'phone', index: 'phone', width: 200, align: 'left', editable: true, edittype: 'text', editrules: { required: true }, formoptions: { elmsuffix: ' *'} },
                  { name: 'email', index: 'email', width: 300, align: 'left', editable: true, edittype: 'text', editrules: { required: true, email: true }, formoptions: { elmsuffix: ' *'} },
                  { name: 'speedDial', index: 'speedDial', width: 200, align: 'center', editable: true, edittype: 'text', editrules: { required: true }, formoptions: { elmsuffix: ' *'}}],
                pager: $('#pager'), 
                emptyrecords: "Nothing to display",             
                rowNum: 3,
                rowList: [3, 10, 20, 50],
                sortname: 'dirId',
                sortorder: "desc",
                viewrecords: true,
                caption: '전화번호부',
                ondblClickRow: function (rowid, iRow, iCol, e) {
                    $("#list").editGridRow(rowid, updateDialog);
                }
            }).navGrid('#pager',
                {
                    edit: true, add: true, del: true, search: false, refresh: true
                },
                updateDialog,
                updateDialog,
                updateDialog

            );

너무 많이 바뀌었나요? 그래도 알아보시죠? :) 굵은거~굵은거~

먼저 보이는것은 updateDialog가 보이네요. url도 보이고 submit 하고 ajax도 보이고.
데이터의 추가,편집,삭제시에 뜨는 팝업창에서 할일들이죠. Home 컨트롤러의 Update라는 액션메쏘드를 호출할거고요.
추가, 편집 후에는 창을 닫을 것이고(closeAfterAdd: true, closeAfterEdit: true), 모달창이고(modal: true), submit시 ajax를 사용하는 것 같아보이네요^^

다음으로 보이는게 $.jgrid.nav... 입니다. 이것은 지난 시간에도 말씀드렸던  grid.locale-en.js 을 수정하는 부분입니다. 이런 언어파일에는 디폴트값이 들어가 있다고 말씀드렸었죠? 기억하시죠? ;;

$.jgrid.edit = {
    addCaption: "Add Record",
    editCaption: "Edit Record",
    bSubmit: "Submit",
    bCancel: "Cancel",
    bClose: "Close",
    processData: "Processing...",
    msg: {
        required:"Field is required",
        number:"Please, enter valid number",
        minValue:"value must be greater than or equal to ",
        maxValue:"value must be less than or equal to",
        email: "is not a valid e-mail",
        integer: "Please, enter valid integer value",
        date: "Please, enter valid date value"
    }
};
$.jgrid.del = {
    caption: "Delete",
    msg: "Delete selected record(s)?",
    bSubmit: "Delete",
    bCancel: "Cancel",
    processData: "Processing..."
};
$.jgrid.nav = {
    edittext: " ",
    edittitle: "Edit selected row",
    addtext:" ",
    addtitle: "Add new row",
    deltext: " ",
    deltitle: "Delete selected row",
    searchtext: " ",
    searchtitle: "Find records",
    refreshtext: "",
    refreshtitle: "Reload Grid",
    alertcap: "Warning",
    alerttext: "Please, select row"
};

뷰페이지에서 수정한 부분은 두껍게 표시하였습니다.

$.jgrid.edit 의 msg중 required:"Field is required", email: "is not a valid e-mail" 이 부분이 뷰페이지의 colModel의 editrules: { required: true, email: true } 값과 매치가 되는 거죠.
또, grid의 네비게이션바에서 추가, 편집, 삭제 표시가 이미지로만 되어있었던 것을( $.jgrid.nav 의 add,edit,del 텍스트값이 빈값으로 되어있죠?) 뷰페이지에서 타이틀을 달아본겁니다.

나머지 부분도 재미있게 수정해서 테스트해보세요^^ 버튼 이름 변경, 필수값 에러 메시지, 경고메시지 등이 수정가능하네요. 



데이터를 처리하자


컨트롤러의 액션메쏘드가 추가되었겠죠? 다음은 HomeController의 추가된 Update 액션메쏘드입니다.

public ActionResult Update(TelDir telInfo, FormCollection formCollection)
{
    var operation = formCollection["oper"];
    if (operation.Equals("add"))
    {
       TelDir telData = new TelDir();
       telData.name = telInfo.name;
       telData.phone = telInfo.phone;
       telData.speedDial = telInfo.speedDial;
       telData.email = telInfo.email;
       _db.AddToTelDirSet(telData);
       _db.SaveChanges();
    }
    else if (operation.Equals("edit"))
    {
       var telData = _db.TelDirSet.First(m => m.dirId == telInfo.dirId);
       telData.name = telInfo.name;
       telData.phone = telInfo.phone;
       telData.speedDial = telInfo.speedDial;
       telData.email = telInfo.email;
       _db.SaveChanges();
    }
    else if (operation.Equals("del"))
    {
       var telData = _db.TelDirSet.First(m => m.dirId == telInfo.dirId);
       _db.DeleteObject(telData);
       _db.SaveChanges();
    }
    return Content("ok");
}

각 요청(add,edit,del) 대로 분기하여 처리하도록 하였습니다.



이제야 보는구나


실행을 해서 결과화면을 보면


멋드러지게 지난시간과는 다른 모습의 grid 가 있는 것을 확인할 수 있습니다. 추가,편집,삭제 버튼이 들어가 있고 옆에 리프레쉬 버튼도 있네요.

추가 버튼을 누르면


깔끔한 박스가 뜨네요. 전 슈퍼맨을 등록해보도록 하겠습니다. 값을 입력하지 않고 그냥 Submit 버튼을 살짝 눌러보시면 이름 입력하라고, 전화번호 입력하라고 아우성일겁니다. editrules: { required: true } 이것때문이죠. 매치된 메시지는 언어 스크립트에 있는 required:"Field is required" 이고요. 값을 정상적으로 입력후에 Submit을 하면...
바로바로 처리가 가능하네요^^ 모달창이 닫히는 부분은 위에서 설명드렸던 closeAfterAdd: true 때문인거죠. 편집하는 부분도 해볼까요? closeAfterEdit: truefalse로 하여 테스트해보세요. 모달창이 닫히지 않고 수정한 값들이 화면의 울렁거림 없이 처리가 되는 것을 확인하실 수 있습니다.

편집하는 부분을 테스트 하시려면 먼저 수정하려는 해당 로우(row)를 선택하신 후 편집 버튼을 클릭하시거나, 해당 로우를 더블클릭하시면 됩니다. 해보시죠?^^;


마지막으로 정말 삭제하실거에요? 를 본후 노는 시간을 마치도록 하겠습니다.




마무리요


정말 jquery 관련 플러그인들은 참 편리하네요. 이렇게 손쉽게 처리가 가능하니 말이죠.
암튼, 이번시간으로 jqGrid는 마치겠습니다. 뭐 한것도 없이 마친다고 하니 웃기네~ 라고 말씀을 해주셔도 마칠겁니다.^^;

아직은 무덥고, 다양한 날씨속에서 지내는 지금, 건강 꼭 챙기세요. 감사합니다^^

참고자료 : http://elijahmanor.com/webdevdotnet/post/jQuery-jqGrid-Plugin-Add-Edit-Delete-with-ASPNET-MVC.aspx

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 / Twitter 스타일 방명록 만들기 #1

Cloud 2010. 7. 27. 09:00 Posted by 알 수 없는 사용자

꼭 읽어주세요: 이 글이 작성된 현 시점에 가장 최신 버전의 Azure Tools는 버전 1.2입니다. 이 강좌를 시작하시기 전에 Windows Azure Tools for Visual Studio를 1.2 버전으로 업그레이드하여 주십시오. 이전 버전을 설치하신 경우에는 SDK와 Tools를 모두 완전히 제거한 후 1.2 버전으로 새로 설치하여 주십시오. Windows Azure Tools for Visual Studio 1.2 한글판 다운로드는 http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=2274a0a8-5d37-4eac-b50a-e197dc340f6f 에서 가능합니다.

지난번 글 (2010/06/07 - [Cloud Development] - Hello Windows Azure / Understanding Windows Azure Development Process)에 이어서, 오늘부터는 Twitter 스타일 방명록 만들기 첫 번째 시간입니다. 이번 시간에는 Windows Azure 프로젝트를 만들고, 데이터 모델을 작성하고 파악하는 것을 실습 목표로 정의하고자 합니다.

빠르고 편리한 실습을 위하여, Visual Web Developer 2010 Express를 사용하여 실습하는 것을 기준으로 하겠습니다. Visual Studio 2010이 이미 설치되어있으신 경우 이를 이용하셔도 됩니다. 어떤 개발 도구를 사용하더라도 반드시 Windows Azure Tools for Visual Studio가 설치되어있어야 하며 설치 방법은 2010/06/03 - [Cloud Development] - Hello Windows Azure / Windows Azure 개발 환경의 구축 에서 소개하는 내용에 따라 완료하여 주시기 바랍니다.

개발 도구 시작하기 및 프로젝트 생성하기

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 프로젝트 항목을 선택하면 연필 모양의 아이콘이 이름 옆에 나타납니다. 이를 클릭하면 아래와 같이 이름을 바꿀 수 있도록 편집 영역이 나타납니다. 이 예제에서는 다음과 같이 이름을 정하였습니다.

- MvcWebRole1 => TwistBook.WebRole
- WorkerRole1 => TwistBook.LinkProcessor

이름을 편집하고 나면 아래와 같은 화면이 되어있을 것입니다. 확인 버튼을 눌러 프로젝트를 생성합니다.

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 키를 누른채로 하나씩 클릭하면, 아래 그림과 같이 여러 대상을 선택하고 참조로 추가할 수 있습니다.

* System.Data.Services.Client
* Microsoft.WindowsAzure.StorageClient

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]

namespace TwistBook.DataModel
{
    internal class TwistDataServiceContext : TableServiceContext [각주:7]
    {
        internal TwistDataServiceContext(CloudStorageAccount account)
            : base(account.TableEndpoint.AbsoluteUri, account.Credentials) [각주:8]
        {
        }

        internal const string TwistModelName = "TwistModel";

        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]

           CloudTableClient.CreateTablesFromModel(
                typeof(TwistDataServiceContext),
                storageAccount.TableEndpoint.AbsoluteUri,
                storageAccount.Credentials); [각주:13]

        }

        public TwistDataSource()
        {
            this.serviceContext = new TwistDataServiceContext(storageAccount); [각주:14]
            this.serviceContext.RetryPolicy = RetryPolicies.Retry(
                3, TimeSpan.FromSeconds(1)); [각주:15]

        }

        public DataServiceResponse Insert(TwistModel model)
        {
            this.serviceContext.AddObject(
                TwistDataServiceContext.TwistModelName,
                model); [각주:16]

            return this.serviceContext.SaveChanges(); [각주:17]
        }

        public IEnumerable<TwistModel> Select()
        {
            var results = from eachTwist in this.serviceContext.TwistTable
                          select eachTwist; [각주:18]

            var query = new CloudTableQuery<TwistModel>(
                results as DataServiceQuery<TwistModel>,
                RetryPolicies.Retry(3, TimeSpan.FromSeconds(1))); [각주:19]

            return query.Execute(); [각주:20]
        }

        public DataServiceResponse Delete(TwistModel model)
        {
            this.serviceContext.AttachTo(
                TwistDataServiceContext.TwistModelName,
                model, "*"); [각주:21]

            this.serviceContext.DeleteObject(model);
            return this.serviceContext.SaveChanges();
        }
    }
}

Preface: ASP.NET MVC 2 Web Role에 대한 이해

다음 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)을 통하여 항상 받고 있습니다. 언제든 의견 주시면 감사하겠습니다. :-)

  1. 이 샘플에서 Windows Azure Storage를 이용하는 방향으로 설명이 되어있지만, 실제로 여러분이 개발할 Windows Azure 서비스에서는 SQL Azure나 다른 곳에 배치되어있을 고가용성의 관계형 데이터베이스 시스템 (예: SQL Server 2008 R2)을 이용하는 것이 더 좋을 수 있습니다. [본문으로]
  2. Windows Azure SDK와 함께 제공되는 Table Storage를 위한 API가 포함되어있는 네임스페이스입니다. [본문으로]
  3. TableServiceEntity 클래스를 상속받도록 자료 구조를 만들어야 SDK를 이용하여 Table Storage에 데이터를 저장하거나 가져올 수 있습니다. [본문으로]
  4. TableServiceEntity 클래스의 기능을 적용하기 위하여 동일한 시그니처를 가진 생성자를 하나 만듭니다. Table Storage의 접근 효율성을 위하여, 테이블은 여러 개의 파티션으로 구분됩니다. 이 샘플에서 파티션의 분리 단위로 "날짜"를 사용하였습니다. 그리고 파티션 내에서 각각의 Entity가 고유한 의미를 가질 수 있게 하기 위하여 Row Key를 사용하여 구분합니다. Partiton Key와 Row Key가 더해져서 테이블 내에서는 이 Entity가 "유일할 수 있다"는 특성을 보장합니다. [본문으로]
  5. WriterName Property는 작성자의 이름, MessageBody Property는 메시지 본문, WrittenDate Property는 작성한 날짜와 시간을, ImageUrl Property는 같이 첨부하는 사진의 URL을 보관하는 목적으로 사용됩니다. C# 3.0 이후로 지원되는 단축 Property Getter/Setter 선언으로 별도의 private 멤버 변수를 배치하지 않고 이와 같이 단순한 코드를 만들 수 있습니다. [본문으로]
  6. Windows Azure SDK와 함께 제공되는 Table Storage를 위한 API가 포함되어있는 네임스페이스입니다. [본문으로]
  7. LINQ를 이용하여 손쉽게 데이터를 가져오거나 설정할 수 있도록 LINQ의 설정을 확장해주는 기본 추상 클래스입니다. [본문으로]
  8. CSCFG 파일 상의 정보를 표현하는 객체인 CloudStorageAccount를 생성자에서 인자로 받아 이 객체를 초기화하고, Windows Azure Storage와의 연결을 초기화합니다. [본문으로]
  9. 지연 실행을 목적으로 하는 질의 객체를 생성합니다. 지연 실행이란, 각각의 요소를 다룰 필요가 있을 때 식을 계산하고 평가하는 방식으로, 전체의 내용을 미리 메모리에 읽어들여서 처리하는 것과는 차이가 있습니다. 이 객체는 앞서 우리가 정의한 TwistModel 클래스를 트랜잭션의 단위로 사용하도록 SDK 내의 프레임워크에서 생성됩니다. [본문으로]
  10. 주의: 엔티티 클래스의 이름, ServiceContext에서 노출하는 프로퍼티의 이름, CreateQuery 메서드에 전달하는 테이블 명의 이름이 모두 같아야 혼선없이 올바르게 동작할 수 있음을 보증할 수 있습니다. [본문으로]
  11. LINQ to Azure Table Storage를 활용하는데에 필요한 클래스 및 원격 데이터 액세스에 필요한 클래스들이 굵게 강조 표시한 3개의 네임스페이스 안에 모두 포함되어있습니다. [본문으로]
  12. 현재는 실제 Windows Azure Storage 계정을 지정하지 않고 Local Development Storage 계정을 대신 지정합니다. [본문으로]
  13. 테이블 모델을 생성할 때 사용할 기준이 될 DataContext 클래스를 선택하고, 생성을 요청합니다. 이 때 접속할 대상 스토리지의 HTTP 주소와 자격 증명 정보도 한꺼번에 지정합니다. 또한, 이 작업은 TwistDataSource 클래스를 프레임워크에서 로드할 때 한 번만 발생할 수 있도록 유도하기 위하여 정적 생성자에 정의하였습니다. 테이블의 초기 구조를 할당하는 작업은 자주 일어날 필요가 없는 작업이기 때문에 성능 상의 이득을 위하여 이와 같이 작성합니다. [본문으로]
  14. 서비스 객체를 초기화합니다. 앞에서 가져온 계정 정보를 사용하여 복원된 계정 정보 객체를 사용하여 초기화하고 있다는 점을 확인하십시오. [본문으로]
  15. 혹시 있을지 모르는 장애에 대해 좀 더 완벽한 대비를 위하여, 재시도 정책을 설정할 수 있습니다. (이 부분은 매우 중요한 개념입니다.) 첫 번째 인자에는 재시도 횟수, 그리고 두 번째 인자에는 재시도 간격을 TimeSpan 객체를 이용하여 지정할 수 있습니다. 여기서는 처음 실패가 발생한 시점을 기준으로 3회 더 시도하며 각 시도 간격은 1초로 정합니다. [본문으로]
  16. 전형적인 LINQ to Entity 서비스와 마찬가지로 AddObject를 이용하여 객체의 참조를 기반으로 새로운 데이터를 추가할 수 있습니다. [본문으로]
  17. 트랜잭션 개념을 기본적으로 사용하므로 SaveChanges 메서드는 삽입, 변경, 삭제 작업이 있은 직후에는 반드시 병행되어야 합니다. [본문으로]
  18. Windows Azure Table Storage에 전송할 Query를 지연된 실행을 위한 객체로 초기화합니다. 이 문장이 실행되었다고해서 곧바로 데이터가 수집되는 것은 아닙니다. [본문으로]
  19. Windows Azure Table Storage에 실제로 Query를 전송할 클라이언트 객체를 초기화하고, 이 객체의 재시도 정책도 추가로 정의할 수 있습니다. 여기서도 3회 재시도, 매 시도마다 1초 간격을 두기로 설정합니다. [본문으로]
  20. 비로소 이 부분에서야 실제 전송과 데이터 수집이 발생하게 됩니다. [본문으로]
  21. Table Storage에서 객체를 삭제할 때에는 삭제를 위하여 검색할 대상을 지정해야 하며 이 때 사용하는 것이 AttachTo 메서드입니다. 이 메서드를 이용하여 어떤 테이블에서 어떠한 유형의 데이터를 검색할 것인지를 서비스 객체에 지정합니다. [본문으로]
  22. Plain Old CLR Object (Plain Old Java Object; POJO를 응용한 줄임말) [본문으로]
  23. Plain Old .NET Framework Object (Plain Old Java Object; POJO를 응용한 줄임말, POCO와 동일한 의미의 다른말) [본문으로]
  24. 이 글을 작성하는 시점에서 ASP.NET MVC 3에서 기본으로 채택될 예정인 Razor View Engine이 새로 발표되었습니다. Razor View Engine은 Web Form을 대체하는 ASP.NET MVC 전용의 View Engine 시스템을 뜻합니다. [본문으로]

Razor in WebMatrix

ASP.NET 4.0 2010. 7. 15. 22:28 Posted by 알 수 없는 사용자

지난 7월 2일경 스캇구스리 옹의 블로그에서는 Razor 라는 새로운 view 엔진이 소개되었습니다. Razor 는 기존에 <%%> 으로 통칭되던 코드 블록을 좀더 간소화 시키기 위해 고안되었으며 , 스파케티 코드의 늪에서 허덕이는 개발자를 위해서 새로 고안되었습니다. 특히 이 코드블록은 기존에 미칠듯이 복잡하고 어려웠던 asp.net 인라인 코드를 좀더 html스럽게 보여줄수 있게되었다는 것에서 차별화 됩니다


스캇옹의 블로그 에서 Razor를 감상해BoA요



 


[기존 <%%> 코드블록으로 구성된 코드]

 


[Razor 스타일로 재구성된 코드]

 

현재 우리가 이 Razor를 테스트 해볼수 있는 환경은 크게 두가지로 나뉠수 있습니다.


첫번째는 WebMatrix 를 설치하고 , 그 안에서 CSHtml 파일을 직접 작성해보는것이고 ,

두번째는 ASP.NET 프로젝트에서 Microsoft.WebPages.dll 을 참조하는 것 입니다.

 

애석하게도 아직 Visual studio 에서는 Razor 관련 인텔리센스를 지원하고 있지 않으나 , 스캇 블로그에서 이미 인텔리센스 데모를 선보이고 있기 때문에 빠른 시일내에 , 관련 자료가 배포될것으로 보입니다. 이와 관련된 내용은 발표되는대로바로 알려드리도록 하겠습니다.

 



Razor in WebMatrix



현재 가장 빠르게 Razor 를 접해보는 방법은 WebMatrix 를 설치하는 것 입니다. 설치에 대한 부분은 준서아빠님이 소개해주셨으니 간략하게 넘어가도록 하겠습니다. 설치에 대해서는 WebMatrix 설치부터 HelloWorld까지 를 참조해 주시기 바랍니다.


WebMatrix 를 설치하게 되면 처음에 Website1 이라는 페이지가 생성되어 있는 것을 알수 있습니다. Razor 를 간단하게 살펴볼수 있는 데모프로젝트라고 이해하시면 될거 같습니다. ^^



WebSite1을 더블클릭하거나 선택하고 Ok 버튼을 클릭합니다.




프로젝트를 로드하고 나면 한눈에 웹 페이지의 상태를 볼수 있는 화면이 나옵니다. Url 과 소스코드가 있는 폴더가 나오고 있네요. 일단은 Run 버튼을 눌러 사이트를 구동시켜보도록 하겠습니다





깔끔한 웹 페이지가 나오네요
. 이 페이지는 Razor 과 간단한 html 로 구성되어 있습니다. 그럼 한번 코드를 보도록 하겠습니다.




 

Files를 클릭하면 현재 구동중인 웹 사이트 프로젝트를 수정할수 있습니다. 이곳에서 우리는 default.cshtml 을 살펴보도록 하겠습니다.

 

<div class="twitter">

<div class="twitterfeed">

@Twitter.Search("@vsts2010", caption: "#helloworld", width: 740, height: 150)

       </div>

</div>

 

해당 파일에서는 Twitter 위젯을 Razor 코드로 처리하고 있는 것을 확인할수 있습니다. Twitter 객체는 WebMatrix엔진에서 제공되는것으로 보입니다만 , 특별히 선언된 부분이나 참조되는 dll 찾는 것은 조금 힘들어보였습니다. 또한 특정 벤더의 API 사용 하는 부분은 효용성이 떨어지므로 , 데모 이외의 용도로 사용시 문제가 될수 있을것으로 보입니다.

해당 객체는 WebMatrix 에서 제공되는것이 맞네요. 많은 흥미로운 WebMatrix Helper 들을 확인해보세요 이부분에 대해서도 따로 한번 다루도록 하겠습니다 ^^



페이지에서는 Twitter 검색어를 조회해서 보여주는 위젯을 선보여주고 있습니다만, 저는 이것보다는 트윗을 노출시키고자 합니다. 이를 위해 기존 코드를 간단하게 주석처리하고 트윗을 노출시켜보도록 하겠습니다.

 


<div class="twitterfeed">              

@//Twitter.Search("@vsts2010", caption: "#helloworld", width: 740, height: 150)

@Twitter.Profile("@vsts2010")

</div>


이제 어플리케이션을 다시 구동시키면!




의도했던 트윗이 노출되는 것을 확인할수 있습니다. ^^

 

Summary


이번 글에서는 WebMatrix 에 포함된 기본 프로젝트에서 Razor 를 사용하는 부분과 함께 , 외부 함수를 끌어오고 기존의 프로그래밍 경험을 접목시켜보는 것을 보여드렸습니다. 다음 글에서는 외부 함수를 직접 구현하고 그 함수를 사용하는 방법에 대해 알아보도록 하겠습니다.

안녕하세요. 지난 시간에는 jqGrid를 이용해서 리스트를 구현해봤습니다. 정말 맛보기였죠? :)
이번 시간은 실제 데이터베이스에서 데이터 조회, 페이징과 정렬부분을 다루도록 하겠습니다.

먼저 데이터베이스 생성

테이블 구조는 다음과 같습니다.

 컬럼명  데이터 타입
 dirId  int
 name  nvarchar(50)
 phone  nvarchar(50)
 email  nvarchar(50)
 speedDial  decimal(2,0)

그냥 기본세팅이죠^^;

엔터티 모델 클래스를 생성할 건데요, 자세히(?)를 원하신다면 이전 포스팅을 참고해주세요^^;
완료가 되면,


여기까지 잘 오셨죠? 저는 Entity Set Name을 TelDir에서 TelDirSet으로 변경하였습니다. 헷갈려서요^^;;

자. 이제는 본격적(?)으로 살펴볼까요? (어째.. 오늘도 맛보기일것 같은 분위기가 물~씬 풍기시죠? ㅡ,.ㅡ;)

페이징 기능을 달자

지난 뷰페이지에 pager란 id로 div 태그를 추가하겠습니다.

<div id="pager" class="scroll" style="text-align:center;"></div>

테이블 뒤에 추가하시면 됩니다.
그리고, 스크립트 부분도 수정해야겠죠?

    <script src="/Scripts/grid.locale-en.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#list").jqGrid({
                url: '<%= Url.Action("EntityGridData", "Home") %>',
                datatype: 'json',
                mtype: 'POST',
                colNames: ['No', '이름', '전화번호', '이메일', '단축다이얼'],
                colModel: [
                  { name: 'dirId', index: 'dirId', width: 40, align: 'center' },
                  { name: 'name', index: 'name', width: 100, align: 'left' },
                  { name: 'phone', index: 'phone', width: 150, align: 'left' },
                  { name: 'email', index: 'email', width: 250, align: 'left' },
                  { name: 'speedDial', index: 'speedDial', width: 100, align: 'center'}],
                    pager: $('#pager'),
                emptyrecords: "Nothing to display",            
                rowNum: 3,
                rowList: [3, 10, 20, 50],
                sortname: 'dirId',
                sortorder: "desc",
                viewrecords: true,

                caption: '전화번호부'
            });
        });
    </script>

추가된 부분은 굵은글씨로 표시하였습니다. 일단, grid.locale-en.js를 추가해야되더라고요^^; 디폴트로 그냥 jqGrid 스크립트를 넣을때 추가하라고 하였는데, 제가 지난 포스팅때는 빠뜨렸죠.
이런 언어 스크립트 파일에는 페이징 관련한 디폴트 값들이 들어가 있습니다.

defaults:{
   recordtext:"View {0} - {1} of {2}",
   emptyrecords:"No records to view",
   loadtext:"Loading...",
   pgtext:"Page {0} of {1}"
  }


나머지 프로퍼티에 대한 설명을 드리자면,
pager는 위 이미지 보이시죠? ^^; 저렇게 레코드들을 이동할수 있게 해주는 페이징 바를 정의합니다.
저같은 경우는 $('#pager')로 jQuery 표현을 썼는데요, jqGrid의 wiki를 보니 '#pager', 'pager', jQuery('#pager') 세가지 경우가 모두 가능한데요. 앞에 두가지 방법을 추천한다네요. 흠. jQuery 변수가 내보내기, 가져오기 모듈을 이용할때 문제를 발생시킬수 있다고 합니다. 이 부분은 차츰(?) 찾아보도록 하죠;;

The definition of the pager in the grid can be done this way:pager : '#gridpager', pager : 'gridpager' or pager : jQuery('#gridpager'). All the three methods are valid, but I recommend to use the first or second one, since the jQuery variant causes problems when we try to use Exporting and Importing modules.

emptyrecords는 말 그대로 데이터가 없을 때 표현할 문구를 나타내고요,
rowNum은 페이지에서 보여줄 레코드 갯수,
rowList는 페이지 갯수를 선택할 수 있도록 하는 셀렉트박스의 옵션들,
sortname, sortorder는 각각 정렬할 컬럼과 정렬방식(오름차순, 내림차순),
viewrecords는 토탈 레코드의 수(위 이미지에서 View 1 -3 of 5)를 표현하는 것을 허용할 것인지 여부를 나타냅니다.

이제 뷰페이지는 완성이 되었고요, 컨트롤러 손봐야겠죠?
EntityGridData() 라는 이름의 액션메쏘드를 추가하겠습니다.

[HttpPost]
        public ActionResult EntityGridData(string sidx, string sord, int page, int rows)
        {
            // 데이터베이스 연결
            MvcDbEntities _db = new MvcDbEntities();

            // 페이징 변수 세팅
            int pageIndex = Convert.ToInt32(page) - 1;
            int pageSize = rows;    // 3
            int totalRecords = _db.TelDirSet.Count();
            int totalPages = (int)Math.Ceiling((float)totalRecords / (float)pageSize);

            // 데이터 조회(페이징&정렬)
            // sidx : dirId
            // sord : desc
            var dirs = _db.TelDirSet
                .OrderBy("it." + sidx + " " + sord)
                .Skip(pageIndex * pageSize)
                .Take(pageSize)
                .ToList();

            var jsonData = new
            {
                total = totalPages,
                page = page,
                records = totalRecords,
                rows = (
                  from dir in dirs
                  select new
                  {
                      i = dir.dirId,
                      cell = new string[] {
                          dir.dirId.ToString(), dir.name.ToString(), dir.phone.ToString(), dir.email.ToString(), dir.speedDial.ToString()                         
                      }
                  }).ToArray()
            };
            return Json(jsonData);
        }

궁금해 보이는 것이 없죠? ㅎㅎ
jqGrid가 EntityGridData를 호출할때 파라미터(sidx : dirId, sord : desc, page : 1, rows : 3)를 날립니다~~~
실행을 해보면,


너무 간단하게 페이징 기능이 완성되었습니다^^
네이게이션 기능 되고요~ 셀렉트박스로 로우 갯수 선택 기능 되고요~ No탭 클릭하시면 정렬 기능 됩니다요~

마무리요

실행화면 출력하고 보니 아직도 맛!보!기! 인것을 보면 아직 한참 멀은 듯 합니다.
더 알찬 정보로 준비하도록 하겠습니다^^
감사합니다. 


참고자료 :
http://haacked.com/archive/2009/04/14/using-jquery-grid-with-asp.net-mvc.aspx
http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pager&s[]=paging&s[]=properties

ASP.NET 의 WebMatrix & Razor 신 기술 소개

------------- 2010. 7. 7. 17:09 Posted by POWERUMC

오늘 Microsoft 의 Scottgu's Blog 를 통해 WebMatrix & Razor 기술이 소개되었습니다.    

우선 간단하게 용어를 정리해봅시다.

WebMatrix

경량화한 웹 개발 도구

Razor

ASP.NET 의 뷰(View) 엔진

즉, WebMatrix&Razor 는 빠르게 웹 개발 환경을 구성하고 Razor 의 뷰(View) 엔진을 이용하여 신속하게 웹 페이지를 개발하고자 합니다. 웹 환경/웹 개발/데이터베이스/웹 개발 도구 등 WebMatrix&Razor 에 모두 포함이 되어 있습니다.

아마도 처음 웹 개발에 접하시는 분들이 처음 갖는 고민은..? 웹 환경 구성/웹 프로토콜 및 통신의 이해/호스팅 등 복잡했던 초기 작업을 매우 효과적이고 간소화하여 신속하게 작업을 진행할 수 있는 장점이 있습니다.

   

WebMatrix 란 무엇인가요?

WebMatrix 는 약 15MB 의 용량으로 빠르게 웹 개발 환경을 갖출 수 있습니다. (단, .NET Framework 4.0 이 인스톨 되지 않았을 경우 약 50MB). 현재 WebMatrix 는 Beta 버전이며 이 링크를 클릭하시면 다운로드 받으실 수 있습니다.

이 웹 WebMatrix 에는 다음의 구성 요소가 포함이 되어 있습니다.

  • IIS Express
  • SQL Compact Edition
  • ASP.NET Extensions

그리고 Visual Studio 2010 또는 Visual Web Development 2010 Express 개발 도구를 이용하여 웹 개발 또는 커스터마이징을 하실 수 있습니다.

   

WebMatrix 는 쉽게 사용할 수 있게 설계가 되었습니다.

초기 웹 개발 환경은 웹 페이지를 만들기 위해 해야 할 일들이 많았습니다. 환경 구성/개발 환경 구성 등 말이죠.

WebMatrix 는 아래와 같이 매우 심플한 디자인으로 웹 개발의 시작을 빠르고 쉽게 수행할 수 있습니다. Site from Web Gallery 는 오픈 소스 웹 어플리케이션을 바로 설치하여 사용할 수 있고, Site From Template 으로 최적화된 환경에서 개발을 시작할 수 도 있습니다.

Site From Web Gallery 는 온라인에서 인기 있는 다양한 오픈 소스 웹 어플리케이션이 포함되어 있답니다. ASP.NET, PHP 의 오픈 소스 웹 어플리케이션이 포함되어 있으며, 설치나 배포가 매우 간단합니다.


그 중, Scott 님은 BlogEngine.NET 으로 예제를 보여주시네요. BlogEngine.NET 는 이미 예전부터 CodePlex 를 통해 오픈 소스로 공개가 되고 현재도 인기 있는 블로그 웹 어플리케이션입니다.

BlogEngine.NET 을 선택하고 Next 버튼을 클릭하면 이것을 설치하기 위한 컴포넌트를 체크하거나 다운로드 받습니다. 즉, 원클릭(One-Click) 으로 어플리케이션이 동작하기 위한 모든 환경을 스스로 구성한다는 얘기죠^^

PHP 어플리케이션의 경우 WordPress, Drupal, Joomla, Sugar CRM 등은 MySQL 이 필요한데, 개별적인 설치 없이 이런 환경도 자동으로 다운로드 받아 설치를 합니다.

간단하게 소프트웨어 사용권 동의(EULA) 를 클릭하면 바로 설치와 구성 작업을 시작합니다.

   

그리고 금새 설치와 구성이 완료가 되었지요^^

   

모든 구성이 완료되면, 아래와 같은 Overview 페이지가 나타납니다.

   

설치된 BlogEngine.NET 을 실행하기 위해서 아래의 Run 버튼을 클릭합니다. 인터넷 익스플로러, 크롬, 파이어폭스로 실행할 수 있고, Open in all browsers 로 여러 브라우저로 한꺼번에 실행할 수 있습니다.

   

자! 여태까지 클릭 클릭만 했는데, 아래와 같이 BlogEngine.NET 이 설치되고, 구성되고, 모든 구성이 완료가 됩니다.

   

초기 관리자 아이디와 비밀번호는 admin/admin 입니다. 관리자로 로그인 하셔서 블로그의 이름을 달아주시고, 블로그 저자 소개 등을 입력해 주시면, 곧바로 블로그를 운영을 준비하셔도 될 것 같습니다.^^

WebMatrix 는 오픈 소스 웹 어플리케이션을 커스터마이징 할 수 있는 약간의 개발 환경도 제공해 줍니다. 아래와 같이 소스 코드를 변경할 수 도 있고, Files 버튼으로 파일을 편집하거나 추가, 삭제를 할 수 있습니다.

   

이제 블로그를 운영하기 위해 배포와 호스팅을 해야 합니다.

WebMatrix 의 특징은 매우 경량화되었고 작업 환경이 통합된 장점을 갖습니다. 로컬 또는 원격 웹 서버로 배포할 때, FTP 또는 FTP/SSL 또는 Microsoft Web Deploy(MSDeploy) 를 이용하여 쉽게 배포가 가능합니다.

아래의 Publish 버튼을 클릭하면 배포 준비가 시작됩니다.

   

배포 세팅 화면은 아래와 같습니다. 서버의 기본 정보를 입력하고, 데이터베이스의 연결 문자열을 입력한 후에, Publish 버튼을 클릭합니다.

   

모든 준비가 완료되었고, Publish 버튼을 누르면 이제 배포를 시작할 준비가 완료 되었습니다.

   

이하.. 금일 Microsoft Korea 의 김대우 과장님께서 진행하는 WebMatrix&Razor 세미나에 참석하기 위해 이만 줄입니다. 다른 분들께서 더 알차고 재미있게 포스팅 해 주시리라^^

안녕하세요. 늦바람이 무섭다고 하는데요. jQuery를 향한 늦바람이 불어주길 바라는 1인입니다. ㅎㅎ

이렇게 간단해도 되는겨?

이번 포스팅을 준비하면서 정말 jQuery의 놀라운 힘에 다시 한번 놀랐습니다. 이렇게 간단히 탭메뉴를 넣는게 가능했던건가요?

준비물 준비

먼저, jQueryUI 사이트에서  jquery-ui-1.8.2.custom.zip 파일을 다운받습니다. 압축을 푸시면 jquery-ui-1.8.2.custom.min.js 와 jquery-ui-1.8.2.custom.css 파일이 있습니다.(각각 js폴더와 css폴더에 있습니다.) 이 두 파일을 프로젝트의 Content와 Scripts 폴더에 추가시킵니다. 이제 준비는 됐고요.

준비끝! 예제로!

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">
        $(function () {
            $("#tabs").tabs();
        });
    </script>
    <div>
        <div id="tabs">
        <ul>
            <li><%: Html.ActionLink("홈", "Index", "Product")%></li>
            <li><%: Html.ActionLink("제품", "List", "Product")%></li>
            <li><%: Html.ActionLink("연락", "Contact", "Product")%></li>
        </ul>
        </div>
    </div>
</asp:Content>

$("#tabs").tabs() 이게 바로 그 놀라운 능력을 가진 탭메뉴를 가능케하는 힘입니다. (자세한 것은 다음으로 미루고~ 언제가 될지는 몰라요. 그냥 잘 쓰면 되는거죠^^;;)
아. 추가시켰던 두 파일은 마스터페이지에 끌어다놨습니다.


이제 Html.ActionLink() 에 걸린 액션들만 만들어 주면 됩니다.


요청 컨트롤러입니다. 첫 Index() 액션 메쏘드를 보시면 dynamic 이라는 타입이 보이는데요. 처음에는 Contact()와 마찬가지로 PartialView()만 리턴을 하였는데, 파샬뷰를 생성해 놓고 보니.


저렇게 dynamic 이 눈에 딱 띄는바람에 어쩔수(?) 없이 dynamic데이터를 전달하게 되었습니다. 간단히 설명드리면 dynamic은 대인배의 마음 씀씀이를 갖고 있어서 어떤 타입이던지 모두 수용합니다.(컴파일타임에는 터치를 안합니다. 귀찮아서 런타임한테 넘기는거죠;;) dynamic으로 선언된 변수에 멤버, 메쏘드, string, int 가리지 말고 막 넣어주세요. 다 받아줍니다. 하.. 저도 대인배로 살아가야 할텐데 참.. 아쉽습니다 :)
다음 기회에 dynamic에 대해 좀더 자세히 알아보면 좋겠네요.

다시 본론으로 넘어와서, /Product/Index.ascx 를 보시면

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<h3><%: Model.Message %></h3>
<p>
    ASP.NET MVC에 대한 자세한 내용을 보려면 <a href="http://asp.net/mvc" title="ASP.NET MVC 웹 사이트">http://asp.net/mvc</a>를 방문하십시오.
</p>

Model객체의 Message의 접근하면(Model.Message) Index 메쏘드에서 넘겨준 다이나믹한 메시지를 받을 수 있습니다.

두번째, /Product/List.ascx는

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<MvcWithjQuery.Models.Product>>" %>
<table width="400px">
<tr>
    <th>제품명</th>
    <th>가격</th>
</tr>
<% foreach (var product in Model) { %>
<tr>
    <td><%: product.Name %></td>
    <td><%: product.Price %></td>         
</tr>
<% } %>
</table>

너무 간단해서 할말을 잃게 만들죠. foreach문을 통해 루프를 돌면서 데이터를 출력합니다.
나머지 Contact.ascx는 안보셔도 됩니다.^^;

자, 완료가 되었으니 확인을 해보죠.


페이지 로드 없이 깔끔하게 탭기능이 완성되었습니다.

마무리요

일반적인 페이지(aspx)도 탭메뉴로 가능합니다. 파샬뷰를 사용한 것은 한 페이지 전체보다 불필요한 부분을 제거한(head, html, body가 보시다시피 파샬뷰에는 존재하지 않습니다.) 간결함때문이랄까요? ㅎㅎ
다음은 더 재미있는 것으로 찾아뵙겠습니다. (지금은 재밌다는겨? 뭐여? ㅡ.ㅡ 이렇게 생각하시는 분은 제발 없으시길 바래요^^)

참고자료 :  http://www.kevgriffin.com/blog/index.php/2010/02/23/using-jquery-tabs-and-asp-net-mvc-partial-views-for-ajax-goodness/
지금 잠을 자면 꿈을 꿀 수 있지만, 잠을 자지 않으면 꿈을 이룰 수 있다고 하죠. 그래서 이렇게 눈꺼풀이 내려오는데도 버티고 있는가 봅니다^^; 이 글을 읽고 있는 분들도 꿈을 위해 노력하고 계신거겠죠?

귀 따갑다 jQuery

고마해라~ 마이 들었다 아이가~. 너무 들어서 지겨울 만큼의 jQuery. 이제 시작합니다. 이렇게 늦게 jQuery를 들쳐보는 저를 용서하시고, 격려의 한말씀 해주시면 정말 감사하겠습니다. :)
그런데, jQuery 정말 다들 아시는거죠?

jQuery가 뭔데?

아직도 jQuery를 모른단 말인가?! 자네 정말 웹 관련 일하는 사람이 맞긴 맞는건가? 어헣. 이런 말이 오고가진 않아야 겠죠?

jQuery 공식 홈페이지에는 다음과 같은 말이 떡~하니 있습니다.

jQuery is a new kind of JavaScript Library.
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.

쭉 보면,
jQuery는 자바스크립트 라이브러리의 한 종류입니다. jQuery는 신속한 웹 개발을 위한 HTML 문서 탐색, 이벤트 처리, 애니메이션, Ajax와의 상호작용을 간단하게하는 빠르고 간결한 자바스크립트 라이브러리입니다. jQuery는 자바스크립트 작성 방식의 전환을 위해 설계되었습니다.

다른건 몰라도, 암튼 웹 개발을 빠르게 해준다니까 오케이입니다. 귀찮은 작업도 간결하게 해주는 것 같고요.
Visual Studio 에 jQuery가 탑재되어있는 것도 마이크로소프트가 이를 지원한다는 얘기? 그래서 오케이. 스캇 구쓰리의 블로그를 보시면 계속 jQuery 플러그인 얘기가 올라오고 있는 것으로 봐서 활발하게 개발중인 것 같습니다.

간단 예제

정말 간단한 예제를 한번 살펴보도록 하겠습니다. 실망하시면 안~되요.
지금 하려는 것은 'ASP.NET MVC에 대한 자세한 내용을 보려면...' 이 있는 p 태그의 스타일을 변경해 볼겁니다. 먼저 MVC 프로젝트를 새로 생성하겠습니다. 그 다음, Index.aspx 페이지에 Contents 폴더에 있는 jquery-1.4.1.js 파일을 끌어다 놓습니다. '$(' 입력해보시면 놀랍게도 정말 놀~랍게도 인텔리센스를 지원해주는 것을 확인하실 수 있습니다. 멋지죠? :-)


아. 이거할때가 아닌데 좋아하고 있었네요. 먼저 필요한 스타일을 추가하겠습니다. 간단합니다.


색과 크기변경만 할겁니다.^^;
그 다음 스타일을 변경해줄 소스를 추가하겠습니다.

    <script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {           
            $('#btnStyle1').click(function () {
                $('p').removeClass();
                $('p').addClass('color-yellow').addClass('size-large');
            });
            $('#btnStyle2').click(function () {
                $('p').removeClass();
                $('p').addClass('color-red').addClass('size-small');
            });
        });
    </script>
    <div id="styleChange" style="background-color:Gray; width:300px">
        <h3 style="color: Yellow">스타일을 바꿔요</h3>
        <button id="btnStyle1">스타일_1</button>
        <button id="btnStyle2">스타일_2</button>
    </div>

jQuery 의 경우, 메쏘드 체인이 가능합니다. $().addClass().addClass().removeClass()...
암튼, 너무 간단해서 할 말을 잃으셨다면, 다음을 기대(?)해주세요^^;
실행 결과를 보면 스타일_1 버튼을 클릭했을시,


스타일_2 버튼을 클릭했을시,


참~ 이쁘게(?) 되네요.

마무리요

간단해서 따로 드릴 말씀은 없고요. 바로 다음 글 준비하겠습니다!!!


참고자료 : jQuery 1.3, 조나단 채퍼, 칼 스웨드버그

M, V 그리고 C의 각방생활(6) - 유효성 검사(2)

ASP.NET MVC 2010. 6. 27. 09:00 Posted by 네버덜레스
유효성 검사 안끝난겨?

네. 아직입니다. ^^; 원래는 마무리를 지으려고 했었는데요. 갑자기 jQuery 가 급땡기는 바람에 슬슬 관련글을 적어보렵니다.

클라이언트단에서 유효성검사하기

지난번 포스팅을 보시면, 서버단의 모델 클래스에 DataAnnotaion을 사용하여 유효성검사를 했습니다. 물론, 클라이언트단에서도 자바스크립트를 사용하여 유효성검사를 할 수 있지만, 이는 동일한 유효성 검사를 두번(서버와 클라이언트) 하게됩니다. DRY(Don't Repeat Yourself) 규칙에 위반되는 작업인 거죠.

근데 왜?

저 아시는 분 없죠? 듣보잡인거죠. 그래서 이렇게 앞뒤가 없습니다. 이번 포스팅을 먼저 했으면 하는 마음도 있지만, 뭐 이렇게 된 것 그냥 적어내려갑니다.^^
DRY에 반하는 작업을 한다고 너무 차가운 피드백은 달지 말아주세요; '이런 방법도 있는 거였군'이라는 생각만 가져주셨으면 좋겠습니다.

먼저, 지난번 유효성 검사를 했던 소스에 jQuery를 이용한 유효성 검사 스크립트를 추가하겠습니다.


프로젝트내의 Scripts폴더에 있는 jquery-1.4.1.js와 jquery.validate.js파일을 추가합니다.(프로젝트에 이런 스크립트 파일들이 자동으로 적용되어있는 것으로 봐서는 맘껏 사용하라는 거겠죠?^^; 아. 그리고 미니버전을 사용해도 되는 것은  다들 아시죠? *min.js)

추가된 소스도 함께 보시죠.

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

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <script src="/Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script src="/Scripts/jquery.validate.js" type="text/javascript"></script>   
    <script type="text/javascript">
        $(function () {
            $("form").validate({               
                rules: {
                    "Name": { required: true, maxlength: 5 },
                    "Phone": { required: true },
                    "SpeedDial": { required: true, range: [1, 99] },
                    "Email": { email: true }
                },
                messages: {
                    "Name": "5자 이내로 이름을 입력하시오.",
                    "Phone": " 전화번호를 입력하시오.",
                    "SpeedDial": "1~99까지의 수만 입력하시오.",
                    "Email": "이메일이 형식에 맞지 않습니다."
                }
            });
        });   
    </script>
    <h2>Create</h2>
    <% using (Html.BeginForm()) { %>
    <div>
        이름 : <%= Html.TextBox("Name") %>
    </div>
    <div>
        전화번호 : <%= Html.TextBox("Phone")%>
    </div>
    <div>
        단축다이얼 : <%= Html.TextBox("SpeedDial")%>
    </div>
    <div>
        이메일 : <%= Html.TextBox("Email")%>
    </div>
    <input type="submit" value="Create" />
    <% } %>
</asp:Content>

소스를 보시면(빨간색) jQuery 스크립트와 유효성 검사를 위한 스크립트를 추가하였습니다. 또한 유효성 검사를 담당하는 jQuery 스크립트 구문도 추가하였습니다.

자, $("form").validate() 를 통해 유효성 검사를 합니다. 보시는대로, rulesmessages를 통해 에러를 표시하게되죠. Email을 제외한 각 필드를 필수값으로 세팅을 했고( required: true), 이름은 5자 이내(maxlength :5), 단축다이얼은 1~99까지의 숫자를 받도록(range[1,99]), 이메일은 이메일 형식을 체크(email: true)하도록 하였습니다. 이밖의 옵션들은 여기서 확인하실 수 있습니다.
빈값으로 폼을 전송하려고하면 클라이언트단에서 이에 제재를 가하게 됩니다.


이메일(필수값 아님)을 제외한 나머지는 에러가 났습니다. 올바른 값을 하나하나 입력하면 바로바로 에러메시지가 사라지는 것을 확인할 수 있습니다.


이메일을 잘못입력하면 에러메시지가 뜨는 것도 확인할 수 있습니다.

여기까지 잘 따라오셨으면 보다 싶게 클라이언트단에서의 유효성 검사를 진행해보죠. (윗부분은 이제 잊어도 좋습니다. 딱히 잊으라는게 아닌 아래 소스에서는 필요가 없어서.. 이렇게 말씀드리는건데...음.. '아 이런방법도 있구나'만 기억하시면 됩니다.^^;;)

DRY 잊지말자

지난번 포스팅에서는 서버단에서 유효성 검사를 하였기때문에 유효성 에러 메시지를 보려면 서버단까지 다녀와야할 필요가 있었습니다. 이를 가만히둘 마이크로소프트가 아닙니다. 정말 심플한 방법으로 손쉽게 클라이언트단과 서버단 두군데 모두 유효성검사를 할 수 있도록 하는 단 세줄의 코드가 있습니다.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTest.Models.TelDir>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
    <script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>
    <% Html.EnableClientValidation(); %>
    <h2>Create</h2>
    <% using (Html.BeginForm()) {%>       
        <fieldset>
            <legend>Fields</legend>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Name) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Name) %>
                <%: Html.ValidationMessageFor(model => model.Name) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Phone) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Phone) %>
                <%: Html.ValidationMessageFor(model => model.Phone) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.SpeedDial) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.SpeedDial) %>
                <%: Html.ValidationMessageFor(model => model.SpeedDial) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Email) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Email) %>
                <%: Html.ValidationMessageFor(model => model.Email) %>
            </div>
           
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    <% } %>
</asp:Content>

위 세줄을 추가함으로 지난번 포스팅에서 DataAnnotation을 이용한 유효성 검사 로직을 클라이언트단에서도 사용할수 있게 되었습니다. 실행을 시킨 후, Create 버튼을 클릭하면 리로드없이 즉각적으로 에러메시지를 확인할 수 있습니다.


또한, 유효한 값을 입력하면 즉시 에러메시지가 사라집니다.
저희는 지금 클라이언트단에 유효성 검사 로직을 추가하지 않았습니다. 유효성 검사로직은 모델클래스에만 존재하고 있습니다. 하하하.(승리자의 웃음인거죠^^) 룰은 한 곳에다가 두고, 두군데(클라이언트와 서버)에서 모두 검사를 하도록 하였습니다. 이로써 DRY를 잊지 않은체 작업이 완료되었습니다.

마무리요

이렇게 손쉽게 클라이언트단에서도 검사가 가능한 방법이 있었습니다. ㅎㅎ 기분좋네요.
마이크로소프트는 현재 jQuery 프로젝트에 참여하여 계속 플러그인을 개발중에 있습니다. (이 얘기는 왜하는 걸까요? 음..) 이 부분에 대해서도 포스팅을 하도록 노력해보겠습니다.


참고자료 :
http://docs.jquery.com/Plugins/Validation
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx

M, V 그리고 C의 각방생활(4) - 유효성 검사

ASP.NET MVC 2010. 5. 31. 09:00 Posted by 네버덜레스
안녕하세요. 지난 포스팅에 이어서(넘흐 오랜만이죠^^;) 시작하겠습니다. 아마 다들 잊으셨을 겁니다. 여기까지 했었죠?


_db.SaveChanges() 를 하려 했더니, 에러가 발생했습니다. 자세히 들여다 보니


ID 에 NULL 값을 넣을 수가 없다네요. 이래서 에러가 발생했죠.
아~ 이래서 사용자가 빈 값을 넣으려 하면 막아야하겠구나~ 라는 생각이 번뜩 드셨을겁니다.

유효성 검사!

유효성검사라 하면 필수입력값에는 꼭 데이터를 입력해야하고, 데이터의 타입이나 길이에 맞게 들어오게 체크하는 것을 말하겠죠?

ASP.NET MVC 프레임워크에서는 모델 스테이트(Model State)를 제공합니다. 정확히 말하면 model state dictionary 라고 해서 유효성 에러들을 표시하기 위해 사용됩니다. 유효성 검사중에 해당 프로퍼티에서 fail 이 발생하면 모델 스테이트에 이를 추가합니다. 모델 스테이트에 에러가 있으면 ModelState.IsVaild 는 false를 반환합니다.
여기까지 설명을 드리고, 예제와 함께 보시겠습니다.

예제 만들기

아주 간단한 전화번호를 담는 TelDir 클래스를 만들겠습니다.


DirectoryController 도 추가하겠습니다. 이 컨트롤러에 두개의 Create 액션메쏘드를 만들겠습니다. 하나는 /Directory/Create url 요청시(GET) 호출되는 메쏘드이고, 다른 하나는 POST로 호출되는 메쏘드 입니다. 아시죠?^^
ASP.NET MVC 프레임워크에서는 자동적으로 폼 필드에 값을 해당 모델 속성들과 매핑을 시킵니다. 모델 바인더가 이런 일을 하게되죠. 예제에서 처럼 HTML 폼 필드의 값을 TelDir 객체에 매핑을 시키는데, 에러가 없이 바인딩이 되면 즉, ModelState.IsValid가 true 이면 데이터베이스에 저장을 하는 것이고, 그렇지 않다면, 다시 폼을 그리며 에러를 표시하게됩니다.


뷰도 같이 만들겠습니다. 액션메쏘드에서 오른쪽버튼을 클릭하여 Add View 를 선택하고, 강하게 생성하겠습니다.


추가하기 전에 빌드하는 것 잊지 않으셨죠? 모델 생성 후 빌드를 하지 않으면 View data class 항목에 표시가 되지 않습니다. Add 해서 완료를 하시면 /Views/Directory/Create.aspx 가 생성되었습니다.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTest.Models.TelDir>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Create</h2>
    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>
        <fieldset>
            <legend>Fields</legend>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Name) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Name) %>
                <%: Html.ValidationMessageFor(model => model.Name) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Phone) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Phone) %>
                <%: Html.ValidationMessageFor(model => model.Phone) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.SpeedDial) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.SpeedDial) %>
                <%: Html.ValidationMessageFor(model => model.SpeedDial) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Email) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Email) %>
                <%: Html.ValidationMessageFor(model => model.Email) %>
            </div>
           
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    <% } %>
</asp:Content>

휴. 여기까지 했으니 이제 유효성검사를 해보실까요?
다음과 같이 컨트롤러에서 유효성 검사를 할수 있습니다.


물론, 클라이언트단인 aspx 에서도 할 수 있겠죠. 제가 프로젝트에서 경험해본 유효성검사는 클라이언트단에서 먼저 검사를 하고 혹시나 몰라서, 클라이언트에서의 유효성검사를 신뢰할수 없어서 서버단에서도 한번 더 유효성검사를 했었습니다. 코드가 중복되고 또한 비슷한 UI 에서도 같은 검사를 해야했었죠.
ASP.NET MVC 에서는 이러한 부분을 모두 없애고 모델클래스에서 이를 담당하게 합니다. 심플해지고 개발속도도 향상되죠.

DataAnnotation을 이용한 유효성 검사


위와같이 컨트롤러와 뷰가아닌 모델에 유효성 검사로직을 두게되면, 다른 UI(Edit와 같은) 에서도 따로 유효성 검사를 하지 않고도 동일한 유효성 검사를 할 수 있습니다. 이렇게 함으로써 중복되는 코드를 피할 수 있게되는 거죠. DRY관점에서도 올바른 방향으로 나가는 거겠죠?ㅡ.ㅡ

위 소스를 보시면 유효성 검사를 위한 몇개의 속성들이 눈에 띄실겁니다.  using 문에 System.ComponentModel.DataAnnotations를 추가하면 유효성 검사 속성들을 사용할 수가 있습니다. 
각 필드에 속성들을 추가할 수 있는데요. [Required], [Ragng], [ReqularExpression], [StringLength] 등이 있고 커스텀한 속성도 만들 수가 있습니다.
만약 Name 에 길이제한을 5자로 하고 싶다면 [StringLength(5, ErrorMessage="5자까지만!")] 을 추가만 하시면 됩니다. 모델의 유효성 검사를 추가함으로(컨트롤러와 뷰 수정없이), 이 모델을 사용하는 부분에는 모두 적용이 되는거죠. 참 쉽죠잉?

일단 에러를 내볼까요?


위 에러메시지가 표시되는 것은 Create.aspx 소스를 보시면

<div class="editor-field">
    <%: Html.TextBoxFor(model => model.Name) %>
    <%: Html.ValidationMessageFor(model => model.Name) %>
</div>

<%: Html.ValidationMessageFor() %> 를 보실 수 있습니다. 바로 이것이 ModelState.IsValid 가 false 여서 뷰를 다시 그릴때, 각각의 해당 필드 옆에 붙어서 에러메시지를 보여주는 유효성 검사 헬퍼 메쏘드 입니다.

여기서 마무리

바로 지난번에 이어 계속 진행하고 싶지만, 유효성 검사에 대해 설명하다보니 이것만으로 너무 길어져서 오늘도 여기서 마무리 하겠습니다(__). 곧 찾아뵙도록 하겠습니다. ^^

참고 : http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
안녕하세요. 추운날씨 잘 견디셨죠? 이제야 좀 어깨펴고 글좀 쓰겠네요. 자~ 오늘도 함께하시죠^^

이번에는 MVC로 사이트를 만드는 시간을 가져보려합니다. 간단하게 회원가입, 로그인, 게시판 정도로 해볼 생각입니다. 오늘은 첫번째로 회원가입을 해보도록 하겠습니다. 너무 썰렁하더라도 옷 단단히 더 껴입으시고 웃음으로 넘어가 주세요^^;

DB 생성하기

사용자 테이블을 만들어봐야죠^^ 테이블 컬럼은 다음과 같습니다.

 컬럼명  데이터 타입
 SEQ  int
 ID  nvarchar(50)
 NAME  nvarchar(50)
 PWD  nvarchar(50)
 EMAIL  nvarchar(100)
 EMAIL_YN  char(1)
 RGST_DT  datetime

다음의 순서대로 테이블을 생성하겠습니다.

1. SQL Server DataBase를 생성합니다. 솔루션 탐색기에서 마우스 우클릭하여 App_Data -> Add -> New Items을 선택하여 MvcDb.mdf라는 이름으로 데이터베이스를 생성합니다.
2. 서버 탐색기에서 생성된 MvcDb.mdf를 클릭하면 데이터베이스가 연결되면서 DB구조가 확장이됩니다. Tables 폴더에서 마우스 우클릭하여 Add New Table을 클릭하고, 위의 테이블 구조로 TB_USER 테이블을 만들겠습니다. 다 만든후 저장해주세요^^


DB와 테이블이 생성이 되었으면 이제 다음의 순서대로 모델을 생성하겠습니다.

1. 솔루션 탐색기에서 Models -> Add -> New Items을 선택합니다.
2. Data 카테고리를 선택하고 ADO.NET Entity Data Model 템플릿을 선택합니다.
3. 모델 이름을 UserDbModel.edmx라고 입력한후 다음버튼을 클릭합니다.
4. Entity Data Model 위자드 팝업이 뜨면 Generate from database를 선택하여 다음버튼을 클릭합니다.


5. 완료가 되면 엔티티 데이터 모델 디자인 창이 열립니다.


SQL Server DataBase의 생성과 모델 생성은 여기를 참고하세요.

초간단한 가입페이지 만들기

정말 간단하게 만듭니다. 위 모델 보시면 항목도 많지가 않죠^^

먼저, 컨트롤러를 하나 생성하겠습니다. 이름은 Member로 하겠습니다.
Controllers에서 Add -> Controller을 하시고 다음과 같이 Member 입력하시고 Add를 꼬옥 눌러줍니다.


다음으로 액션메쏘드를 만들도록 하겠습니다. 이름은 Join으로 하겠습니다.^^

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

네, 잘 만들었죠. 그러면 View 페이지도 생성하도록 하겠습니다. 메쏘드 안에서 오른쪽 버튼 클릭후 Add View 하시면 Member 폴더 밑에 Join.aspx 페이지가 생성이 됩니다. 소스는 다음과 같습니다.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Join
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>회원가입</h2>
    <% using (Html.BeginForm()) {%>
       <p>아 이 디 : <%= Html.TextBox("Id") %></p>
       <p>이    름 : <%= Html.TextBox("Name") %></p>
       <p>패스워드 : <%= Html.Password("Pwd") %> </p>
       <p>패스워드 확인 : <%= Html.Password("CPwd") %></p>
       <p>이 메 일 : <%= Html.TextBox("Email") %>
       <%= Html.CheckBox("Email_Yn", true, new { @value = "Y" } )%>수신여부</p>         
       <input type="submit" value="가입" />  
    <% } %>
</asp:Content>

여기서 확인해볼 것은, Html.BeginForm() 입니다. 브라우저를 열어서 소스보기를 해보시면


자연스럽게 <form>~</form>태그가 생성된 것을 확인하실 수 있습니다. 또, action 부분에 /Member/Join 이 매핑되는 것도 확인하실 수 있습니다.

페이지를 만들었으니 확인을 해봐야겠죠? F5를 꾸욱 눌러봅니다.(위에서 먼저 눌러서 확인했잖아~!! 소스보기는 하늘에서 뚝 떨어진 거니? 라고 물으신다면, 저는 할말이 ;;;)


^^ 바로 띄우기도 좀 거시기해서, 링크하나 걸었습니다;;


폼 내용을 입력하시고, 가입버튼을 클릭합니다.
그런데 이게 무슨 퐝당한 시츄에이션인지요. 뭔가 상태바를 보니 서버를 호출하는것은 같은데 제가 입력한 값들만 다 사라지고 아무 변화가 없습니다. 이유인 즉, 폼이 /Member/Join으로 전송되면 Join메쏘드를 호출합니다. 그런데 거기서 아무 처리를 안해줬으니 그냥 동일하게 뷰페이지만 새로 렌더링하는거죠. 그래서 제가 입력한 값들은... 과감히 버려졌습니다. 앍!
여기서 하나 알게된 것은, 아~ 그러면 값들을 처리하는 메쏘드가 하나 더 있어야겠구나 하는거죠^^;


소스를 보시면, Join 메쏘드가 두개인 것을 확인하실 수 있습니다. 하나는 /Member/Join이 호출되었을때의 메쏘드 이고, 다른 하나는 폼입력을 마친후에 submit시 호출되는 메쏘드 입니다.

Post로 받는 메쏘드를 보시면 EMAIL_YN이 하나 걸리긴 하는데요. Html.CheckBox() 헬퍼 메쏘드를 사용하면 hidden 필드값이 하나 더 생깁니다.(소스보기 참고) value 값은 false 로 되어있고요, 그래서 체크박스가 체크가 되어있으면 제가 value로 지정한 'Y'가 넘어오는데 체크해제때에는 'false'로 넘어옵니다. 그래서 저런 구문을 추가했긴했는데, 추후에 조금 더 알아보도록 하겠습니다.
_db.AddToUserSet(userInfo); 의 경우 엔티티 프레임워크에서 제공하는 프로퍼티로 저희가 생성한 User를 추가해준다는 거겠죠? 그리고 항상 디비작업을 완료하려면 SaveChanges(); 메쏘드를 호출해야합니다.

완료가 됐으니 JoinSuccess 페이지에 모델객체를 넘겨서 마무리를 짓도록 하겠습니다.
Views 폴더 밑 Member 폴더에서 Add -> View 를 하셔서 형식화된 뷰를 생성하도록 하겠습니다.


JoinSuccess.aspx 페이지의 소스는 아래와 같습니다.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcSite.Models.USER>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 JoinSuccess
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>회원가입 완료</h2>
    <%= Html.Label(Model.NAME) %> 님, 회원가입이 완료되었습니다. <br />
    환영합니다!
</asp:Content>

다시, 실행을 시켜서 폼 입력을 마친 후 가입버튼을 클릭하면,


네, 디비에 값이 잘 인서트 되었는지도 확인해보세요^^;
여기서 잠깐! 우리가 여기까지 너무 쉽게 온 것 같네요. 다시 실행을 시켜보도록 하죠. 그리고 폼에 값을 입력하지 않고 가입버튼을 클릭하면 Excepton 발생!!!


역시, 너무 쉽게 됐다고 생각했습니다. View Datail을 클릭하니


'Cannot Insert the value NULL into column 'ID...'
아이디부터 걸리기 시작합니다.

급 마무리요

글이 너무 길어졌습니다. 간단한 것을 이리도 길게 글을 쓰는 저를 꾸짖진 말아주세요^^
암튼, 여기서 과제가 생겼습니다. 다음 포스팅에서는 유효성 검사 부분들을 다루도록 하겠습니다.
안녕하세요. 아직 떨린 마음을 감추지 못하는 팀블로그 새내기 박세식입니다. 너무나 오랜만에 포스팅을 하게되네요. 제 개인사까지 다 말씀드릴 필요는 없겠지만, 여러분 모두 새해에는 하시는일 모두 잘되었으면 좋겠습니다. 진심으로요ㅠㅠ 늦었지만 새해 복 많이 받으세요^^

이번 시간은 ASP.NET MVC와 인사를 나눠보는 시간을 갖도록 하겠습니다. 반갑게 만나보도록 하죠^^

M, V, C의 각방생활

먼저 프로젝트를 생성합니다. 새 프로젝트 열기에서


ASP.NET MVC 2 Web Applicatoin 을 선택하고, 이름은 HelloMVC 로 하겠습니다. OK를 클릭하면 다음과 같이 유닛 테스트 프로젝트를 생성할 것인지 묻는 창이 뜹니다. (이게 ASP.NET MVC의 장점이라는 겁니다. 프로젝트 자체에서 유닛 테스트를 지원해주고 있습니다. 이 창에서 Yes 를 선택하면 간단하게 유닛테스트 프로젝트를 생성할 수 있습니다.) 이 시간은 유닛테스트와는 전혀 상관이 없는 관계로 No를 선택하도록 하겠습니다.


그러면, 다음과 같은 구조의 프로젝트가 생성된 것을 확인하실 수 있습니다.


Controllers, Models, Views 폴더가 각각 분리되어 생성되었습니다. 지나가는 얘기로, 저의 한가지 꿈이 여러개의 방이 있는 집을 갖는건데요.^^; 하나는 서재실, 또 하나는 음악실, 나머지 하나는 침실 뭐 이런거죠. 정말 각각의 방 안에서의 할일이란 명확합니다. 각각의 방에서 할일들을 한 방에서 하게 된다면... 잠시 상상해보겠습니다. 한쪽에서는 드럼과 피아노와 일렉기타의 절묘한 하모니의 시끄러움(?)이, 다른 한쪽에서는 책과 씨름하며, 또 다른 한쪽에서는 코를 곯며 자는 모습... 상상이 가십니까? 음악실에서는 음악을 연주하며 맘껏 소리높여 노래도 부르고, 서재실에서는 조용한 가운데 책도 보며 교양을 쌓고, 침실에서는 자면되는거죠. 모델, 뷰 그리고 컨트롤러 또한 각자의 할 일이 뚜렷하기 때문에 한 방에 같이 있을 수가 없습니다. 각방을 써야하는거죠^^ 

프로젝트가 생성될때 샘플페이지도 같이 생성이 됩니다. F5를 눌러서 확인해보겠습니다. 클릭하시면 디버깅을 할 수 있도록 Web.config 파일을 수정한다는 팝업창이 뜨는데요, OK하고 넘어가도록 합니다.


자, Welcome to ASP.NET MVC! 가 출력되는 것을 확인하실 수 있습니다. 우리를 먼저 반갑게 맞이해주는데요.^^ 기분좋네요~ 가만히 있을 순 없죠. 저도 인사를 해보도록 하겠습니다.

Hello! ASP.NET MVC!!!

먼저, 컨트롤러 폴더의 HomeController 클래스의 Index() 메쏘드를 다음과 같이 수정하겠습니다.

public ActionResult Index()
{
    ViewData["Message"] = "Hello! ASP.NET MVC!!!";
    return View();
}

소스 1 - /Controllers/HomeController.cs

컨트롤러는 ViewData 를 통해서 뷰에 데이터를 전달할 수 있습니다. 뷰에서는 인라인 코드로 ViewData에 접근할 수 있습니다. (자세한건 여기에서 'ViewData로 뷰에 전달하기'를 봐주세요) 수정한 내용은 F5 로 실행하여 확인할 수 있습니다.


그런데 주소를 보니 http://localhost:1589/로 되어있는데도 원하는 결과를 확인할 수 있습니다. ASP.NET MVC는 기존 웹폼에서처럼 직접적인 페이지 호출이 아닌 라우팅 룰에 의해 호출이 됩니다. ASP.NET MVC 애플리케이션이 처음 시작되면 Global.asax.cs 에 있는 Application_Start() 메쏘드가 호출되고 이는 라우트 테이블을 생성합니다.


소스 2 - Global.asax.cs

라우트 테이블은 들어오는 웹 요청을 3부분으로 나눕니다. 처음은 컨트롤러 이름과 매핑이되고, 두번째는 액션메쏘드와 매핑이 되고, 세번째는 그 메쏘드에 전달될 파라미터와 매핑이 됩니다. 예를들어, /Product/Detail/3 과 같이 요청이 들어오면 다음과 같이 나뉩니다.

Controller  = ProductController
Acton = Detail
Id = 3

예제에서 주소가 http://localhost:1589/  이렇게 되어도 라우트 테이블의 디폴트값인 HomeController의 Index 액션메쏘드를 호출하기 때문에 우리가 원하는 결과를 볼수 있었던 거죠^^

이제 인사는 나누었는데 처음 만남에 인사만 나누기는 섭섭하죠? ^^; 커피라도 한잔하며 얘기 나눠보
는게 좋겠죠? 흠.. 이번시간은 간단히 인사만 나누고 끝내려고 했는데요. 너무 금방 헤어지면 정말 아쉬울것 같아서 더 진행하는 것이니 간단하게 해보도록 하겠습니다. 아무쪼록 저의 허접함을 탓하지 마옵소서...(저 스스로 분발하도록 하겠습니다^^;)

우리 차라도 한잔?

먼저 차한잔 마실거니까요 Index.aspx 를 수정해서 차 마시러 가보도록 하죠^^

<h2><%= Html.Encode(ViewData["Message"]) %></h2>
<br />
차라도 한잔 하실래요?
<%= Html.ActionLink("네", "../Menu/MenuForm") %>

소스 3 - /Views/Home/Index.aspx

Html.ActionLink() 메쏘드는 A 태그를 만듭니다. 링크를 MenuForm으로 하네요. 물론 실행해 보면 에러가 나겠죠. 메뉴컨트롤러와 해당 액션메쏘드가 없기 때문이죠. 그러면 컨트롤러를 생성해보도록 하겠습니다.

using HelloMVC.Models;

namespace HelloMVC.Controllers
{
    public class MenuController : Controller
    {
        //
        // GET: /Menu/
        public ActionResult MenuForm()
        {
            List<Menu> MenuList = new List<Menu>();

            MenuList.Add(new Menu { Id = 1, Name = "커피", Price = "4000" });
            MenuList.Add(new Menu { Id = 2, Name = "레몬에이드", Price = "5000" });

            return View(MenuList);   
        }
    }
}

소스 4 - /Controllers/MenuController.cs

요란한 밑줄이 보이는 Menu 클래스도 생성하겠습니다.

public class Menu
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Price { get; set; }
}

소스 5 - /Models/Menu.cs

모델 클래스의 경우 생성을 하신 후에 꼭! 꼭! 꼭! 빌드를 하셔야합니다. 그래야 아래 보이는 Add View를 하실때 원하는 모델 클래스를 선택하실 수 있습니다.

ViewResult로 MenuList 모델을 파라미터로 뷰페이지로 리턴합니다. 액션메쏘드에 아무데서나 마우스 오른쪽 버튼을 클릭하여 뷰페이지를 생성하도록 하겠습니다.


Add View를 클릭하면 다음과 같은 팝업이 뜹니다. 먼저 저희가 컨트롤러에서 메뉴를 추가한 리스트를 확인해보기 위해서 다음과 같이 세팅하도록 하겠습니다.


Create a stringly-typed view를 선택하고(뷰를 생성할때 모델과 강력한 결합을 이끌게 되면 직접적으로 모델의 애트리뷰트들을 액세스할수 있게 됩니다.), View data class 를 Menu클래스로 선택합니다(여기서 Menu 클래스가 안보이시면 빌드를 안하신거에요. 빌드하셔야죠!). 마스터 페이지의 체크를 해제하도록 합니다. Add 버튼을 클릭하시면 MenuForm 페이지가 생성되는 것을 확인하실 수 있습니다. 바로 실행을 해보시면,


잘나오네요. 그러면 소스를 조금(?) 수정하도록 하겠습니다.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IEnumerable<HelloMVC.Models.Menu>>" %>
<!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 id="Head1" runat="server">
    <title>MenuForm</title>
</head>
<script type="text/javascript">
    var menuList = new Array();
    function Menu() {
        this.id;
        this.name;
        this.price;
        this.quantity = 0;
        this.cPrice;
    }
    function addMenu(id, name, price) {
        var menuNum = -1;
        for (var k = 0; k < menuList.length; k++) {
            if (id == menuList[k].id) {
                menuNum = k;
                break;
            }
        }
        if (menuNum == -1) {
            var mn = new Menu();
            mn.id = id;
            mn.name = name;
            mn.price = price;
            mn.quantity++;
            mn.cPrice = price;
            menuList.push(mn);
        }
        else {
            menuList[k].quantity++;
            menuList[k].cPrice = menuList[k].quantity * parseInt(menuList[k].price);
        }

        var str = "";
        var totalPrice = 0;
        for (var i = 0; i < menuList.length; i++) {
            str += menuList[i].name + " " + menuList[i].quantity + "잔 " + menuList[i].cPrice + "<br />";
            totalPrice += parseInt(menuList[i].cPrice);
        }
        document.getElementById("divMenuList").innerHTML =
str + "<br /> 돈부터 내시죠~ 여긴 선불!! " + totalPrice + "원 되겠습니다~~^^";
    }
</script>
<body>
    <h2>Menu</h2>
    <table width="400px" cellspacing="1" style="background-color:#CCCCCC">
    <% foreach (var menu in Model) { %>
    <tr>
        <td style="background-color:#EEEEEE"><%= Html.Encode(menu.Name) %></td>
        <td style="background-color:#AAAAAA" align="center"><%= Html.Encode(menu.Price) %></td>
        <td style="background-color:#BBBBBB">
<a href="javascript:addMenu('<%= menu.Id %>', '<%= menu.Name %>', '<%= menu.Price %>');"><%= Html.Encode(menu.Name)%> 추가요</a></td>
    </tr>  
    <% } %>       
    </table><br />
    =================== 주문서 ===================<br /><br />      
    <div id="divMenuList" style="border:1px solid #E3E3E3; width:400px; height:200px" ></div>
</body>
</html>

소스 6 - /Views/Menu/MenuForm.aspx

이상하게 복잡스럽게 보이지만(죄송합니다__) 수정된것은 테이블에 색깔준것과 주문추가 이벤트, 주문서네요.
결과물을 보면


추가 이벤트를 일으킬때마다 주문서가 수정되는 간단한 예제를 보고 계십니다.^^

마무리요

마지막의 결과물은 간단한데 너무 복잡하게 표현한건 아닌지 심히 걱정이 됩니다. 제 실력이 여기까지인지라..다음 포스팅때는 더 깔끔하고 세련된 코드로, 더 나아진 모습으로 찾아뵙도록 하겠습니다.


참고자료 :
http://www.techbubbles.com/aspnet/aspnet-35-mvc-application/
안녕하세요. 이번에 새롭게 팀원이된 박세식이라고 합니다. 많이 부족해서 컴터앞에 앉아 이렇게 자판을 두드리고 있는 시간에도 떨림이 멈추질 않네요-_-; 앞으로 잘 부탁드리겠습니다.(따스~한 피드백 아시죠^^;)  
자~ 그럼. ASP.NET MVC에 대해 알아보도록 하겠습니다.

첫번째 시간으로 ASP.NET MVC vs ASP.NET WEB FORM 에 대해 글을 써보도록 하겠습니다. 제 포스트는 ASP.NET MVC에 관한 글입니다.^^; 그래서 이 둘의 대결구도라기 보다는 웸폼의 문제점을 짚어보고 MVC에 좋은 점에 대해서 글을 써 나가려고 합니다.


ASP.NET WEB FORM의 문제점?

ASP.NET WEB FORM은 ASP.NET 개발의 전통적인 스타일이고, 큰 스케일의 웹사이트를 좀더 간단하게 만들게 해주는 기술입니다. 웹폼은 드래그 앤 드랍으로 컨트롤들을 ASP.NET 페이지에 추가하고 그것들에 맞는 코드를 작성합니다. 이러한 개발방식이 개발자들의 마음을 끄는거죠. 그!러!나! 웹폼은,

관계가 분리되어 있지 않습니다. UI와 코드가 섞여있죠--;
● 자동적으로 테스트하는 것이 쉽지 않습니다. 런타임 시작없이 테스트하기가 어렵죠. 이는 실행환경을 쭉~ 돌면서 테스트할 수 밖에 없다는 겁니다-_-;
● 상태정보를 저장하려면 각 서버페이지의 상태를 뷰스테이트라고 하는 히든 필드값으로 클라이언트 페이지에 저장합니다.

하지만, 웹폼 개발에서의 특징인 뷰스테이트와 포스트백은 축복이자 파멸의 원인이됩니다. 뷰스테이트는 클라이언트와 서버간의 오고가는 데이터들의 상태를 히든 필드로 구성하고 있는 메커니즘으로, 이것의 크기는 무지막지하게 커질 수 있습니다. 매번 호출시 이 거대한 데이터가송수신된다니 끔찍스럽죠-_-; 특히 페이지에 데이터그리드나 그리드뷰와 같은 서버컨트롤이 포함되어있다면 한페이지 한페이지 넘길때마다 응답지연의 원인이 되어 사용자들을 기다림에 지치게 만드는거죠 OTL;; 또한 포스트백은 매번 액션에 의한 트리거로서 발생이됩니다. 그 거대한 데이터(뷰스테이트)를 어떤 액션만 취하게 되면 다시 그리게 되는거죠. 이 뷰스테이트에 대해 좀더 알아보죠.


뷰스테이트(View State)란?

뷰스테이트는 포스트백시 웸폼의 상태의 변화를 유지시켜주는 기술입니다. 일반적으로 __VIEWSTATE라는 이름의 히든 필드로 웹페이지에 위치합니다. 이 히든값은 수십 킬로바이트씩 쉽게 커질 수 있습니다. 이 뷰스테이트는 사용자가 웹 페이지를 포스트백 할 때 천천히 불려지고(다운로드), 그 히든 값의 내용을 웹 요청시에 포스트해서 요청 시간을 길어지게 합니다. ASP.NET 웹 페이지의 요청이 올 때 정확하게 무슨 일이 일어나는지를 알려면 ASP.NET 페이지의 생명주기(Life Cycle)을 알아봐야 하는데요. 잠깐 간단하게만 알아보도록 하죠^^;(어째~ 이야기가 점점 옆으로 새네요.. 언제 제자리로 돌아올지는 생각하지 말아주세요-_-;)


Asp.Net Page Life Cycle

매번 ASP.NET 웹 페이지에 의해 서버에 요청이 오면 첫째로 웹 서버는 ASP.NET 엔진으로 요청을 넘깁니다. ASP.NET 엔진은 웹 페이지의 올바른 파일 접근을 확인하는 여러 단계로 구성된 파이프라인을 통해서 사용자의 세션 정보를 얻습니다. 파이프라인의 끝에서는 요청된 웹 페이지에 상응하는 클래스를 인스턴스화하고 ProcessRequest() 메쏘드를 호출합니다. ASP.NET 페이지의 생명주기는 ProcessRequest() 를 통해서 시작됩니다. 이 메쏘드는 페이지의 컨트롤 단계를 준비합니다. 그 다음으로, 페이지와 서버는 ASP.NET 웹 페이지에 필요한 여러 단계를 하나씩 단계별로 밟게 됩니다. 이 단계들에는 뷰스테이트를 관리하고, 포스트백 이벤트를 처리하고, HTML 페이지를 렌더링하는 것들이 포함되어 있습니다.


그림 1. 페이지 생명 주기의 이벤트들
출처 : http://msdn.microsoft.com/en-us/library/ms972976.aspx

다시 뷰스테이트로 돌아와서 정리하자면, 세상에 공짜는 없습니다.(비용이 든다는거죠) 여기 뷰스테이트도 예외일 수는 없습니다^^; ASP.NET 페이지가 요청될 때마다 뷰스테이트는 두가지 성능에 관련되는 일을 합니다. 첫째는, 모든 페이지를 방문할 때 뷰스테이트는 해당 컨트롤 계층의 모든 컨트롤의 뷰스테이트를 모아서 베이스 64 인코딩으로 시리얼라이즈합니다. 여기서 생성된 스트링이 __VIEWSTATE에 값인거죠^^

반대로, 포스트백시에는 뷰스테이트를 읽엇 저장된 뷰스테이트 데이터로 디시리얼라이즈하고 컨트롤 계층구조에 있는 적절한 컨트롤러로 업데이트합니다. 두번째로, 뷰스테이트는 클라이언트가 다운로드 받아야할 웹페이지의 크기를 증가시킵니다. 원래 보여질 페이지의 데이터와는 또다른 데이터인거죠. 뷰스테이트 크기가 큰 페이지는 이 뷰스테이트의 크기가 수십 킬로바이트가 됩니다.  이것을 다운받기 위해 또 수 초나 수 분의 시간을 할애해야합니다.(이게~ 뭔가요-_-;) 또, 포스트백시에도 뷰스테이트를 웹 서버로 보내야하고, 그것 때문에 포스트백 요청시간이 증가하는 거죠.


ASP.NET MVC 알아보기

ASP.NET MVC는 모델, 뷰 그리고 컨트롤러로의 3개의 파트로 분리되어 구현됩니다.


그림 2. MVC Framework
출처 : http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx

● 뷰(View) 는 애플리케이션의 UI를 담당하고, 이는 단지 컨트롤러에 의해 애플리케이션의 데이터로 채워질 HTML 템플릿에 지나지 않습니다.
● 모델(Model) 은 UI를 렌더링하는 뷰가 사용할 애플리케이션의 객체, 즉 애플리케이션의 데이터의 비즈니스 로직을 구현합니다.
● 컨트롤러(Controller) 는 사용자 입력과 상호작용하는 응답을 핸들링합니다. 웹 요청은 컨트롤러에 의해 핸들링되고, 요청된 URL의 표현될 뷰와 적절한 데이터(모델 객체)를 결정합니다.


ASP.NET MVC의 요청 흐름도

ASP.NET MVC 애플리케이션은 웹폼과 같이 주소창에 입력된 URL이 해당서버의 디스크에 있는 페이지(파일)을 찾는것이 아닌 컨트롤러를 통한 뷰를 호출하게 됩니다. URL은 컨트롤로와 매핑이 되고 컨트롤러는 뷰를 리턴해주는 식이죠. 다음의 그림을 보시면


그림 3. 요청 흐름도
출처 : http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx

처음 사용자 요청(URL)이 들어오면 Routing Rule 에 의해 컨트롤러를 매핑시킵니다. Routing Rule 은 ASP.NET MVC 프로젝트를 생성하면 Global.asax에 정의되어 있는데요. 제 블로그에 컨트롤러를 설명하면서 Global.asax에 대해 간략하게 설명한 부분이 있는데요 참고하시기 바랍니다. 여기서도 간단히 설명드리자면, 주소는 {controller}/{action}/{id} 이런식으로 들어오게됩니다. 특정 컨트롤러의 특정 액션메쏘드의 파라미터로 id를 넘겨준다는 룰이죠. (액션메쏘드는 컨트롤러 클래스에서 public으로 제공되는 메쏘드를 가리킵니다.)

다시, 컨트롤러는 뷰에 넘겨줄 데이터를 만들 모델을 선택합니다. 선택된 모델객체는 뷰에 넘겨줄 데이터(ViewData)를 만들고, 컨트롤러가 이를 뷰에 넘겨줍니다. 마지막으로 뷰는 HTML로 렌더링해서 사용자에게 보여줌으로 하나의 흐름이 흘러가는 거죠^^;

그래서 ASP.NET MVC의 장점은?

● 모델, 뷰, 컨트롤러로 명확하게 관계과 분리되어 있어 복잡한 애플리케이션 로직을 관리하기 쉽게 해줍니다. 이로 인해 각 개발자는 MVC 패턴의 분리된 컴포넌트를 각자 개발할 수 있습니다. 페이지 단위로 되어 있는 웸폼은 비하인드 코드에서 로직을 담당하게 되는데요. 다른 두개의 페이지에서 비슷한 작업을 하게 된다해도 하나의 작업으로 사용하기가 어렵다는 거죠^^; 특정 페이지의 이벤트가 그 페이지의 비하인드 코드를 호출하기 때문에 같은 작업을 해도 저처럼 잘 모르는 사람들은 저희에게 무한한 능력(?)을 심어주는 카피 앤 페이스트 작업을 거쳐야한다는 겁니다.

● 유닛테스팅을 쉽게해줍니다. 이 유닛테스트도 M,V,C 가 각각 깔끔하게 분리되어 있어주기 때문에 가능한거죠^^; 추후에 유닛테스트가 과연 얼마나 쉬운지 알아보는 시간을 갖도록 하겠습니다.

● MVC 모델은 뷰스테이트와 포스트백을 사용하지 않습니다. 그래서, MVC는 일반적으로 웹폼에 비해 페이지 사이즈가 작습니다. 폼의 히든 필드인 뷰스테이트 데이터와 같은 불필요한 데이터가 없기 때문이죠.

● 웸폼 모델에서의 URL이 서버의 존재하는 파일을 직접 요청하는 것 대신에 REST 방식의 URL을 사용하여 URL을 간단하게 해주고, 기억하기 쉽게 해주고, SEO의 도움을 줍니다.

SEO(Search Engine Optimization) 는 검색엔진의 결과로 보여질 랭크의 순위를 높이는 작업을 말합니다. 상위 랭크의 검색결과로 나타내어지면 사용자가 아무래도 계속 그것만 보게되니 좋겠죠^^; 그래서 URL 또한 사용자가 보기 쉬운 걸로 나타내면 좋다는거죠. 예를들어, /Product/Edit/4 이런 URL 이라면 4번째 상품을 수정하겠다는 거군. 이렇게 명시적으로 알수 있다는 겁니다. 예가 좀 부적합한가요^^;

● jQuery나 ExtJS와 같은 클라이언트단 자바스크립트 프레임워크와 쉽게 통합할 수 있습니다.


마무리요

사실, 웸폼과 MVC를 비교해서 꼭 이것이 더 좋으니까 이것만 사용해야지가 아닙니다. 각 프로젝트의 특성에 맞게 선택하는 거죠. (다시한번 말씀드리지만 이 포스트는 ASP.NET MVC에 관한 글입니다--;) 성능을 따지지 않고 빠른 개발을 원한다면 웹폼을 선택하는 것이 좋을 것 같고, 유닛테스팅과 성능에 중점을 두겠다 싶으면 MVC쪽을 선택해봄이 좋겠다는 거죠. 다음 포스트에는 예제를 통해서 ASP.NET MVC와 인사를 나눠보는 시간을 갖도록 하겠습니다^^


참고자료 :
http://msdn.microsoft.com/en-us/library/ms972976.aspx
http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx
http://msdn.microsoft.com/en-us/magazine/dd942833.aspx
http://www.techbubbles.com/aspnet/aspnet-mvc-and-web-forms/
http://www.taeyo.net/lecture/NET/AspNet35_pre02.asp
http://weblogs.asp.net/shijuvarghese/archive/2008/07/09/asp-net-mvc-vs-asp-net-web-form.aspx
http://weblogs.asp.net/scottgu/archive/2007/10/14/asp-net-mvc-framework.aspx