'개발/jQuery'에 해당되는 글 4건

  1. 2010.03.12 jQuery UI Theme 4
  2. 2010.02.22 Practice : jQuery Dialog & Validity
  3. 2010.02.14 Error Handling
  4. 2010.02.09 Practice : jQuery DataTables

jQuery UI Theme

개발/jQuery 2010. 3. 12. 09:37

jQuery UI Theme를 Customizing하는 방법에 대해서 알아보도록 하겠습니다.

먼저 jQeury UI CSS Framework에 대한 구조를 살펴보고 아래의 2가지 방법을 통해서 Theme를 변경해보도록 하겠습니다.
1. ThemeRoller를 이용한 방법
2. jQuery UI CSS Framework를 직접 변경하는 방법

jQuery UI의 Theme는 표준 스타일과 각 컨트롤의 스타일이 합쳐져서 구성됩니다. jQuery UI Theme중에 'ui-lightness'를 다운받고 압축을 풀어서 themes폴더를 보면 아래 그림과 같습니다.
=> 앞에서 말한 표준 스타일에 해당하는 파일은 'ui.theme.css'입니다. 'ui.core.css'는 Layout Helpers, Icos 등에 대한 클래스들이 존재하고 'ui.base.css'는 'ui.core.css'와 각 컨트롤에 대한 css파일을 import해주는 파일이며 ui.all.css는 'ui.base.css'와 'ui.theme.css'파일을 import해주는 파일입니다. 그리고 마지막으로 'jquery-ui-1.7.2.custom.css'는 이런 모든 파일을 단순히 모아놓은 파일이고 실질적으로 프로젝트를 진행하면서는 이 파일 하나만을 참조해서 사용합니다.
'ui.theme.css'파일을 열어보면 widget이라는 container가 보입니다. 이것은 각 컨트롤마다 공통되는 부분의 스타일을 정의해놓은 클래스입니다. 따라서 ui-widget-header를 변경해주면 jQuery UI 컨트롤인 dialog, datepicker, tabs, progressbar등 모든 컨트롤의 header가 변경됩니다.
개인적으로 가장 재미있는 부분은 바로 icon입니다. images폴더에 보면 하나의 파일에 여러 가지 아이콘 모양이 있는 것이 보입니다. 그리고 똑같은 위치에 색깔만 다른 아이콘 가지고 있는 것을 볼 수 있습니다.


.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }

 

.ui-icon-alert { background-position: 0 -144px; }

.ui-icon-info { background-position: -16px -144px; }

.ui-icon-notice { background-position: -32px -144px; }

.ui-icon-help { background-position: -48px -144px; }

.ui-icon-check { background-position: -64px -144px; }

.ui-icon-bullet { background-position: -80px -144px; }

.ui-icon-radio-off { background-position: -96px -144px; }

.ui-icon-radio-on { background-position: -112px -144px; }

.ui-icon-pin-w { background-position: -128px -144px; }

.ui-icon-pin-s { background-position: -144px -144px; }

.ui-icon-play { background-position: 0 -160px; }

.ui-icon-pause { background-position: -16px -160px; }

.ui-icon-seek-next { background-position: -32px -160px; }

=> icon 역시 widget으로 각 부분(여기서는 header)별로 지정할 수 있습니다. 백그라운 이미지로 하나의 파일을 첨부해주는 것을 볼 수 있으며 파일명에서 그 색깔(ffffff:흰색)을 알 수 있습니다. 그리고 밑을 보면 하나의 파일에 position으로 아이콘을 나타내고 있습니다. 그래서 첨부되는 아이콘 파일은 한 가지이며 그 위치가 모두 동일한 색만 다른 파일 이였던 것입니다. 이런 파일을 만드는 것은 어렵겠지만 이미 만들어진 파일이 있으므로 해당 파일만 변경해주면 jQuery UI에 사용된 모든 아이콘의 색을 변경할 수 있는 것입니다. 바로 css를 사용하는 이유이기도 합니다.

그러면 이제 테마를 변경해보겠습니다.
1. ThemeRoller를 이용한 방법
http://jqueryui.com/themeroller/

아래의 사이트를 가보면 갤러리에 이미 20개정도의 테마가 존재하며 해당 테마를 선택하고 이름에서도 알 수 있듯이 font, header, content 등을 변경하면 오른쪽에 화면에서 적용되는 컨트롤 전부를 볼 수 있습니다. 이렇게 변경한 이후에 download theme 를 클릭하게 되면 변경된 테마로 jQuery UI를 다운받을 수 있습니다.

2. jQuery UI CSS Framework를 직접 변경하는 방법
특정한 스타일로 변경해야 하는 것이 아니라면 ThemeRoller를 이용하면 대부분 해결이 됩니다. 그러나 특정 이미지를 백그라운드 이미지로 하고 싶은 경우처럼 수동으로 변경해야 할 경우도 생길 수 있습니다.
만약에 dialog 컨트롤에 백그라운드 이미지로 특정한 파일을 보여주고 싶다면 어떻게 해야 될까요?

'jquery-ui-1.7.2.custom.css'파일에서 아래의 부분을 변경해주시면 됩니다.
.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }

.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; }

.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }

.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }

.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }

.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }

.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }

.ui-dialog .ui-dialog-buttonpane button { background-image: url(images/파일명) ; float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }

 

=> dialog 컨트롤이기 때문에 당연히! ui-dialog클래스를 찾아주면 됩니다. 위와 같이 이름만 보아도 알 수 있는 클래스와 그 클래스에 대한 속성들이 나열되어 있습니다. dialog 컨트롤에는 여러 개의 버튼이 올 수 있기 때문에 buttonpane이라는 것도 보이고 titlebar, cotent 등등 변경이 참 쉬워 보입니다. ^^
Posted by resisa
,


이번 포스트에서는 jQuery UI Dialog Validity라는 플러그인을 사용해보도록 하겠습니다. 먼저 아래의 사이트에서 다운로드 받으시면 됩니다.
http://jqueryui.com/
http://validity.thatscaptaintoyou.com/

Validity
에서 Validation 메세지를 보여주는 방식은 3가지 있습니다. Label, Modal, Summary입니다. Summary방식을 사용하면 Validation 메세지를 한꺼번에 지정된 위치에 보여줄 수 있어 Summary 방식을 사용하였습니다. 그런데 여러 개의 Dialog Summary를 사용하다 보니 Dialog에서 Validation를 체크하는 코드가 실행되고 난 이후에 다른 Dialog에의 Validation메세지가 한꺼번에 보여지는 문제점이 있었습니다. Validity 코드를 살펴보니 validity-summary-container라고 class가 지정된 Dialog Validation를 사용하고 있었기 때문입니다. 그래서 저는 마스터 페이지에 Validation메세지를 보여주는 div태그를 하나 두고 해당 div태그를 Dialog prepend해주는 방식으로 사용했습니다.

마스터 페이지 추가 코드

<div id="validity" style="display:none" class="validity-summary-container">

    <ul></ul>

</div>


초기화 코드

$(function() {

 

    $.validity.setup({ outputMode: "summary" });

 

    $("#dialog").dialog({

        width: "auto",

        bgiframe: true,

        autoOpen: false,

        modal: true,

        resizable: false,

        close: function() {

            var allFields = $([]).add($("#id")).add($("#name"));

            allFields.val('');

            $.validity.clear();

        }

    });

 

    $("#create").click(Create);

    $("#modify").click(Modify);

 

});

 

 

<input type="button" id="create" value="Create" />

<input type="button" id="modify" value="Modify" />

   

<div id="dialog" >

    <table>

        <tr>

            <td>ID</td>

            <td><input type="text" id="id" /></td>

        </tr>

        <tr>

            <td>Name</td>

            <td><input type="text" id="name"/></td>

        </tr>

    </table>

</div>

=> 먼저 Validity의 모드를 Summary로 해준 것을 볼 수 있고 dialog()를 사용하여 해당 div태그를 초기화 해주었습니다. 이렇게 초기화를 해주게 되면 해당 div태그는 화면에 보이지 않는 효과가 있습니다. 그리고 Create, Modify 이벤트를 등록해주는 것을 볼 수 있습니다. Delete와 같은 경우에는 Delete에 해당하는 Dialog를 따로 만들어서 사용하는 것이 바람직해 보여 여기서는 제외하였습니다.
close
를 살펴보면 재미있는 문법이 보입니다. jQuery Object를 배열 형태로 만들어서 id name이라는 jQuery Object Add해주고 val('')이라는 메소드를 사용하여 배열 안에 존재하는 모든 jQuery Object를 초기화 해주는 것을 볼 수 있습니다. 실질적으로 여러 데이터가 존재하고 초기화 해주는 내용이 다르다면 하나씩 초기화 해주는 것이 맞지만 여기서는 해당 문법이 재미있어 보여 한 번 사용해보았습니다. 그리고 Validity Clear()메소드를 호출해주어서 Validation메세지도 초기화 해줍니다. 여기서 close가 가장 중요한 부분인데 이제 Create, Modify에서 만들게 될 Buttons에서 이 close를 호출해줌으로써 Dialog의 상태가 항상 초기화 상태로 돌아갈 수 있게 되는 것입니다. 그러면 Create에 대한 코드를 먼저 보도록 하겠습니다.

function Validate() {

    $.validity.start();

    $("#id").require();

    $("#name").require();

    var result = $.validity.end();

 

    $("#dialog").prepend($("#validity"));

    return result.valid;

}

          

function Create() {

 

    var options = {

        title: "Create",

        buttons: {

            OK: function() {

                if (Validate()) {

                    // Execute Server Side Call

                    $(this).dialog('close');

                }

            },

            Cancle: function() {

                $(this).dialog('close');

            }

        }

    };

   

    $('#dialog').dialog('option', options);

    $('#dialog').dialog('open');

}

=> $(function(){})에서 Create이벤트를 추가해주었기 때문에 Create라는 button를 클릭하게 되면 위의 Create 함수가 실행이 됩니다. 먼저 Dialog title를 지정해주고 button를 만들어줍니다. 여기서는 ok, cancle 두 개의 버튼을 만들어주는데 공통적으로 $(this).dialog('close')를 호출하는 것을 볼 수 있습니다. 바로 이 부분이 이전에 초기화 코드에서의 close부분을 호출해주는 것입니다. ok버튼에서는 Validate라는 함수를 호출해줍니다. Validate라는 함수를 살펴보면 require라는 Validity에서 제공해주는 함수를 사용하는데 id, name중에 하나라도 공백으로 놔두고 ok버튼을 클릭하면 false가 리턴되어 Validation를 체크하는 것입니다. 이외에도 match, range, greaterThan 등등 많은 함수를 Validity에서 제공해주며 사용방법은 Validity 사이트의 문서를 참고하시기 바랍니다.

거의 모든 경우의 Create Modify의 양식은 똑같을 것입니다. 이번에는 Modify 함수의 코드를 보도록 하겠습니다.

function Modify() {

   
var
options = {
        title:
"Modify"
,
        buttons: {
            OK:
function
() {
               
if
(validate()) {
                   
// Execute Server Side Call                   
                    $(this).dialog('close');

                }

            },

            Cancle: function() {

                $(this).dialog('close');

            }

        }

    };

 

    $('#dialog').dialog('option', options);

    $('#dialog').dialog('open');

}

=> Create와 유사합니다. 실질적으로 모두 구현을 한다면 buttons에의 ok버튼의 Server Side를 호출해주는 부분과 Modify기 때문에 Dialog를 띄우기 전에 id, name에 수정되기 전의 값을 조회해서 넣어주는 코드가 필요합니다.
jQuery UI
Dialog를 이용하여 CRUD를 해줄 경우에 이러한 방법으로 구현하는 것이 제가 사용하는 가장 심플한 방법입니다.

 

 

Posted by resisa
,

Error Handling

개발/jQuery 2010. 2. 14. 17:43

jQuery ASP.NET MVC 액션 메소드에서 Error Handling에 대해서 검색을 해보았습니다.
먼저 jQuery에서는 아래의 사이트처럼 처리하였습니다.

http://www.maheshchari.com/jquery-ajax-error-handling/

$().ready(function(){

      $.ajaxSetup({

            error:function(x,e){

                  if(x.status==0){

                  alert('You are offline!!\n Please Check Your Network.');

                  }else if(x.status==404){

                  alert('Requested URL not found.');

                  }else if(x.status==500){

                  alert('Internel Server Error.');

                  }else if(e=='parsererror'){

                  alert('Error.\nParsing JSON Request failed.');

                  }else if(e=='timeout'){

                  alert('Request Time out.');

                  }else {

                  alert('Unknow Error.\n'+x.responseText);

                  }

            }

      });

});

=> ajaxSetup()이라는 jQuery함수가 있네요. error State에 따라서 if문으로 필터를 해주네요한 가지 주의하실 점은 직접 $.ajax()를 호출해줄 경우에 error처리를 해주면 ajaxSetup에서 설정한 error처리 루틴은 실행되지 않는다는 것입니다.


서버쪽 액션 메소드에서는 아래의 사이트처럼 처리해주었습니다.

http://blog.dantup.com/2009/04/aspnet-mvc-handleerror-attribute-custom.html

 

 

protected override void OnException(ExceptionContext filterContext)

{ 

// Error Handle : ex) Log

 

filterContext.ExceptionHandled = true;

this.View("Error").ExecuteResult(this.ControllerContext);

}

=> Controller OnExeption메소드를 override해서 Exception true로 변경해주고 예외 발생시에 보여줘야 할 페이지와 ControllerContext ExcuteResult()에 매개변수로 넘겨주는 것을 볼 수 있습니다. ErrorHandle Attribute를 사용할 수도 있는데 제가 생각했던대로 동작하지 않았고 OnException메소드에서 모든 예외를 한꺼번에 처리하는 방법이 구조상 심플해보입니다.

 

클라이언트에서는 AjaxSetup로 서버쪽에서는 OnException메소드에서 Error를 처리해줍니다. 그런데 클라이언트에서 Ajax호출시에 서버쪽 액션 메소드에서 예외가 발생하는 경우 서버쪽에서 OnExcption에서 Error 처리해주는 로직이 수행되고나면 클라이언트의 Error의 상태가 변경되는 문제점이 있었습니다. 그렇게 크게 문제가 되지는 않는다는 생각과 일단은 상태가 변경되는 부분에 주석을 걸어주고 Unknow Error쪽의 루틴이 실행되도록 변경하였습니다.

 

 

Posted by resisa
,

이전 포스트에서 jQuery Table Plugin 중에 DataTables을 소개해드렸습니다. 이번 포스트에서는 DataTables를 사용하는 제가 알고 있는 가장 Best Practics한 사용법을 설명드리도록 하겠습니다.

먼저 필요한 기능들을 나열해보도록 하겠습니다.
 - 헤더를 클릭하여 열 정렬이 가능해야 한다.
 - Paging이 가능해야 한다. (1, 2, 3, 4 숫자로 표시되는 Paging)
 - 해당 Page의 보이는 Data만을 조회하여야 한다. (성능이 떨어지면 안된다.)
 - CSS를 통해서 사용자 정의 스타일이 가능해야 한다.

가장 중요한 기능은 Page를 나타내는 테이블을 구현하려고 하다 보니 성능적인 문제가 제일 문제가 되는데 이러한 것은 DataTables의 Server-Side를 호출해주는 방법으로 해결할 수 있습니다. DataTables의 Example을 살펴보면 Server-Side코드들이 php로 작성된 코드들이라 ASP.NET MVC에 맞는 코드를 Forum에서 찾아보았습니다. 여기서 한가지 알아야 할 사항이 있는데 Client에서 Sever쪽 액션 메소드(MVC)를 호출하고 액션 메소드의 결과를 Json타입으로 직렬화하여 리턴해줄 때의 Data형태를 DataTables이 알 수 있는 형태로 리턴을 해주어야 합니다. (물론 일반적인 List<T>타입을 Json타입으로 Serialize해준 후에 Client측에서 DataTables이 원하는 Data형태로 변경해줄 수도 있습니다.)

1. http://weblogs.asp.net/zowens/archive/2010/01/19/jquery-datatables-plugin-meets-c.aspx
2. http://datatables.net/forums/comments.php?DiscussionID=932&page=1#Item_0

첫 번째 방법으로 먼저 시도를 해보았는데 사용하는 방법이 쉽지만 'Caveat'부분을 보면 버그가 있다는 경고 메세지가 있고 실제로 시도해보았을 경우에 단순히 하나의 테이블에서 string의 타입만 있을 경우에는 문제가 없었지만 여러 가지 타입이나 테이블 조인의 경우에 문제가 있어 아쉽게 사용하지 못하게 되었습니다.

두 번째 방법을 살짝 Custom하게 고쳐서 사용해보도록 하겠습니다.

ORM은 Entity Framework를 사용하였으면 DB모델은 아래 그림과 같으며 Script파일을 첨부하였습니다.



Client-Side
var logTable;

 

$(function() {

 

    $("#logList tbody").click(function(event) {

        $(logTable.fnSettings().aoData).each(function() {

            $(this.nTr).removeClass('row_selected');

        });

        $(event.target.parentNode).addClass('row_selected');

    });

 

    logTable = $("#logList").dataTable({

        "bAutoWidth": false,

        "iDisplayLength": 10,

        "bLengthChange": false,

        "bInfo": false,

        "sPaginationType": "full_numbers",

        "bProcessing": true,

        "bServerSide": true,

        "aaSorting": [[0, "asc"]],

        "aoColumns": [

                        { "bVisible": true, "sClass": "Center" },

                        null,

                        null

                            ],

        "sAjaxSource": '<%= Url.Action("GetAllChildren", "Home") %>'

    });

});

 

<table id="logList" class="display">

    <thead>

        <tr>

            <th>P_ID</th>

            <th>P_Name</th>

            <th>C_COUNT</th>

        </tr>

    </thead>

    <tbody></tbody>

</table>

=> click()부분은 Table에서 해당 로우를 선택하였을 경우에 해당 로우만 다른 색깔로 표시하는 것이며 클릭하였을 경우에 액션을 취하고 싶을 경우에 사용하시면 됩니다. logTable이라는 변수를 선언해주는 이유는 DataTable의 sAjaxSource부분이 MVC의 액션 메소드를 호출해주는 부분인데 액션 메소드 호출시에 특정한 상황에 따라서 동적으로 변하는 매개변수를 넘길 경우가 있을 수 있습니다. 이럴경우에 logTable이라는 변수를 사용해서 sAjaxSource를 내용을 고치고 Table의 내용도 변경된 내용으로 Refresh하기 위함입니다. 기타 옵션들은 그 단어만 봐도 어떤 것인지 어느 정도는 알 수 있으며 자세한 사항은 DataTables 사이트를 참조하시기 바랍니다. DataTables를 다운 받아보시면 Demo_Table.css파일이 있는데 이 파일에서 스타일을 원하는 형태로 변경하셔서 사용하실 수 있습니다.

Server-Side Helper Class
public class GridParams

{

    public int iDisplayStart { get; set; }

    public int iDisplayLength { get; set; }

    public string sSearch { get; set; }

    public bool bEscapeRegex { get; set; }

    public int iColumns { get; set; }

    public int iSortingCols { get; set; }

    public int iSortCol_0 { get; set; }

    public string sSortDir_0 { get; set; }

    public int sEcho { get; set; }

    public bool bSortable_0 { get; set; }

    public bool bSearchable_0 { get; set; }

}

 

public class FormatedList

{

    public int sEcho { get; set; }

    public int iTotalRecords { get; set; }

    public int iTotalDisplayRecords { get; set; }

    public string[][] aaData { get; set; }

}

 

public static class QuerableExtensions

{

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool asc)

    {

        var type = typeof(T);

        string methodName = asc ? "OrderBy" : "OrderByDescending";

        var property = type.GetProperty(propertyName);

        var parameter = Expression.Parameter(type, "p");

        var propertyAccess = Expression.MakeMemberAccess(parameter, property);

        var orderByExp = Expression.Lambda(propertyAccess, parameter);

        MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));

        return source.Provider.CreateQuery<T>(resultExp);

    }

}

 

public static class GridsUtils

{

    public delegate string[] GetStringDelegate<T>(T obj);

 

    public static string[][] GetStrings<T>(this IQueryable<T> source, GetStringDelegate<T> del)

    {

        string[][] retArray = new string[source.Count()][];

        int i = 0;

        foreach (var s in source)

        {

            retArray.SetValue(del(s), i);

            i++;

        }

        return retArray;

    }

}

=> GridParams클래스는 열을 클릭하거나 페이지를 클릭하거나 조회 상자에서 조회하는 내용 등의 정보를 Server쪽에서 받기 쉽게 정의해놓은 클래스입니다. FormatList클래스는 위의에 말했듯이 DataTable이 인식할 수 있는 정보를 받기 위해 정의해놓은 클래스입니다. 참고적으로 iTotalDisplayRecord는 Page에 나타나는 숫자를 의미합니다. QuerableExtensions클래스는 정렬 시에 사용하기 위해서 확장해놓은 클래스인데 해당 클래스의 프로퍼티와 asc, desc인지 여부를 Expression으로 구현해놓았습니다. 그런데 헤더 정렬시에 꼭 프로퍼티 이름만으로 정렬을 하는 것이 아니라 자식 프로퍼티의 개수로도 소트할 수 있기 때문에 꼭 사용하지는 않습니다. GridsUtils클래스는 IQeuryable<T>형태로 반환된 결과물을 string[][]의 형태로 만들어주기 위한 Helper 클래스입니다.

Server-Side 액션 메소드
public ActionResult GetAllParent(GridParams gridParams)

{

    using (TestEntities Context = new TestEntities())

    {

        //data

        var query = Context.Parent.Include("Children").AsQueryable();

 

        int total = query.Count();

 

        //search whatever property you like

        if (!string.IsNullOrEmpty(gridParams.sSearch))

        {

            query = from x in query

                    where x.P_Name.Contains(gridParams.sSearch)

                    select x;

        }

 

        //sort

        if (gridParams.iSortingCols > 0)

        {

            var propertyName = "P_ID";

 

            switch (gridParams.iSortCol_0)

            {

                case 0: propertyName = "P_ID"; break;

                case 1: propertyName = "P_Name"; break;

            }

 

            query = query.OrderBy(propertyName, (gridParams.sSortDir_0 == "asc"));

        }

 

        //display

        int totaldisp = query.Count();

        if (gridParams.iDisplayLength > 0)

            query = query.Skip(gridParams.iDisplayStart).Take(gridParams.iDisplayLength);

 

        return Json(new FormatedList

        {

            sEcho = gridParams.sEcho,

            iTotalRecords = total,

            iTotalDisplayRecords = totaldisp,

            aaData = query.GetStrings(t => new string[]

            {

                t.P_ID.ToString(),

                t.P_Name,

                t.Children.Count.ToString()

            })

        });

    }

}

=> 여기서 제일 중요한 부분은 AsQueryable()로 IQueryable<T>로 리턴해주는 부분입니다. 필요한 기능목록에서 Sort와 Page시에 사용되는 매개변수(GridParams)와 함께 실질적인 쿼리만을 만들어주며 Json타입으로 Serialize해주는 부분에서 실질적인 쿼리가 DB에 질의 됩니다. 물론 그 전에 Count를 알기 위한 쿼리는 Count()를 호출해줄 때마다 질의됩니다.

그런데 아마 Page, Sort에 대해서 궁금증이 생기실 수 있습니다. 과연 얼마나 효율적으로 데이터를 가져올까요? 전체 데이터를 가져와서 asc, desc로 정렬한 이후에 가져오는 걸까요? 정답은 row_number()라는 SQL함수에 있습니다. (이전에 row_number() 대한 포스트가 있는데 참고하시기 바랍니다.)
SQL Server Profiler로 질의된 쿼리들을 살펴보도록 하겠습니다.

SELECT

[Project2].[P_ID] AS [P_ID],

[Project2].[P_Name] AS [P_Name],

[Project2].[C1] AS [C1],

[Project2].[C3] AS [C2],

[Project2].[C2] AS [C3],

[Project2].[C_ID] AS [C_ID],

[Project2].[C_Name] AS [C_Name],

[Project2].[P_ID1] AS [P_ID1]

FROM ( SELECT

       [Limit1].[P_ID] AS [P_ID],

       [Limit1].[P_Name] AS [P_Name],

       [Limit1].[C1] AS [C1],

       [Extent2].[C_ID] AS [C_ID],

       [Extent2].[C_Name] AS [C_Name],

       [Extent2].[P_ID] AS [P_ID1],

       CASE WHEN ([Extent2].[C_ID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2],

       CASE WHEN ([Extent2].[C_ID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C3]

       FROM   (SELECT TOP (10) [Project1].[P_ID] AS [P_ID], [Project1].[P_Name] AS [P_Name], [Project1].[C1] AS [C1]

             FROM ( SELECT [Project1].[P_ID] AS [P_ID], [Project1].[P_Name] AS [P_Name], [Project1].[C1] AS [C1], row_number() OVER (ORDER BY [Project1].[P_ID] ASC) AS [row_number]

                    FROM ( SELECT

                           [Extent1].[P_ID] AS [P_ID],

                           [Extent1].[P_Name] AS [P_Name],

                           1 AS [C1]

                           FROM [dbo].[Parent] AS [Extent1]

                    )  AS [Project1]

             )  AS [Project1]

             WHERE [Project1].[row_number] > 0

             ORDER BY [Project1].[P_ID] ASC ) AS [Limit1]

       LEFT OUTER JOIN [dbo].[Children] AS [Extent2] ON [Limit1].[P_ID] = [Extent2].[P_ID]

)  AS [Project2]

ORDER BY [Project2].[P_ID] ASC, [Project2].[C3] ASC

=> 빨간색으로 표시된 부분을 보면 row_number()함수를 사용하였고 select절에는 TOP(10)과 where절에 row_number > 0이 눈에 들어옵니다. Page시에 사용하는 Skip()과 Take()가 쿼리로 변경되는 부분이 바로 이 부분입니다. Sort시에는 row_number()함수안에서 사용하는 Order By절과 제일 마지막에 보이는 Order By절에 의해서 Data가 정렬됩니다.
예를 들어 일반적인 게시판을 생각해보시면 보여지는 글의 건수를 10건이라고 하고 첫 번째 페이지를 보여준다면 쿼리는 TOP(10), row_number > 0이라는 조건절이 만들어져 질의됩니다. 만약에 세 번째 페이지를 보여준다면 TOP(10), row_number > 20이 조건절로 되는 것입니다. 마찬가지로 Order By는 해당 열을 처음 클릭하면 asc 두 번째로 클릭하면 desc으로 조건절이 만들어져 질의되는 것입니다. 실질적으로 row_number()가 어떻게 만들어져 있는지는 알 수 없지만 최소한 화면에 보이는 Data를 빠르게 가져온다는 사실만은 분명합니다. 또한 P_ID가 아닌 P_Name으로 정렬을 해보면 속도가 P_ID보다 느린 것을 알 수 있는데 이것은 row_number()가 인덱스와 상관이 있다는 것을 간접적으로 알 수 있습니다.
Posted by resisa
,