안녕하세요. 지난 시간에는 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
이번 시간은 jQuery 플러그인인 jqGrid를 잠깐(?) 사용해보는 시간을 갖도록 하겠습니다. 

jqGrid 플러그인 다운

먼저, jqGrid 사이트에서 jqGrid 플러그인을 다운받습니다.
다운받은 압축파일을 푸신 후, ASP.NET MVC 프로젝트에 3개의 파일을 추가하겠습니다. jquery.jqGrid.min.js 파일과 jquery-ui-1.7.1.custom.css, ui.jqgrid.css 파일입니다.


자, 이제 시작해볼까요?

jqGrid 맛보기

한꺼번에 다 보여드리기 보다는 조금조금씩~ 맛을 보여드리도록 하겠습니다^^

좀전에 프로젝트에 추가한 파일을 뷰페이지에 쭈~욱 끌어다 놓습니다.

<link href="/Content/jqGrid/jquery-ui-1.7.1.custom.css" rel="stylesheet" type="text/css" />
<link href="/Content/jqGrid/ui.jqgrid.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="/Scripts/jqGrid/jquery.jqGrid.min.js" type="text/javascript"></script>

그 다음으로, 이 jqGrid 관련 자바스크립트 소스를 추가하겠습니다. 한눈에 봐도 너무 간단한 스크립트 부분이라 jQuery를 모르셔도 딱!! 파악하실수 있을 겁니다.

<script type="text/javascript">
        $(function () {
            $("#list").jqGrid({
                url: '<%= Url.Action("GridData", "Home") %>',
                datatype: 'json',
                mtype: 'get',
                colNames: ['No', '이름', '전화번호', '이메일', '단축다이얼'],
                colModel: [
                  { name: 'DirId', index: 'DirId', width: 40, align: 'center' },
                  { name: 'Name', index: 'Name', width: 100, align: 'left' },
                  { name: 'Phone', index: 'Phone', width: 100, align: 'left' },
                  { name: 'Email', index: 'Email', width: 200, align: 'left' },
                  { name: 'SpeedDial', index: 'SpeedDial', width: 100, align: 'center'}],
                caption: '전화번호부'
            });
        });
    </script> 

위 소스를 잠깐 살펴보면, url은 Home 컨트롤러에서 GridData라는 액션메쏘드를 호출하고 있습니다. 잠시 후에 이를 구현해야겠죠?^^; datatype은 json이네요. 음.. GridData라는 놈이 json객체를 넘겨주겠군?! 하고 생각하시면 되죠. colNames는 리스트를 보여줄때 각각의 컬럼을 구분짓는 이름입니다. colModel을 통해 grid에서 받을 리스트에 width라던지 align을 주고 있는 것을 보실수 있습니다.

그래서 완성된 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">
    <link href="/Content/jqGrid/jquery-ui-1.7.1.custom.css" rel="stylesheet" type="text/css" />
    <link href="/Content/jqGrid/ui.jqgrid.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script src="/Scripts/jqGrid/jquery.jqGrid.min.js" type="text/javascript"></script>
 
    <script type="text/javascript">
        $(function () {
            $("#list").jqGrid({
                url: '<%= Url.Action("GridData", "Home") %>',
                datatype: 'json',
                mtype: 'GET',
                colNames: ['No', '이름', '전화번호', '이메일', '단축다이얼'],
                colModel: [
                  { name: 'DirId', index: 'DirId', width: 40, align: 'center' },
                  { name: 'Name', index: 'Name', width: 100, align: 'left' },
                  { name: 'Phone', index: 'Phone', width: 100, align: 'left' },
                  { name: 'Email', index: 'Email', width: 200, align: 'left' },
                  { name: 'SpeedDial', index: 'SpeedDial', width: 100, align: 'center'}],
                caption: '전화번호부'
            });
        });
    </script> 
<h2><%: ViewData["Message"] %></h2>
<table id="list" class="scroll" cellpadding="0" cellspacing="0"></table>
</asp:Content>

table에 리스트를 쫙~ 뿌려주도록 하겠습니다.

이제, Home 컨트롤러를 잠깐 손보도록 하겠습니다. jqGrid에서 받을 json객체를 리턴하는 GridData라는 액션메쏘드를 만들도록 하겠습니다. 이번 포스팅은 정말 맛보기이기 때문에 간단하게 바로 객체를 만들어서 리턴하겠습니다.


와우~ 정말 간단하게 모든 구현이 완료되었습니다! 이제 실행을 해볼까요?


엥? 이건 또 뭔가요? 역시 한번에 되는 것은 없나봐요;;
내용을 보니 GET 요청이 차단되었고, JsonRequestBehavior를 AllowGet으로 설정하라고?! 호출 스택을 보니 JsonResult를 실행하다가 에러가 발생하였네요. 음.. JsonResult 부분이 잘못되었군. 한번 수정해보죠^^;

return Json(dirs, JsonRequestBehavior.AllowGet);

수정후 실행해보면~


네. 멋지게 성공하였습니다.

ASP.NET MVC 2 에서는 기본적으로 이러한 GET방식의 호출을 보안상의 문제로 막아놨습니다. 그래서 JSON 객체를 리턴할때는 JsonRequestBehavior.AllowGet을 추가하여 클라이언트의 GET요청을 허용하도록 한 것이죠.

하지만, 막아놓은 것을 굳이 풀 필요는 없겠죠?^^; POST 방식으로 호출하는 것이 좀더 좋을 듯 합니다.
이 부분은 따로 설명드릴 필요없겠죠? 약간(^^;;) 설명드리면~
일단 GridData액션 메쏘드 위에 GET으로 요청한 놈은 접급하지마! 라는 표지판([HttpPost])을 세워두는거죠. 실행시켜보면 역시 에러가 발생할겁니다. 리소스를 찾을수 없다는... 그래서! 뷰페이지 스크립트 부분의 mtype을 POST로 수정하는거죠^^ 실행해보세요~ 잘되시나요?

이거슨 번외요~

웹 개발자를 위한 Web Development Helper 유틸이 있습니다. 익스플로러에 확장할 수 있죠. 저같은 경우는 Fiddler를 많이(?) 사용하는데요. Web Development Helper는 특히 Ajax와 ASP.NET 개발자를 위한 것이라고 하네요. 사이트에서 다운 받고 인스톨하시고, 익스플로러의 도구 메뉴의 탐색창->Web Development Helper를 클릭하시면 됩니다.
이번 jqGrid 맛보기에서의 request&response정보도 확인할 수 있네요.


마무리요

이번시간은 정말 jqGrid 플러그인의 맛보기였고요, 다음 포스팅에서 뵙도록 하겠습니다. (다음 포스팅도 맛보기처럼 보이면 어떡하죠?^^;;)

참고자료 :
http://haacked.com/archive/2009/04/14/using-jquery-grid-with-asp.net-mvc.aspx
http://www.trirand.net/
http://projects.nikhilk.net/WebDevHelper/
http://geekswithblogs.net/michelotti/archive/2008/06/28/mvc-json---jsonresult-and-jquery.aspx
안녕하세요. 늦바람이 무섭다고 하는데요. 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.06.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
모기와의 사투를 버린 끝에 이제야 컴퓨터 앞에 앉아 글을 쓸 수 있게 되네요(새벽 1시네요ㅡ.ㅡ) 아흑.
비록 눈이 따갑고 눕고 싶지만, 이제는 정말 제 자신과의 약속을 지키기 위해 한자 한자 적어나가렵니다.^^

지난 시간에 유효성 검사에 대해 살펴봤는데요. 이제 본론으로 넘어와서 적용해봐야겠죠?

유효성 검사 적용하기

저희가 USER 모델을 생성할때 엔터티 프레임워크(엔티티가 입에 붙었는데 한글판에 엔터티라고 명시되어있네요;;)를 통해 생성한 것 다들 기억하시죠? 엔터티 프레임워크의 경우 자동으로 모델 클래스를 생성해 주는 것도 다들 아실겁니다. 또한, 엔터티 프레임워크로 생성된 모델클래스를 직접적으로 컨트롤 할수 없다는 것도..
그렇다면 유효성 검사 부분은 도대체 어디다 둬야 한단 말이냐?

파샬 & 메타데이타 클래스 생성하기

메타 데이타 클래스를 만들어야 합니다. 또한 USER 모델에 해당하는 파샬 클래스도 생성해야합니다.
파샬 클래스의 경우 여러 파일, 여러 부분에 멤버나 메쏘드 등의 정의를 각각 두면 컴파일시에 이들 모두를 결합하게 되죠. 다들 아시는 내용!
여기서 잠깐, 엔터티 프레임워크로 생성된 모델 클래스의 소스를 잠깐 살펴보면,


모델 클래스가 파샬 클래스로 정의 되어 있는 것을 확인할 수 있습니다. 아~ 이러면 자동 생성된 이 모델 클래스는 건들 필요 없이 파샬 클래스를 하나 더 추가해서 그곳에다가 우리가 필요한 정의를 내려주면 되겠구나~ 라는 생각이 팍팍 드시죠?

그래서 추가해봤습니다. 동일한 이름의 모델 클래스를 하나 만들어 보죠. 그리고, 메타 데이타 클래스도 같이 만들겠습니다.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace MvcSite.Models

    [MetadataType(typeof(USERMetaData))]
    public partial class USER
    {      
    }

    public class USERMetaData
    {
        [Required(ErrorMessage="아이디 입력하셔야죠!")]
        [StringLength(10)]
        public object ID { get; set; }

        [Required(ErrorMessage="이름 입력하셔야죠!")]
        public object NAME { get; set; }
               
        [Required(ErrorMessage="패스워드 입력하셔야죠!")]       
        public object PWD { get; set; }
              
        [Required(ErrorMessage="이메일 입력하셔야죠!")]
        [RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$",
                                                             ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
        public object EMAIL { get; set; }
    }
}

메타 데이터의 경우 테이블의 필드값을 대신합니다. 즉 모델과 같아야 합니다.
메타 데이터를 만든 후 파샬로 된 모델(USER) 클래스에 MetadataTypeAttribute를 통해 USERMetaData을 정의합니다. 이렇게하면 1차작업이 완료됩니다. 실행해 보시면 잘 돌아갑니다. 확인페이지는 따로 보여드리지 않겠습니다^^ 글이 너무 길어지면 지루해지겠죠?

모델에 없는 필드 확인하기

우리는 패스워드 확인 필드를 갖고 있습니다. 필수값이고 비교도 해야하지만 테이블에는 없는 필드죠. DataAnnotation을 통해 나머지 필드들은 각각 비교는 했는데, 패스워드 확인 필드는 어떻게~ 어떻게~ 어떡하면 되냐고~ 띠리링~ 그냥 만들어!

헉. 뭐 만들면 되죠;;;
일단, ValidationAttribute를 상속 받는 PropertiesMatchAttribute라는 이름의 두 값을 비교할 커스텀한 DataAnnotation 클래스를 만듭니다. 중요한건 검사를 담당하게될 IsValid 메쏘드를 오버라이드해야합니다.


이렇게 만든 후에, 생성한 USER 클래스를 수정하도록 하겠습니다.

    [PropertiesMatchAttribute("PWD", "CPWD",
                                         ErrorMessage = "패스워드 확인 안하실거에요?!")]

    [MetadataType(typeof(USERMetaData))]
    public partial class USER
    {
        [Required(ErrorMessage = "패스워드 확인 입력하셔야죠!")] 
       public string CPWD { get; set; }
    }

    public class USERMetaData
    {
        [Required(ErrorMessage="아이디 입력하셔야죠!")]
        [StringLength(10)]
        public object ID { get; set; }

        [Required(ErrorMessage="이름 입력하셔야죠!")]
        public object NAME { get; set; }
               
        [Required(ErrorMessage="패스워드 입력하셔야죠!")]       
        public object PWD { get; set; }
              
        [Required(ErrorMessage="이메일 입력하셔야죠!")]
        [RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$",
                                                            ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
        public object EMAIL { get; set; }
    }

USER 클래스에 커스텀한 DataAnnotation 정의를 추가했고요, 패스워드 확인 필드를 필수값으로 정의하였습니다.
여기까지 잘 오셨죠? 실행해 보도록 하겠습니다.


위 결과물은 모든 필드에 입력을 안하고 submit을 했을 경우고, 아래 결과물은 패스워드를 다르게 입력 했을 경우입니다.


일단 원하는대로 출력되는 것을 확인했습니다.

역시 급정리요

이번시간 역시 유효성 검사 부분을 다뤘고요, 메타 데이터와 파샬 클래스를 이용한 유효성 검사를 살펴봤습니다.
정말 간단한 내용인데 쓰다보면 길어지네요;; 더 간단하게 필요한 메시지만 전달하도록 노력하겠습니다.

참조 : http://byatool.com/mvc/custom-data-annotations-with-mvc-how-to-check-multiple-properties-at-one-time

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

ASP.NET MVC 2010.05.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

Visual C++ 팀 블로그에 C++0x Core Language Features In VC10: The Table라는 이름으로 C++0x의 기능 중 코어 언어와 관련된 것 중에서 VC++ 10에 구현된 것들을 테이블 표로 정리되어 있습니다.

GCC C++0x 구현 항목 테이블 표 형식을 차용했다고 하네요.


 

 

위의 테이블 표에서는 C++0x가 처음 구현된 VC++9VC++ 10을 비교하고 있습니다.

 

그리고 글의 마지막에 작년에 Boost Con(Boost 라이브러리 관련 행사)에서 발표한 자료가 첨부 파일로 있습니다. 이 문서를 보면 VC++ 10에서 구현한 C++0x의 코어 언어 기능들을 설명하고 있습니다.

 

문서를 보니 큰 기능들은 제가 작년부터 공부하면서 저희 팀 블로그나 여러 장소에서 설명 하였지만 일부 기능은 저도 미쳐 파악 하지 못한 것들도 있더군요. 앞으로 이런 빠진 부분에 대해서 팀 블로그를 통해서 설명해 드리겠습니다.^^


 

안녕하세요. 추운날씨 잘 견디셨죠? 이제야 좀 어깨펴고 글좀 쓰겠네요. 자~ 오늘도 함께하시죠^^

이번에는 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...'
아이디부터 걸리기 시작합니다.

급 마무리요

글이 너무 길어졌습니다. 간단한 것을 이리도 길게 글을 쓰는 저를 꾸짖진 말아주세요^^
암튼, 여기서 과제가 생겼습니다. 다음 포스팅에서는 유효성 검사 부분들을 다루도록 하겠습니다.

VSTS 2010에는 이전보다 더 지능적인 코드 검색을 위해 “Navigate To”라는 강력한 기능을 제공합니다.

 

“Navigate To”를 사용하려면 “Ctrl 키 + , 키”를 누르면 아래와 같은 다이얼로그 창이 나옵니다.

 

 


“Clear”을 입력하면 아래와 같은 결과가 나옵니다.

단순하게  “Clear”이 있는 위치만 알려주는 것이 아니고 Type, 메소드/프로퍼티 이름, 필드 선언, 파일 이름을 포함한 모든 것을 보여줍니다.

 

Result에서 표시된 항목에서 찾기를 원했던 것을 마우스 클릭을 하면(아니면 Tab 키로 이동하여 선택) 해당 코드를 보여줍니다.

 

 

 

 

기억이 안 나는 단어는 “fuzzy 검색으로 찾기

 

“fuzzy 검색이라는 것은 완전한 단어를 알지 못하지만 일부 단어만을 사용하여 검색 하는 것을 가리킵니다.

 

GetStreamID 라는 함수를 찾아야 하는데  “Stream” 이라는 단어만 생각난다면 이것을 입력하면 아래와 같이 출력됩니다.

 “Stream”이라는 단어를 사용한 모든 것을 다 보여줍니다.

 

 

 

“Pascal Casing” 규약으로 검색

 

닷넷 프레임워크에서는 type이나 메소드의 이름을 “Pascal Casing” 규약으로 짓기를 권유합니다. “Pascal Casing” 방식이라는 것은 여러 단어가 합쳐서 하나의 이름이 되는 것은 해당 단어의 첫 글자를 대문자로 하는 것입니다. “get”“stream”, “id”라는 단어를 붙여서 하나의 메소드 이름을 만든다면 “GetStreamID”로 됩니다.

 

“GetStreamID”라는 단어를 “Pascal Casing” 패턴으로 찾을 때는 “GSI”라는 단어만 입력하여 찾을 수 있습니다.

 

그런데 현재 RC 버전에서는 VC++의 경우는 제대로 지원되지 않습니다. VC++의 경우는 조금 더 입력을 해야 찾아집니다.

“GetStreamID”를 예를 들면 “GStrame”으로 검색을 하면 “GetStreamID”를 찾습니다.



앞선 시간을 통해서 GPGPU 를 위해서 마이크로소프트가 제공하는 플랫폼이
DirectCompute 라는 것이라고 말씀드렸습니다.
앞으로 DirectX11 을 지원하는 모든 그래픽카드들은 이 DirectCompute 를 지원할 것입니다.
그 이외에도 일부 DirectX10 을 지원하는 그래픽카드들도 지원을 하고 있습니다.


GPGPU 를 위해서 가장 기본적이고 핵심이 되는 기능은 무엇일까요?
저는 GPU 에서 처리된 메모리를 CPU 쪽의 메모리로 보내는 것이라고 생각합니다.
( 이는 개인 의견입니다.^^ )
즉, 그래픽카드에 있는 메모리를 메인메모리로 보내는 작업입니다.
DirectX9 세대까지는 이 작업이 불가능 했습니다.
예를 들면, 그래픽스 파이프라인 중간에 처리된 결과를 다시 가공할 수 있는 방법은
VertexShader 나 PixelShader 같은 쉐이더 스테이지 정도 뿐이였습니다.

하지만 DirectX10 부터는 이들에 대한 중간 결과를 메인메모리로 보내는 기능이 추가되어지면서,
GPGPU 의 시작을 알렸다고 생각합니다.
이 단순한 Copy 작업이 앞으로도 얼마나 유용하게 사용될 수 있을지는 기대가 상당합니다.



< DirectCompute 를 위한 ComputeShader >

DirectCompute 를 위해서 개발자가 할 일은 ComputeShader 를 작성하는 일입니다.
ComputeShader 는 HLSL 이라는 기존 DirectX 의 쉐이더 문법 구조로 작성을 합니다.




HLSL 코드는 DirectX 쉐이더 컴파일러인 FXC 나 API 를 통해서 컴파일 됩니다.
HLSL 은 결국 최적화된 IL 코드를 생성하게 되고,
이 IL 코드를 기반으로 런타임에 각각의 하드웨어에 최적화된 명령어들로 변환
되어져서 실행됩니다.


< GPGPU 에게 실행이란? >

GPGPU 를 활용해서 실행한다는 것은 하드웨어 내부적으로 어떻게 동작하도록 할까요?
앞선 시간에 GPU 는 병렬 처리에 최적화된 많은 SIMD 형태로 구성되어져 있다고 언급했었습니다.
결국 이들은 스레드들의 그룹으로써 실행합니다.
스레드들을 얼마나 많이 생성할 것인지를 개발자가 정해주면, 그에 맞게 연산을 수행합니다.

API 에서는 이들을 큰 그룹으로 나누어 줍니다.
큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 입니다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에
ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정합니다.

[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}




결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 됩니다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 합니다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘오오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 됩니다.

아래에 간단한 예가 있습니다. 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있습니다.

일단 먼저 어떻게 DirectCompute 가 실행되어지는지에 대해서 살펴보았습니다.
실행까지 가기 위해서는 일련의 절차를 거쳐야 합니다.
이들에 대해서는 앞으로 차근차근 살펴보겠습니다.



참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

VC++ 10 C++0x나 병렬 프로그래밍 라이브러리 이외에도 툴적인 측면에서도 여러 좋은 기능들이 추가 되었습니다. 알고 있으면 작업할 때 편리한데 시간이 부족하여 제가 아직 자세하게 찾아보지 못해서 소개하지 못한 것이 많이 아쉽습니다. 그래서 짥은 것이라도 틈틈이 시간나면 소개하려고 합니다.

 

 

VC++ 10에서는 디버깅 모드에서도 역어셈블리 코드를 볼 수 있습니다.

 

메뉴에서 “Debug” -> “Windows” -> “Disassembly”를 선택합니다.



아래와 같이 역어셈블리 코드 창이 나타납니다.



그러나 위 화면을 보면 코드 바이트는 표시되지 않고 있습니다.

코드 바이트를 보고 싶다면 위 화면 왼쪽 상단의 “Viewing Option”을 클릭합니다.



위와 같이 옵션을 선택할 수 있습니다. 이 중 “Show code bytes”를 선택합니다.

그러면 아래와 같이 코드 바이트가 표시됩니다.



 

 

 

참고

http://d.hatena.ne.jp/kkamegawa/20100130/p1

 

nullptr

C++0x 2010.01.28 09:00 Posted by 흥배

오랜만에 팀 블로그에 C++0x 관련 글을 올립니다.

이미 알고 계시겠지만 Visual Stuido 2010 Beta2에 새로운 C++0x 기능이 추가 되었습니다.

추가된 것은 nullptr 이라는 키워드 입니다.

nullptr C++0x에서 추가된 키워드로 널 포인터(Null Pointer)를 나타냅니다.

 

 

null_ptr이 필요한 이유

 

C++03까지는 널 포인터를 나타내기 위해서는 NULL 매크로나 상수 0을 사용하였습니다.

그러나 NULL 매크로나 상수 0을 사용하여 함수에 인자로 넘기는 경우 int 타입으로 추론되어 버리는 문제가 발생 합니다.

 

< List 1 >

#include <iostream>

 

using namespace std;

 

void func( int a )

{

cout << "func - int " << endl;

}

 

void func( double *p )

{

cout << "func - double * " << endl;

}

 

int main()

{

func( static_cast<double*>(0) );

                 

func( 0 );

  func( NULL );

                 

getchar();

return 0;

}

 

< 결과 >

 


첫 번째 func 호출에서는 double* 로 캐스팅을 해서 의도하는 func이 호출 되었습니다. 그러나 두 번째와 세 번째 func 호출의 경우 func( doube* p ) 함수에 널 포인터로 파라미터로 넘기려고 했는데 의도하지 않게 컴파일러는 int로 추론하여 func( int a )가 호출 되었습니다.

 

바로 이와 같은 문제를 해결하기 위해서 nullptr 이라는 키워드가 생겼습니다.

 

 

 

nullptr 구현안

 

C++0x에서 nullptr의 드래프트 문서를 보면 nullptr은 아래와 같은 형태로 구현 되어 있습니다.

 

const class {

public:

    template <class T>

    operator T*() const

    {

        return 0;

    }

 

    template <class C, class T>

    operator T C::*() const

    {

        return 0;

    }

 

private:

    void operator&() const;

 

} nullptr = {};

 

 

 

nullptr 사용 방법

 

사용방법은 너무 너무 간단합니다. ^^

그냥 예전에 널 포인터로 0 이나 NULL을 사용하던 것을 그대로 대처하면 됩니다.

 

char* p = nullptr;

 

<List1>에서 널 포인트를 파라미터로 넘겨서 func( double* p )가 호출하게 하기 위해서는

func( nullptr );

로 호출하면 됩니다.

 



nullptr의 올바른 사용과 틀린 사용 예

 

 

올바른 사용

char* ch = nullptr; // ch에 널 포인터 대입.

sizeof( nullptr ); // 사용 할 수 있습니다. 참고로 크기는 4 입니다.

typeid( nullptr ); // 사용할 수 있습니다.

throw nullptr; // 사용할 수 있습니다.

 

 

틀린 사용

int n = nullptr; // int에는 숫자만 대입가능한데 nullptr은 클래스이므로 안됩니다.

 

Int n2 = 0

if( n2 == nullptr ); // 에러

 

if( nullptr ); // 에러

 

if( nullptr == 0 ); // 에러

 

nullptr = 0; // 에러

 

nullptr + 2; // 에러

 

 

 

nullptr 너무 간단하죠? ^^

VC++ 10에서는 예전처럼 널 포인터를 나타내기 위해서 0 이나 NULL 매크로를 사용하지 말고 꼭 nullptr을 사용하여 함수나 템플릿에서 널 포인터 추론이 올바르게 되어 C++을 더 효율적으로 사용하기 바랍니다.^^

 

 

 

짜투리 이야기...... ^^


왜 nullptr 이라고 이름을 지었을까?

nullptr을 만들 때 기존의 라이브러리들과 이름 충돌을 최대한 피하기 위해서 구글로 검색을 해보니 nullptr로 검색 결과가 나오는 것이 별로 없어서 nullptr로 했다고 합니다.

제안자 중 한 명인 Herb Sutter은 현재 Microsoft에서 근무하고 있는데 그래서인지 C++/CLI에서는 이미 nullptr 키워드를 지원하고 있습니다.

 

 

C++0x 이야기

근래에 Boost 라이브러리의 thread 라이브러리가 C++0x에 채택 되었다고 합니다. Boost에 있는 많은 라이브러리가 C++0x에 채택되고 있으므로 컴파일러에서 아직 지원하지 않는 C++0x의 기능을 먼저 사용해 보고 싶다면 꼭 Boost 라이브러리를 사용해 보기 바랍니다.

 


 

참고

http://d.hatena.ne.jp/faith_and_brave/20071002/1191322319

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf

http://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/nullptr

http://d.hatena.ne.jp/KZR/20080328/p1

 

 

[JumpToDX11-10] GPGPU 를 위한 DirectCompute.

DirectX 11 2010.01.27 09:00 Posted by 조진현


아주 오래 전 컴퓨터에는 GPU 라는 개념이 특별히 존재하지 않았습니다.
그저 화면에 얼마나 많은 픽셀을 나타낼 수 있는가 정도가 그래픽 카드의 성능을 나타내는 기준이였습니다.
그랬던 상황이 오늘 날에 이르게 된 것입니다.( 굳이 자세히 언급할 필요가 없을 것 같습니다.^^ )

오늘날의 GPU 의 성능은 가히 놀라울 정도입니다.
하지만 이런 놀라운 성능을 가진 GPU의 processing unit 들이 대부분의 시간을 놀면서 있다는 것이
우리의 신경에 거슬렸던 것입니다.
그래서 이들에게 일감을 분배시키기 위한 방안을 생각하게 되었고,
이를 배경으로 등장한 것이 바로 GPGPU 입니다.

GPU 를 활용한 일반적인 처리 방식을
GPGPU( General-purpose computing on graphics processing uints ) 라고 합니다.
범용성 있게 GPU 를 활용해서 처리하겠다는 것이지만,
사실 CPU 와 GPU 의 목적은 엄연히 다릅니다.

CPU 는 광범위한 영역에서도 효율적으로 이용될 수 있도록 설계를 된 것이지만,
GPU 는 그래픽 처리를 위한 산술 연산에 특화된 processing unit 입니다.
오늘 날 PC 는 멀티코어 형식이 많아지고 있는 추세인데,
하나의 CPU 는 기본적으로 특정 시간에 하나의 연산만 수행할 수 있습니다.
GPU 의 경우에는 병렬처리 형식에 완전히 특화된 형태입니다.
오늘날 GPU의 코어는 32개라고 합니다.
즉 32개가 연산이 동시에 실행될 수 있다는 얘기입니다.
아래 그림을 한번 보실까요?




GPU 에는 SIMD 라는 것이 굉장히 많은 것을 볼 수 있습니다.
SIMD( Single Instruction Multiple Data ) 라는 것은 병렬 프로세서의 한 종류입니다.
벡터 기반의 프로세서에서 주로 사용되는데,
하나의 명령어를 통해서 여러 개의 값을 동시에 계산할 수 있도록 해줍니다.
( http://ko.wikipedia.org/wiki/SIMD  --> 여기서 참고 했습니다^^ )

벡터 기반이라는 사실에 우리는 주목할 필요가 있습니다.
GPU 는 광범위한 목적으로 설계된 processing unit 이 아닙니다.
즉, GPGPU 를 활용하는 목적은 주로 수치 연산에만 국한된 이야기 입니다.
일반적인 로직으로 GPGPU 를 활용하는 것은 그리 좋은 선택이 아니라는 것입니다.
현재 GPGPU 가 활용되고 있는 영역은 이미지 프로세싱, 비디오 프로세싱, 시뮬레이션 등과 같이
많은 수학 연산이 필요한 영역입니다.
분명한 것은 이들 수치 연산에 국한된 모델이라 할지라도, 그 성능이 무척 매력적이라는 것입니다.

이런 GPGPU 활용을 위해서 마이크로소프트는 어떤 준비물을 가지고 등장했을까요?
그것이 바로 'DirectCompute' 라는 것입니다.^^
아래 그림을 한번 보실까요?



DirectCompute 외에도 친숙한 이름이 보이시나요?
개인적으로 현재 GPGPU 분야에서 가장 앞서 있다고 보여지는 CUDA 가 있습니다.
이것들에 대한 우열을 가리기는 어려운 문제입니다.
여러분이 처한 상황에서 최선의 선택을 하면 되는 것입니다.
그 중에 DirectCompute 도 하나의 선택지일 뿐입니다.
CUDA 도 굉장히 훌륭한 GPGPU 모델입니다.
( 사실 저도 CUDA 를 공부하면서 GPGPU 의 개념을 잡았습니다.^^ )
CUDA 는 제가 지금 언급하지 않아도 될 정도로 많은 정보들이 공개되어 있습니다.

DirectCompute 는 마이크로소프트에서 가지고 나온 GPGPU 모델입니다.
앞으로 OS 의 강력한 지원을 가지고 등장하게 될 것입니다.

사실 GPGPU 와 DirectCompute 는 매우 혼란스럽게 사용될 수 용어들입니다.
그래서 오늘은 이들 두 용어를 확실히 구분하는 것으로 마무리 하겠습니다.^^
다음 시간부터는 DirectCompute 에 대해서 조금씩 살펴보겠습니다.


참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

concurrent_queue는 사용 용도가 concurrent_vector 보다 더 많을 것 같아서 좀 더 자세하게 설명하겠습니다.

 

온라인 서버 애플리케이션의 경우 Producer-Consumer 모델이나 이와 비슷한 모델로 네트웍을 통해서 받은 패킷을 처리합니다. 즉 스레드 A는 네트웍을 통해서 패킷을 받으면 Queue에 넣습니다. 그리고 스레드 B Queue에서 패킷을 꺼내와서 처리합니다. 이 때 Queue는 스레드 A B가 같이 사용하므로 공유 객체입니다. 공유 객체이므로 패킷을 넣고 뺄 때 크리티컬섹션과 같은 동기 객체로 동기화를 해야 합니다. 이런 곳에 concurrent_queue를 사용하면 아주 좋습니다.

 

 

concurrent_queue를 사용하기 위한 준비 단계

 

너무 당연하듯이 헤더 파일과 네임스페이스를 선언해야 합니다.

 

헤더파일

#include <concurrent_queue.h>

 

네임스페이스

using namespace Concurrency;

을 선언합니다.

 

이제 사전 준비는 끝났습니다. concurrent_queue를 선언한 후 사용하면 됩니다.

concurrent_queue< int > queue1;

 

 


concurrent_queue에 데이터 추가

 

concurrent_queue에 새로운 데이터를 넣을 때는 push 라는 멤버를 사용합니다.

 

원형

void push( const _Ty& _Src );

 

STL deque push_back과 같은 사용 방법과 기능도 같습니다. 다만 스레스 세이프 하다는 것이 다릅니다. concurrent_queue는 앞 회에서 이야기 했듯이 스레드 세이프한 컨테이너이므로 제약이 있습니다. 그래서 deque 와 다르게 제일 뒤로만 새로운 데이터를 넣을 수 있습니다.

 

concurrent_queue< int > queue1;

queue1.push( 11 );

 

 

 

concurrent_queue에서 데이터 가져오기

 

데이터를 가져올 때는 try_pop 멤버를 사용합니다. 앞의 push의 경우는 STL deque와 비슷했지만 try_pop은 꽤 다릅니다.

 

원형

bool try_pop( _Ty& _Dest );

 

try_pop을 호출 했을 때 concurrent_queue에 데이터가 있다면 true를 반환하고 _Dest에 데이터가 담기며 concurrent_queue에 있는 해당 데이터는 삭제됩니다. 그러나 concurrent_queue에 데이터가 없다면 false를 즉시 반환하고 _Dest에는 호출했을 때의 그대로 됩니다.

 

concurrent_queue< int > queue1;

 

queue1.push( 12 );

queue1.push( 14 );

 

int Value = 0;

 

if( queue1.try_pop( Value ) )

{

           // queue1에서 데이터를 가져왔음

}

else

{

           // queue1은 비어 있었음.

}

 

 

 

concurrent_queue가 비어 있는지 검사

 

concurrent_queue가 비어 있는지 알고 싶을 때는 empty()를 사용합니다. 이것은 STL deque와 같습니다.

 

원형

bool empty() const;

 

비어 있을 때는 true를 반환하고 비어 있지 않을 때는 false를 반환합니다. 다만 empty를 호출할 때 비어 있는지 검사하므로 100% 정확하지 않습니다. 100% 정확하지 않다라는 것은 empty push, try_pop 이 셋은 스레드 세이프하여 동시에 사용될 수 있으므로 empty를 호출할 시점에는 데이터가 있어서 false를 반환했지만 바로 직후에 다른 스레드에서 try_pop으로 삭제를 해버렸다면 empty 호출 후 false를 반환했어 try_pop을 호출했는데 false가 반환 될 수 있습니다.

 

 

 

concurrent_queue에 있는 데이터의 개수를 알고 싶을 때

 

concurrent_queue에 있는 데이터의 개수를 알고 싶을 때는 unsafe_size 멤버를 사용합니다.

 

원형

size_type unsafe_size() const;

 

이것은 이름에서도 알 수 있듯이 스레드 세이프 하지 않습니다. 그래서 unsafe_size를 호출할 때 push try_pop이 호출되면 unsafe_size를 통해서 얻은 결과는 올바르지 않습니다.

 

 


concurrent_queue에 있는 데이터 순차 접근

 

concurrent_queue에 있는 데이터를 모두 순차적으로 접근하고 싶을 때는 unsafe_begin unsafe_end를 사용합니다.

 

원형

iterator unsafe_begin();

const_iterator unsafe_begin() const;

 

iterator unsafe_end();

const_iterator unsafe_end() const;

 

unsafe_begin을 사용하여 선두 위치를 얻고, unsafe_end를 사용하여 마지막 다음 위치(미 사용 영역)를 얻을 수 있습니다. 이것도 이름에 나와 있듯이 스레드 세이프 하지 않습니다.

 

 

 

모든 데이터 삭제


모든 데이터를 삭제할 때는 clear를 사용합니다. 이것은 이름에 unsafe라는 것이 없지만 스레드 세이프 하지 않습니다.

 

원형

template< typename _Ty, class _Ax >

void concurrent_queue<_Ty,_Ax>::clear();

 

 

 

제 글을 보는 분들은 C++을 알고 있다는 가정하고 있기 때문에 STL을 알고 있다고 생각하여 아주 간단하게 concurrent_queue를 설명 하였습니다.

 

concurrent_queue 정말 간단하지 않습니까? 전체적으로 STL deque와 비슷해서 어렵지 않을 것입니다. 다만 스레드 세이프 하지 않은 것들이 있기 때문에 이것들을 사용할 때는 조심해야 된다는 것만 유의하면 됩니다.

 

이것으로 Concurrency Runtime PPL에 대한 설명은 일단락 되었습니다.

이후에는 Concurrency Runtime의 다른 부분을 설명할지 아니면 Beta2에서 새로 추가된 C++0x의 기능이나 또는 이전에 설명한 것들을 더 깊게 설명할지 고민을 한 후 다시 찾아 뵙겠습니다.^^

 

 


참고

Producer-Consumer 모델 : 자바워크님의 http://javawork.egloos.com/2397148

MSDN concurrent_queue :

http://msdn.microsoft.com/en-us/library/dd504906(VS.100).aspx#queue

 

안녕하세요. 아직 떨린 마음을 감추지 못하는 팀블로그 새내기 박세식입니다. 너무나 오랜만에 포스팅을 하게되네요. 제 개인사까지 다 말씀드릴 필요는 없겠지만, 여러분 모두 새해에는 하시는일 모두 잘되었으면 좋겠습니다. 진심으로요ㅠㅠ 늦었지만 새해 복 많이 받으세요^^

이번 시간은 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/



이번 시간에는 Multi-threaded Rendering 을 위한 API 들에 대해서 살펴보겠습니다.
기능 위주의 설명을 위해서 인자들에 대한 명시는 생략했습니다.
이점 주의해주시기 바랍니다.

ID3D11Device::CreateDeferredContext()

가장 먼저 살펴볼 것은 DeferredContext 의 생성입니다.
DeferredContext 는 스레드당 하나씩 생성되어질 수 있음을 앞선 시간을 통해서 언급했습니다.
또한 이 DeferredContext 는 Command List 들을 생성해서 가지고 있습니다.
즉, 렌더링이 가능한 상태라는 것입니다.
그런 기능을 우리는 Device 인터페이스를 통해서 생성합니다.
이것은 역시 Free thread 한 작업이기 때문에 Device 인터페이스를 이용합니다.

하나의 DeferredContext 는 thread-safe 합니다.
즉, 스레드 상에서 DeferredContext 가 관련 Command 들을 기록하는 것은 안전한 작업입니다.

간단한 사용 방법은 아래와 같습니다.
ID3D11DeviceContext* pDeferredContext = NULL;
hr = g_pd3dDevice->CreateDeferredContext(0, &pDeferredContext);


ID3D11DeviceContext::FinishCommandList()

신기하게도 우리는 이 API 호출 한번으로 CommandList 들을 기록하고 생성할 수 있습니다.
API 이름이 Finish 여서 Start나 Begin 계열의 API 를 검색해 보았지만, 없었습니다.^^
각각의 DeferredContext 별로 호출되기 때문에 DeviceContext 의 멤버함수로 되어 있습니다.
앞선 시간을 통해서 DeviceContext 는 ImmeidateContext 와 DeferredContext 로
분리될 수 있다고 언급했었습니다.
두 Context 모두 ID3D11DeviceContext 인터페이스를 사용하기 때문에 오해의 소지가 약간 있습니다.
FinishCommandList 는 DeferredContext 를 위한 API 임을 유념하시기 바랍니다.

간단한 사용 방법은 다음과 같습니다.
ID3D11CommandList* pd3dCommandList = NULL;
hr = pDeferredContext->FinishCommandList( FALSE, &pd3dCommandList );


ID3D11DeviceContext::ExecuteCommandList()

이 API는 DeferredContext 에 의해서 생성된 CommandList 들을 실행합니다.
역시나 ID3D11DeviceContext 의 멤버함수이기 때문에 혼란스러울 수 있습니다.
과연 ImmediateContext 가 이 함수를 호출할까요? 아니면, DeferredContext 일까요?

지난 시간들을 통해서 우리는 실제로 Multi-threaded Rendering 이라는 것은
CommandList 생성을 Multi-thread 기반으로 하는 것이라고 언급했었습니다.
그 이후에 실제 그래픽 카드로의 전송은 하나의 스레드만 할 수 있다고 했었습니다.
바로 그 사실입니다.
이 함수는 ImmediateContext 에 의해서 호출됩니다.
즉, 이 API 는 그래픽 카드로 해당 CommandList 들을 전송하는 것입니다.

간단한 사용 방법은 아래와 같습니다.
g_pImmediateContext->ExecuteCommandList( g_pd3dCommandList, TRUE );


이상 3가지 API 에 대해서 살펴보았습니다.
믿기지 않으시겠지만(?)
Multi-threaded Rendering 작업은 이 세가지 API로 할 수 있습니다.
나머지는 스레드 생성과 제어를 위한 작업이 결합되어야 할 것입니다.
일반적인 스레드 프로그래밍과 관련된 내용이라 이곳에서는 배제를 했습니다.
현재 DirectX SDK Sample 에는 'MultithreadedRendering11' 라는 것이 있습니다.( 2009 August 버전 기준 )
이것과 관련된 소스가 있으니 참고해서 보시면 좋을 것 같습니다.

이상으로 Multi-threaded Rendering 의 기본 개념 설명을 마치고자 합니다.
이 부분과 관련된 내용은 앞으로 정리가 되는대로 추가하거나 수정이 되어질 수 있을 것입니다.
다음 시간부터는 DirectX11 의 다른 주제를 가지고 돌아오겠습니다.^^
 
안녕하세요. 이번에 새롭게 팀원이된 박세식이라고 합니다. 많이 부족해서 컴터앞에 앉아 이렇게 자판을 두드리고 있는 시간에도 떨림이 멈추질 않네요-_-; 앞으로 잘 부탁드리겠습니다.(따스~한 피드백 아시죠^^;)  
자~ 그럼. 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

concurrent_queuequeue 자료구조와 같이 앞과 뒤에서 접근할 수 있습니다.

concurrent_queue는 스레드 세이프하게 enqueue와 dequeue(queue에 데이터를 넣고 빼는) 조작을 할 수 있습니다.

또 concurrent_queue반복자를 지원하지만 이것은 스레드 세이프 하지 않습니다.

 



concurrent_queuequeue의 차이점


concurrent_queuequeue는 서로 아주 비슷하지만 다음과 같은 다른 점이 있습니다.

( 정확하게는 concurrent_queue와 STL의 deque와의 차이점 이라고 할수 있습니다. )


- concurrent_queue enqueue dequeue 조작이 스레드 세이프 하다.


- concurrent_queue는 반복자를 지원하지만 이것은 스레드 세이프 하지 않다.


- concurrent_queue front pop 함수를 지원하지 않는다.

  대신에 try_pop 함수를 대신해서 사용한다.


- concurrent_queue back 함수를 지원하지 않는다.

  그러므로 마지막 요소를 참조하는 것은 불가능하다.


- concurrent_queue size 메소드 대신 unsafe_size 함수를 지원한다.

  unsafe_size는 이름 그대로 스레드 세이프 하지 않다.


 

 

스레드 세이프한 concurrent_queue의 함수


concurrent_queue에 enqueue 또는 dequeue 하는 모든 조작에 대해서는 스레드 세이프합니다.

 

- empty

- push

- get_allocator

- try_pop

 

empty는 스레드 세이프하지만 empty 호출 후 반환되기 전에 다른 스레드에 의해서 queue가 작아지던가 커지는 경우 이 동작들이 끝난 후에 empty의 결과가 반환됩니다.

 



스레드 세이프 하지 않은 concurrent_queue의 함수

 

- clear

- unsafe_end

- unsafe_begin

- unsafe_size

 

 


반복자 지원

 

앞서 이야기 했듯이 concurrent_queue는 반복자를 지원하지만 이것은 스레드 세이프 하지 않습니다. 그래서 이것은 디버깅 할 때만 사용할 것을 추천합니다.

또 concurrent_queue의 반복자는 오직 앞으로만 순회할 수 있습니다.


concurrent_queue는 아래의 반복자를 지원합니다.

 

- operator++

- operator*

- operator->

 

 

concurrent_queue는 앞서 설명한 concurrent_vector와 같이 스레드 세이프한 컨테이너지만 STL vector deque에는 없는 제약 사항도 있습니다. 우리들이 Vector deque를 스레드 세이프하게 래핑하는 것보다는 Concurrency Runtime에서 제공하는 컨테이너가 성능적으로 더 좋지만 모든 동작이 스레드 세이프하지 않고 지원하지 않는 것도 있으니 조심해서 사용해야 합니다.

 

 

다음에는 일반적인 queue에는 없고 concurrent_queue에서만 새로 생긴 함수에 대해서 좀 더 자세하게 설명하겠습니다.


ps : 앞 주에 Intel의 TBB에 대한 책을 보았습니다. 전체적으로 Concurrency Runtime과 비슷한 부분이 많아서 책을 생각 외로 빨리 볼 수 있었습니다. 제 생각에 TBB나
Concurrency Runtime를 공부하면 다른 하나도 아주 빠르고 쉽게 습득할 수 있을 것 같습니다.


concurrent_vector의 주요 멤버

 

자주 사용하는 것들과 STL vector에 없는 것들을 중심으로 추려 보았습니다.

멤버

스레드 세이프

 

at

O

 

begin

O

 

back

O

 

capacity

O

 

empty

O

 

end

O

 

front

O

 

grow_by

O

new

grow_to_at_least

O

new

max_size

O

 

operator[]

O

 

push_back

O

 

rbegin

O

 

rend

O

 

size

O

 

assign

X

 

clear

X

 

reserve

X

 

resize

X

 

shink_to_fit

X

new

 

concurrent_vector는 기존 요소의 값을 변경할 때는 스레드 세이프하지 않습니다. 기존 요소의 값을 변경할 때는 동기화 객체를 사용하여 lock을 걸어야 합니다.

 

 

concurrent_vector 사용 방법

 

concurrent_vector를 사용하기 위해서 먼저 헤더 파일을 포함해야 합니다.

concurrent_vector의 헤더 파일은 “concurrent_vector.h” 입니다.

 

concurrent_vector의 사용 방법은 STL vector를 사용하는 방법과 거의 같습니다. 그러니 STL vector에 없는 것들만 제외하고는 vector를 사용하는 방법을 아는 분들은 따로 공부해야 할 것이 거의 없습니다.

STL vector에 대해서 잘 모르시는 분들은 About STL : C++ STL 프로그래밍(4)-벡터 글을 참고해 주세요.

 


 

concurrent_vector 초 간단 사용 예


concurrent_vector를 사용한 아주 아주 간단한 예제입니다.^^

 

#include <ppl.h>

#include <concurrent_vector.h>

#include <iostream>

 

using namespace Concurrency;

using namespace std;

 

 

int main()

{

           concurrent_vector< int > v1;

           v1.push_back( 11 );

           return 0;

}

 

 


STL vector에는 없는 grow_by, grow_to_at_least 사용 법

 

grow_by vector의 크기를 확장해 줍니다.

예를 들어 현재 vector의 크기가(size()에 의한) 10인데 이것을 20으로 키우고 싶을 때 사용합니다.

 

원형은 아래와 같습니다.

iterator grow_by( size_type _Delta );

iterator grow_by( size_type _Delta, const_reference _Item );

 

grow_to_at_least는 현재 vector의 크기가 10인데 이것이 20보다 작을 때만 20으로 증가시키고 싶을 때 사용합니다.

원형은 아래와 같습니다.

iterator grow_to_at_least( size_type _N );

 

grow_bygrow_to_at_least의 반환 값은 추가된 처음 요소의 위치가 반복자입니다.

 

grow_by의 예제 코드입니다.

void Append ( concurrent_vector<char>& vector, const char* string) {

    size_t n = strlen(string) + 1;

    memcpy( &vector[vector_grow_by(n)], string, n+1 );

}

위 예제는 http://japan.internet.com/developer/20070306/27.html 에서 참고했습니다.

 

 


shink_to_fit


shink_to_fit는 메모리 사용량과 단편화를 최적화 시켜줍니다. 이것은 메모리 재할당을 하기 때문에 요소에 접근하는 모든 반복자가 무효화됩니다.


 

Intel TBB


CPU로 유명한 Intel에서는 멀티코어 CPU를 만들면서 병렬 프로그래밍을 좀 더 쉽고, 안전화고, 확장성 높은 프로그램을 만들 수 있도록 툴과 라이브러리를 만들었습니다.

라이브러리 중 TBB라는 병렬 프로그래밍 용 라이브러리가 있습니다. 아마 TBB를 아시는 분이라면 Concurrent Runtime PPL에 있는 것들이 TBB에 있는 것들과 비슷한 부분이 많다라는 것을 아실 것입니다.

VSTS 2010 Beta2가 나온지 얼마 되지 않아서 병렬 컨테이너에 대한 문서가 거의 없습니다. 그러나 TBB에 관한 문서는 검색을 해보면 적지 않게 찾을 수 있습니다. concurrent_vector에 대해서 좀 더 알고 싶은 분들은 Intel TBB에 대해서 알아보시면 좋을 것 같습니다.

( 참고로 TBB 관련 서적이 한국어로 근래에 출간되었습니다.  http://kangcom.com/sub/view.asp?sku=200911100001 )

 


다음에는 concurrent_queue에 대해서 알아 보겠습니다.