관리 메뉴

IT 쟁이

DataGrid컨트롤 -3 -출처 태요.net 본문

ASP.NET/서버컨트롤

DataGrid컨트롤 -3 -출처 태요.net

클라인STR 2008. 1. 16. 18:16

Taeyo's ASP.NET

   강좌 최초 작성일 : 2004년 12월 22일
   강좌 최종 수정일 : 2005년 01월 10일

   강좌 읽음 수 : 22366 회

   작성자 : Taeyo(김 태영)
   편집자 : Taeyo(김 태영)

   강좌 제목 : DataGrid(3) : 자동 페이징

강좌 전 태오의 잡담>

아구... 강좌가 한달에 한, 두 번 정도밖에 올라오질 못하고 있네요. 죄송스럽게 생각합니다. 일주일에 하나이상은 올리도록 힘을 쭈아악 모아보겠습니다~


대상 : 기존 DataList 강좌를 진짜로 읽고, 공부하신 분~~
선수지식 : ADO.NET 기본 지식.

페이징 사용하기

사실, 그리드 컨트롤의 활용도는 제가 설명한 것 이상입니다. 즉, 여러분이 이전 강좌에서 다룬 내용들을 조합하여 더욱 기능적이고 더욱 효과적인 출력물을 생성해 낼 수도 있다는 것이죠. 공부에 왕도는 없습니다. 그리드 컨트롤을 자유자재로 다룰 수 있으려면 자주 이 컨트롤을 사용해 보아야 하는 것은 당연하겠죠?? 여러분이 외국 원서나 기고 글들을 읽어보면, DataGrid가 얼마나 빈번하게 사용되는 컨트롤인지를 알 수가 있을 거예요. 비록 뭔가 배우기에 따분하고, 지루한 면이 없지는 않지만 여러분이 이 컨트롤에 투자한 시간은 분명 그만한 보답을 해줄 것이라 믿고 있답니다.

이번에 하고자 하는 이야기는 DataGrid가 자체 보유하고 있는 멋진 기능인 페이징에 대한 이야기입니다. 페이징(Paging)란 출력된 표를 페이지를 나누어서 보여주는 기능인데요. 여러분이 수많은 게시판들에서 흔히 보게 되는 다음과 같은 기능을 의미하는 것이랍니다.

DataGrid를 사용하면 이러한 페이징을 아주 간단하게 구현할 수 있습니다. 이미 자체적으로 그러한 기능을 내장하고 있기 때문이죠. 페이징을 위해서는 DataGrid의 AllowPaging 속성 값을 true로 설정하고, 적절한 PageSize를 지정한 다음, 페이징 출력부를 어떻게 나타낼 것인지 <PagerStyle> 섹션을 구성하기만 하면 됩니다. 그것만으로 페이징의 출력 부분은 완성이 되는 것이죠.

그렇다면, 한번 해 보도록 할까요? 다음과 같이 HTML 모드에서 DataGrid의 AllowPaging, PageSize 속성을 추가 설정해 보도록 해요. 이 코드는 페이징 기능을 수행하도록 하며, 한 페이지에 출력되는 행을 5로 지정하는 코드입니다. PageSize 속성이 페이지 당 출력될 행의 수를 의미하는 것이니까요.

<asp:datagrid id="DataGrid1" runat="server" BackColor="White" BorderWidth="1px"
    BorderStyle="None" BorderColor="#CCCCCC" AutoGenerateColumns="False"
    CellPadding="3" DataKeyField="title_id" AllowPaging="True" PageSize="5">

그리고, <DataGrid> 구역안에 <PagerStyle>을 정의해 보도록 합시다요. <PagerStyle>은 페이징 출력부가 어떤 스타일로 나타날 것인지를 지정하는 구역인데요. [자동 서식]을 통해 DataGrid의 스타일을 이미 지정했었기에, 이미 여러분은 <PagerStyle> 구역의 코드를 가지고 있을 것입니다. 그 부분을 다음과 같이 수정해 보도록 해요(부가적으로 title_id를 출력하는 TemplateColumn의 ItemTemplate 구역도 변경해 주세요. 기존 코드는 출력 결과로 그 부분에 책의 이미지가 나타나도록 되어 있었는데, 이로 인해 페이지가 밑으로 길어져 스크롤이 생기기 때문에, 그다지 보기에 좋지 않을 겁니다. 물론, 반드시 바꾸어야 하는 부분은 아니예요. 바꾸지 않아도 동작하는 데에는 전혀 문제가 없으니까요). ^^ 

<asp:datagrid id="DataGrid1" runat="server" … >
    …
    <Columns>
        <asp:TemplateColumn>
        <HeaderStyle HorizontalAlign="Center" Width="70px"></HeaderStyle>
        <HeaderTemplate>ID</HeaderTemplate>
        <ItemStyle HorizontalAlign="Center"></ItemStyle>
            <ItemTemplate>
                <%# DataBinder.Eval(Container.DataItem, "title_id") %>
            </ItemTemplate>
        <EditItemTemplate>
            <font color="red"><%# DataBinder.Eval(Container.DataItem, "title_id") %>
                </font></EditItemTemplate>
        </asp:TemplateColumn>
        …
        …
        <asp:BoundColumn DataField="pubdate" HeaderText="출간일"
            DataFormatString="{0:yyyy-MM-dd hh:mm}"></asp:BoundColumn>
        <asp:ButtonColumn Text=" S " ButtonType="PushButton" HeaderText="선택"
            CommandName="Sel"></asp:ButtonColumn>
        <asp:EditCommandColumn ButtonType="PushButton" UpdateText="업데이트"
            CancelText="취소" EditText="편집"></asp:EditCommandColumn>
    </Columns>
    <PagerStyle HorizontalAlign="Right" ForeColor="#000066" BackColor="White"
        Mode="NumericPages"></PagerStyle>

</asp:datagrid>

그리고, 코드 비하인드 페이지에서 BindData() 함수의 코드를 다음과 같이 변경해 줍니다. 기존의 코드는 Pubs 데이터베이스의 Titles 테이블로부터 단지 5개의 데이터만을 가져와서 출력하고 있었거든요(T-SQL의 Top 로직을 사용해서 말입니다). 이는 단지 예제의 결과 출력물을 보기 편하도록 나타내기 위해서 그렇게 했었던 것인데, 페이징 기법을 테스트하기 위해서는 가급적 많은 레코드를 가져오는 것이 좋을 것이므로, 질의 문을 전체 레코드를 가져오는 것으로 변경해 보자는 것입니다.

private void BindData()
{
    string connectStr = "Server=(local); database=Pubs; user id=xxx";

    SqlConnection Con = new SqlConnection(connectStr);
    string strSql = "Select title_id, title, price, pubdate from titles";
    SqlDataAdapter Adap = new SqlDataAdapter(strSql, Con);

    DataSet ds = new DataSet();
    Adap.Fill(ds, "titles");

    DataGrid1.DataSource = ds.Tables["titles"];
    DataGrid1.DataBind();
}

뭔가 대단히 많은 작업을 한 것처럼 느껴질런지도 모르겠지만 사실 페이징 기능을 위해서 실제로 우리가 한 작업의 전부는 AllowPaging 속성과 PageSize 속성을 지정하고, <PAGERSTYLE>를 적절히 설정한 것이 전부입니다. 어쨌든 좋습니다. 그렇다면, 이제 어디 한번 실행해 보도록 하죠. 어떤 멋진 결과가 나올지를 기대하면서 말입니다.

호오, 나름대로 깔끔하게 페이징 부분이 출력되었습니다. 우리가 지정한 PageSize 만큼의 데이터가 출력되는 것도 볼 수 있구요(우리는 그 값을 5로 지정했었죠?). 그렇다면, 이제 두근거리는 맘을 진정시키며 2번 페이지로 이동해 봅시다. 멋들어지게 두 번째 페이지로 이동할 것이라는 기대는 순식간에 무너지고 말 것입니다. ㅠㅠ 그렇습니다. 아무리 페이지를 이동하려 해 봐도 포스트백만이 일어날 뿐, 우리가 원하는 결과는 얻을 수가 없어요~ 그도 그럴 것이, 실제적으로 페이지를 이동하게 하는 코드는 아직 작성되지 않았기 때문입니다.

각각의 페이지 번호가 클릭될 경우 서버에서는 DataGrid의 PageIndexChanged 이벤트가 발생합니다. 해서, 여러분이 실제로 페이지 번호를 클릭했을 때 이동을 하고 싶다면, 그 이벤트 함수에 적절한 처리를 해 주어야 하는 것입니다. 그렇다면, 이제 그러한 처리 부분을 추가해 보도록 합시당. 웹 폼 디자이너에서 DataGrid를 선택하고, [속성 창]에서 번개 모양의 버튼을 클릭한 뒤, 출력되는 이벤트들 중에서 PageIndexChanged를 찾아 그 우측 구역에서 더블 클릭을 합니다. 그러면, 자동으로 코드 비하인드 페이지로 이동할 것이고, DataGrid1_PageIndexChanged 이벤트 처리기가 생성되어져 있을 것입니다. 그 구역을 다음과 같이 작성해 보도록 해요.

private void DataGrid1_PageIndexChanged(object source,
    System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
    DataGrid1.CurrentPageIndex = e.NewPageIndex;
    BindData();
}

PageIndexChanged 이벤트 처리기의 두 번째 인자인 DataGridPageChangedEventArgs 개체의 NewPageIndex 속성 값은 현재 사용자가 선택한 페이지의 인덱스를 알려줍니다. 그러므로, 그 값을 현재 그리드 컨트롤의 CurrentPageIndex 속성으로 지정해 주기만 하면, 이제 그리드는 자신이 바인드할 데이터들 중에서 몇 번째 위치(페이지)에서부터 몇 개의 행을 출력해야할 지를 알 수 있게 되는 것이죠. 그리고, 다시금 데이터를 그리드 컨트롤에 바인드하면 이제 DataGrid는 사용자가 선택한 페이지를 화면에 렌더링하는 것이랍니다.

그렇다면, 이제 페이지를 컴파일하고 실행해 보도록 하세요. 완전하게 페이징이 동작하는 것을 확인할 수 있을 것입니다.

페이징 기능을 위해서 우리가 한 일은 대단히 단순했습니다. DataGrid의 AllowPaging 속성과 PageSize 속성을 설정하고, <PagerStyle> 부분을 적절히 지정한 다음, PageIndexChanged 이벤트 처리기를 위와 같이 작성해 주면 그것이 전부인 것입니다. 단지, 이러한 간단한 작업만으로 기존 ASP 에서는 대단히 복잡하게 처리해야만 했던 페이징 기능이 자동으로 만들어진다는 것이죠. 이 얼마나 멋집니까!

어쩌면, 여러분은 페이징 출력부가 1,2,3,4 .. 이런 식으로 출력되는 것이 불만스러울 수도 있을 것입니다. 단순하게 [이전], [다음] 과 같은 식으로 출력되기를 바랄 수도 있겠죠. 역시나 영리한 DataGrid!

이 컨트롤은 그러한 것까지 이미 준비해 두고 있습니다. 만일, 위의 페이징이 그런 식으로 출력되기를 바란다면, 단지 <PagerStyle> 구역만을 약간 편집하면 됩니다. 다음과 같이 말이죠.

<PagerStyle HorizontalAlign="Right" ForeColor="#000066" BackColor="White"
    Mode="NextPrev" NextPageText="[다음]" PrevPageText="[이전]"></PagerStyle>

단지, Mode를 NextPrev로 변경하고, '이전'과 '다음' 링크에 출력될 텍스트를 각각 PrevPageText와 NextPageText에 지정하기만 하면 됩니다. 위와 같이 변경한 다음 페이지를 다시금 실행해 보도록 하세요.

정말로 똑똑합니다. 그런데, 이게 전부가 아닙니다. 만일 여러분이 '이전', '다음'의 출력 텍스트를 이미지 태그를 사용하여 작성한다면 실제로 그러한 부분을 이미지로 출력해 주기도 한다는 것이죠. 예를 들어, 다음과 같이 작성한다면 말입니다.

<PagerStyle HorizontalAlign="Right" BackColor="White" Mode="NextPrev"     NextPageText="<img src=next.gif>" PrevPageText="<img src=prev.gif>"> </PagerStyle>

참고로, 다음 표는 <PagerStyle>에서 페이징을 위해 사용할 수 있는 대표적인 속성들입니다

속성 설명
Mode 페이징 출력부를 [이전], [다음] 링크버튼으로 출력할 지, 숫자 링크버튼으로 출력할 지를 지정한다. 기본 값은 NextPrev 이다.
사용할 수 있는 값 : NextPrev |NumericPages
NextPageText 다음 페이지용 링크 버튼에 나타낼 텍스트
PrevPageText 이전 페이지용 링크 버튼에 나타낼 텍스트
PageButtonCount 페이징을 숫자 링크버튼으로 출력할 경우 나타낼 링크의 개수

자. DataGrid에서의 페이징이 얼마나 쉽고 간단하지를 알아보았습니다. 속도도 빨라 보이고, 동작도 아주 확실하지요. 하지만, 언제나 그렇듯이 쉽고 간단한 방법 뒤에는 뭔가 어두운 일면도 존재하고 있는 법입니다. 누군가가 편해지면, 다른 누군가는 힘들어지듯이 말이죠.

이러한 자동 페이징의 결정적인 단점은 대용량의 데이터를 페이징할 경우, 극단적인 성능의 저하가 찾아온다는 사실입니다. 위에서 설명한 자동 페이징 기능은 사실상 단지 PageSize에 지정된 개수(예제의 경우 5개)만큼의 행을 출력하기 위해서 전체 데이터를 모두 바인딩해야 한다는 문제점을 안고 있습니다. 즉, 우리의 데이터 원본이 10만건의 레코드를 가지고 있을 경우에도 하나의 페이지를 출력하기 위해서 10만건의 데이터를 모두 바인드해야 한다는 이야기이지요. 우리가 출력하고자 하는 페이지가 3번째 페이지라고 가정해 보아요. 단지 우리는 3번째 페이지에 해당하는 5개의 레코드만을 화면에 출력하려 한다고 말입니다. 하지만, 위의 페이징 기법을 사용할 경우에는, 그렇게 하기 위해서도 서버에서는 10만건의 레코드를 DataGrid에 바인딩해야만 한답니다.

왜 그렇게 되는지 현재의 페이징 기능의 내부를 한번 들여다 보도록 하겠습니다. 자동 페이징 기능을 사용하게 되면, DataGrid는 속성으로 지정되어 있는 PageSize를 가지고 일단 논리적으로 페이지를 나눕니다. 그리고, 사용자가 특정 페이지를 클릭할 경우 사용자가 클릭한 페이지 인덱스에 해당하는 위치로 이동하여 그 위치에서부터 PageSize 만큼의 행을 출력하여 보여주는 것이지요.

이해가 어렵다면 다음 그림을 참고해 보세요.

페이지의 인덱스는 내부적으로는 0부터 시작한다.

그림에서 보여지는 데이터들은 실제 titles 테이블의 레코드들이며, 기본적으로 총 18개의 레코드를 가지고 있습니다. 여러분이 이러한 데이터를 가져와서 DataGrid에 바인드 한 다음, 출력된 페이징 출력부에서 2 페이지를 선택했다고 가정해 보아요. 그렇다면, 그 때 PageIndexChanged 이벤트가 발생할 것입니다.

그 경우, DataGrid는 PageSize에 지정된 값을 가지고 위와 같은 논리적인 분할을 계획합니다. 물론, 이 시점에서는 바인드될 행의 정확한 개수를 알 수 없겠지만(아직 데이터는 바인드 되지 않았으므로), PageSize 값을 통해서 이후 각각의 페이지를 논리적으로 분할할 준비는 된 것입니다.

그 다음, 사용자가 클릭한 새로운 페이지의 값 즉, 가정대로라면 2 페이지에 해당하는 페이지 인덱스 값을 가져와서 그 위치서부터 데이터를 PageSize 만큼 출력하는 것입니다. 만일, 사용자가 3 페이지를 클릭한다면, 3 페이지에 해당하는 페이지 인덱스(pageIndex는 2가 될 것입니다)에서부터 5개의 행을 또한 출력하고 말이죠.

DataGrid는 내부적으로 페이지 인덱스를 사용하며, 그 페이지 인덱스는 0부터 순번을 시작하기에 사용자가 2 페이지를 클릭했을 경우에 넘어오는 페이지 인덱스는 1이 됩니다

이렇게 동작하기 때문에, 10만 건의 레코드 중에서 3 페이지에 해당하는 결과물을 보여주려 할 경우, 1) 10만 건의 데이터를 ASP.NET의 메모리로 가져와서, 2) 그 10 만건을 PageSize로 논리적으로 나누어서 각각의 페이지 인덱스를 메기고, 3) 그 중 사용자가 클릭한 페이지의 인덱스로 이동하여 고작 5개의 행을 출력하는 것입니다.

위의 설명에서 번호를 메긴 부분이 서버에 부하를 주는 부분들이라는 것은 쉽게 눈치챌 수 있었을 것입니다. 특히, 그 중 1)번은 정말로 심각합니다. 10만건의 데이터를 메모리로 가져온다는 것부터가 서버에 엄청난 부하를 주게되기 때문이지요. 서버의 성능이 너무너무 좋아서 그 정도는 문제없다고 하더라도 문제는 거기서 끝나지 않습니다. 여러분의 사이트가 수 많은 회원들을 보유하고 있는 웹 사이트라고 가정해 보아요. 그렇다면, 수시로 수 만명의 사람들이 방문할텐데, 만일 그들이 이렇게 제작된 웹 페이지를 여러 번 요청한다면 어떻게 되겠습니까? 수 만명 x 수 십만건의 분량의 데이터가 여러분의 서버 메모리를 채우게 될 것이고, 아무리 뛰어난 서버라 하더라도 견디지 못하고, 지쳐 쓰러져 울고야 말 것입니다. 엉엉~~~ 

이야기를 하다보니 오해가 있을 수도 있어서 사족을 붙이면, 저는 지금 위에서 설명한 자동 페이징 기능이 형편없다고 이야기하는 것은 결코 아닙니다. 만일, 데이터의 양이 얼마되지 않는 경우(기백건 정도의 데이터)에는 그러한 자동 페이징 기능이 그리 나쁘지 않을 수 있습니다. 지금 제가 이야기하는 것은 대용량의 데이터를 다루어야 할 경우입니다. 그러한 경우에는 자동 페이징 기능이 그리 효과적이지 못하다는 이야기를 하고자 하는 것이지요.

그렇다면, 대안은 있는 것일까요? 만일 없다면 이렇게 침을 튀겨가며 이야기하지도 않았을 것입니다. ^^;; 하지만, 이하의 내용은 그리 녹녹하지 않을 듯 합니다. 특히, 초급자들에게는 상당히 어려운 이야기일 수도 있을 듯 합니다. 그러므로, "현재까지의 내용도 그리 쉽지 많은 않았어. 그러나, 나는 여기까지 왔고, 앞으로도 나는 전진뿐이다"라고 생각하는 분들은 계속해서 진도를 나가도 좋지만, "아아~ 머리에 한계를 느낀다. 더 이상 책을 보다가는 눈에서 레이저가 나올지도 몰라" 하시는 분들은 차후 필요성을 느낄 경우 다시 이 곳을 찾아주셔도 좋겠습니다.

그럼 알아보도록 하죠. 사용자 정의 페이징 기법에 대해서 말입니다.

엇? 그런데.. 지면이... 엇??? 페이지가...  뭐라구요? 다음 강좌에서 이어간다구요? 이런...

Comments