관리 메뉴

IT 쟁이

ASP.NET 1.x 방명록만들기 (1) 본문

ASP.NET Ex

ASP.NET 1.x 방명록만들기 (1)

클라인STR 2008. 2. 1. 22:57

ASP.NET을 배우면서 회원가입 다음으로 만들었던것이 방명록 이었습니다. 상당히 다양하게 여러가지(?) 방법으로 만들 수 있는 방명록 ASP.NET 바이블을 참조하여 나름대로 만들어 보았습니다.


데이터베이스를 생성합니다.

사용자 삽입 이미지


솔루션탐색기를 클릭하여 web.config 파일을 추가하고 <appSettings> 영역의 내용을 입력합니다.
사용자 삽입 이미지

web.config 파일에 다음내용을 추가하였습니다.
사용자 삽입 이미지

<connectionStrings> <appSettings> 영역을 <system.web> 영역 내부에 두지 않도록 합니다.


1. 방명록의 리스트 페이지 작성하기
방명록의 uI는 다양한 방법에 다양한 컨트롤로 작성할수 있습니다. DataList를 사용하여 UI를 작성해보도록 하겠습니다.

그전의 스타일 시트를 추가하여 스타일시트를 작성합니다. (안해도상관은 없음)
body ,span, input, textarea, select ,p
{
 font-family:Verdana;
 font-size:11px;
 
}
td
{
 font-family:Verdana;
 font-size:11px;
 padding : 5px;
 
}

A { text-decoration:none }


다음과 같이 UI를 디자인 하였습니다.

사용자 삽입 이미지


데이터리스트 컨트롤을 드래그하여 생성하고 템플릿 편집기로 다음과 같이 작업해줍니다.

사용자 삽입 이미지

사용자 삽입 이미지

 
List.aspx

<asp:Label ID="lblTitle" runat="server" Width="58px"></asp:Label>
        <asp:DataList ID="GuestList1" runat="server" OnDataBinding="GuestList1_DataBinding" OnItemDataBound="GuestList1_ItemDataBound">
     
        <ItemTemplate>
            <table  id="table1"  cellspacing="0" border="1" rules="none"  style="width: 593px; border-color:Black" >
                <tr>
                    <td>
                        <asp:Label ID="lblId" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Id") %>'></asp:Label>
                    </td>
                    <td>
                        <asp:HyperLink ID="lnkWrite" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Write") %>'></asp:HyperLink>님이
                        <asp:Label ID="lblDate" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Regin_Date") %>'></asp:Label>에 남기신 글입니다.
                    </td>
                    <td>
                        <asp:HyperLink ID="lnkEdit" runat="server">수정</asp:HyperLink>
                        <asp:HyperLink ID="lnkDelete" runat="server">삭제</asp:HyperLink>
                    </td>
                </tr>
                <tr>
                    <td colspan="3">
                        <asp:Label ID="lblContent" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Content") %>'></asp:Label>
                    </td>
                </tr>
              
            </table>
       
        </ItemTemplate>
        <FooterTemplate>
            <table id="table2" cellspacing="0" border="0" rules="none" style="width: 593px">
                <tr>
                    <td style="height: 23px">
                        <asp:Label ID="lblPager" runat="server" Text=""></asp:Label>
                    </td>
                    <td style="width: 355px; height: 23px;">
                    </td>
                    <td style="height: 23px">
                        <asp:HyperLink ID="lnkWrite" runat="server" ImageUrl="~/GuestBook1/image/write.GIF"></asp:HyperLink>
                    </td>
                    <td style="height: 23px">
                        <asp:HyperLink ID="lnkPrev" runat="server" ImageUrl="~/GuestBook1/image/back.GIF"></asp:HyperLink>
                    </td>
                    <td style="height: 23px">
                        <asp:HyperLink ID="lnkNext" runat="server" ImageUrl="~/GuestBook1/image/next.GIF"></asp:HyperLink>
                    </td>
                </tr>
              
            </table>
       
           
        </FooterTemplate>
        </asp:DataList>
        <asp:Label ID="lblError" runat="server" Width="49px"></asp:Label>
       
        </div>

데이터베이스에서 값을 바인딩시키기 위해서 ASP.NET 2.0은 좀더 편한 기능을 제공하지만, ASP.NET 1.X 방식으로 처리하였으며, 향후 2.0형식으로 바꿔보겠다.

<#% DataBinder.Eval(Container, "DataItem.Id") %>
DataItem.Id에서 Id는 테이블에 열이름과 일치한다.

'<%# DisplayContent(DataBinder.Eval(Container, "DataItem.Content").ToString()) %>'>

게시물 내용을  올바르게 출력하기 위해 다음과 같은 데이터바인딩 구문을 이용합니다. 이때 DisPlayContent() 메서드는 코드 비하인드에서 public으로 정의되었으며 행바꿈문자를 <br> 태그로 변환해서 리턴해줍니다.

    public int CurrentPage; //현재 페이지 번호
    public string DisplayContent(string Content)
    {
        //본문내용중 행바꿈문자를 <br> 태그로 변환
        Content = Content.Replace("\r\n", "<br>");
        return Content;
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request.QueryString["page"] == null)
            CurrentPage = 1;
        else
            CurrentPage = int.Parse(Request.QueryString["page"].ToString());

        ShowList();
    }

Page_Load 이벤트 에서는 현재 페이지 번호를 Request.QueryString() 컬렉션에서 참조해서 값을 지정합니다.
만약 쿼리스트링을 값이 없으면 처음 페이지를 보여줘야 함으로 CurrentPage 값을 1로 초기화 한다.

ShowList() 방명록을 게시물의 보여주는 함수이다.

방명록에서의 핵심은 페이징기능인거같다. -_- 개인적인 주관이지만, 생각보다 이해가 어려웠기때문이다. 순전히 산수가 안되는 개인적인생각입니다.ㅎㅎ

먼저 폐이징을 하기위해서 GuestBook 테이블에서 게시물의 전체 레코드 수를 알아야 합니다.
private int GetRecoredCount() //현재 게시물의 전체 레코드 수를 구한다.
    {
        int Record = 0;
        SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["Guest"].ConnectionString);
        SqlCommand cmd = new SqlCommand("SELECT COUNT(*) FROM GuestBook", con);
        try
        {
            con.Open();
            Record = (int)(cmd.ExecuteScalar());
        }
        catch (Exception ex)
        {
            this.lblError.Text = ex.Message;
        }
        finally
        {
            con.Close();
        }

        return Record;
    }

GetRecordCount() 데이터베이스에 접근해 게시물의 전체레코드수를 리턴합니다.

ShowList() 함수는 게시물의 목록을 출력하는 함수입니다.

private void ShowList() //방명록의 게시물을 보여준다.
    {
        int TotalRecordCount = GetRecoredCount(); //테이블에 총레코드수
        int PageSize = int.Parse(ConfigurationSettings.AppSettings["PageSize"]);
        //한 페이지 보여지는 게시물수
       
       int TotalPageCount = (int)((TotalRecordCount - 1) / PageSize) + 1; //총 페이지수

        lblTitle.Text = " Page [" + CurrentPage.ToString() + "/" + TotalPageCount.ToString() + "] Total: " + TotalRecordCount.ToString();
        
        //가져올 게시물의수
        int TopCount = this.CurrentPage * PageSize;

        string query = "SELECT TOP " + TopCount.ToString() + " * FROM GuestBook ORDER BY Id DESC";
        SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["Guest"].ConnectionString);
        SqlCommand cmd = new SqlCommand(query, con);
       
        try
        {
            con.Open();
            SqlDataReader reader =cmd.ExecuteReader();
            for(int i=0; i<(CurrentPage -1)*PageSize; i++)
            {
                reader.Read();
            }
            this.GuestList1.DataSource = reader;
            this.GuestList1.DataBind();
            reader.Close();
        }
        catch(Exception ex)
        {
            lblError.Text = ex.Message;
        }
        finally
        {
            con.Close();
        }
        ShowPager(TotalRecordCount, CurrentPage, TotalPageCount, PageSize);

    }
코드를 살펴 보면

       int TotalRecordCount = GetRecoredCount(); //테이블에 총레코드수
        int PageSize = int.Parse(ConfigurationSettings.AppSettings["PageSize"]);
        //한 페이지 보여지는 게시물수
        int TotalPageCount = (int)((TotalRecordCount - 1) / PageSize) + 1; //총 페이지수

        lblTitle.Text = " Page [" + CurrentPage.ToString() + "/" + TotalPageCount.ToString() + "] Total: " + TotalRecordCount.ToString();
        
        //가져올 게시물의수
        int TopCount = this.CurrentPage * PageSize;



TotalRecordCount 는 테이블에 총 레코드 수 , PageSize는 한 페이지에 보여줄 게시물 수
TotalPageCount는 게시판에서의 총 페이지수 입니다.
((TotalRecordCount - 1) / PageSize) + 1; 이런공식을 사용합니다.
DB 레코드의 수가 6이라면 (6-1/5)+1 = 2  즉  게시판에 나타낼수 있는 페이지수는 2페이지 가됩니다.
레코드의 수가 0일경우 (0-1/5)+1= 1이됩니다.

가져올 게시물의수는 페이지당 읽어와야되는게시물의 수를 처리하기 위해서 다음식을 이용하였다.
        int TopCount = this.CurrentPage * PageSize;

현재 게시물의 페이지가 2폐이지라고하면 TopCount는 2*5 = 10 이 된다.

      string query = "SELECT TOP " + TopCount.ToString() + " * FROM GuestBook ORDER BY Id DESC";
          .... 중략 ...    
            SqlDataReader reader =cmd.ExecuteReader();
            for(int i=0; i<(CurrentPage -1)*PageSize; i++)
            {
                reader.Read();
            }
            this.GuestList1.DataSource = reader;
            this.GuestList1.DataBind();
            reader.Close();
        }
query 문은 Top N 구문을 사용해서 게시물의 수를 하향정렬순으로 레코드를 정렬해서 가지고 옵니다.

사용자 삽입 이미지

이때 현재 페이지가 1페이지 CurrentPage가 1이면, TopCount는 1*5= 5가됩니다.
즉 Select구문은 SELECT TOP 5 * FROM GuestBook ORDER BY Id DESC 의 쿼리문이 실행됩니다.
CurrentPage 값이 1이므로 For루프는 실행되지않는다.
ID 6~2인 5개의 게시물을 레코드를 우선적으로 가져오게됩니다.


사용자 삽입 이미지

첫페이지가 로드되었을때 모습.

2번째 페이지를 선택하면 CurrentPage 값은 2가 되고, TopCount는 2*5= 10이 된다.  
즉 Select구문은 SELECT TOP 10 * FROM GuestBook ORDER BY Id DESC 의 쿼리문이 실행됩니다.
CurrentPage 값이 2이므로 For루프는 5번 수행됩니다.
ID 6~2인 5개의 게시물을 레코드를 우선적으로 가져오게 됩니다.
read.read() 구문이 5번실행 됩니다. 데이터리더의 Read() 메서드는 데이터베이스의 테이블에서 하나의 레코드를 가져오게 됩니다.
this.GuestList1.DataSource = reader; 에서의 테이블에 레코드는 ID1인 레코드가 반환됩니다.

사용자 삽입 이미지


다음과 같이 2페이지에 데이터의 내용은 ID1인 레코드가 화면에 출력됩니다.

사용자 삽입 이미지

ShowPager(TotalRecordCount, CurrentPage, TotalPageCount, PageSize);

ShoWPager() 메서드는 페이징을 구현하는 메서드 입니다. 인자로 필요하는 매개변수에는 TotalRecordCount - 전체 레코드수, CurrentPage - 현재 페이지번호, TotalPageCount 현재 전체 페이지수, PageSize -한 페이지에 보여줄 게시물의 수 를 필요로 합니다.

이동할수 있는 페이지 번호를 10개 단위로 묶어서 하이퍼링크를 제공합니다. prev는 전 10개 next는 다음10개 폐이지 네비게이션으로 이동할수 있는 하이퍼링크를 제공합니다.
일반적으로 10개의 단위를 묶어서 하이퍼링크를 제공하지만, 2개의 단위로 묶어서 우선 테스트 해보도록한다.

     private void ShowPager(int RecordCount, int CurrentPage, int PageCount, int PageSize)
    {
        string Path = Request.ServerVariables["PATH_INFO"] + "?page=";
       
        //FromPage - 페이징의 시작번호
        //ToPage - 페이징의 끝번호
        int FromPage, ToPage;
        FromPage = (int)((CurrentPage - 1) / 2) * 2 + 1;
        if (PageCount > FromPage + 1)
        {
            ToPage = FromPage + 1;
        }
        else
        {
            ToPage = PageCount;
        }

        string Pager = "<font size=2>";

        //이전 10개표시
        if ((int)((CurrentPage - 1) / 2) > 0)
            Pager = Pager + "<a href='" + Path + (FromPage - 1).ToString() + "'>{prev}</a> ";
        //폐이징 표시
        for (int i = FromPage; i <= ToPage; i++)
        {
            if (i == CurrentPage)
                Pager += "<b>[" + i.ToString() + "]</b>";
            else
                Pager = Pager + "<a href='" + Path + i.ToString() + "'>" +
                    "[" + i.ToString() + "]</a> ";
        }
        //다음 10개 표시
        if (ToPage < PageCount)
        {
            Pager = Pager + " <a href='" + Path + (ToPage + 1).ToString() + "'>{next}</a>";
        }  
        Pager = Pager + "</font>";
      
        //페이지 네비게이션 출력하기
      

        this.lPage.Text = Pager;

        if (CurrentPage > 1)
            Prev.NavigateUrl = Path + (CurrentPage - 1).ToString();
        if (CurrentPage < ToPage)
            Next.NavigateUrl = Path + (CurrentPage + 1).ToString();

        Write.NavigateUrl = "Write.aspx?page=" + CurrentPage.ToString();
    }


string Path = Request.ServerVariables["PATH_INFO"] + "?page=";

Request객체의 ServerVariables컬렉션에서 현재 웹페이지의 Path정보를 참조할 수 있다.
http://msdn2.microsoft.com/en-us/library/ms524602 자세한 설명은 링크를 통해서 살펴보면좋을거 같다. 종류가 엄청많다.-_-

FromPage - 페이징의 시작번호 이고 CurPage - 페이징의 마지막 번호이다.
FromPage와 ToPage 번호를 구한 뒤 루프를 돌면서 네비게이션 링크를 구성하고 구성된 문자열을 Label 컨트롤의 Text 프로퍼티에 설정한다.

       FromPage = (int)((CurrentPage - 1) / 2) * 2 + 1;
        if (PageCount > FromPage + 1)
        {
            ToPage = FromPage + 1;
        }
        else
        {
            ToPage = PageCount;
        }
   
FromPage는 시작번호이다. 현재 페이지가 2폐이지일 경우  ((2-1)/2) * 2 +1 = 시작번호는 1이 됩니다. 현재 페이지가 1페이지일 경우에다 ((1-1)/2)*2 +1 =1폐이지가 됩니다. 앞에 PageCount 식과 다른점은 나누고자 하는 수에서 2를 곱했다는점이다.
다음 조건문을 실행한다. PageCount인 전체폐이지수와 비교하여 PageCount가 폐이지의 시작번호+1 보다 클경우는 마지막폐이지번호는 시작폐이지의 번호 +1을 합니다.  여기서 +1을 하는 이유는  폐이지번호가 2개일때 한묶음으로 묶어서 출력할 경우 +1을 더하고 폐이징을 개수가 10개의 단위로 묶을경우 나누는수가 10일경우 다음식에 더해지는 값은 9가됩니다.

예를 들어서  현재 PageCount 총폐이지수가 3일경우 CurrentPage 2일때 FromPage 값은 1이되고
PageCount>FromPage+1 경우이므로 ToPage값은 FromPage+1인  마지막 폐이지 번호의 값은 2가  됩니다.
PageCount 값 즉 총페이지수가 2일경우에는 else구문이 실행되며 ToPage=PageCount 가 되므로 마지막 폐이지의 번호 역시 2이가됩니다. 총폐이지수가 2이므로 {next} 링크가 추가되지않습니다. PageCount 3일 경우 마지막 폐이지 번호의 값이 2가되므로 {next}링크를 추가 시켜야 합니다.

   //이전 10개표시
        if ((int)((CurrentPage - 1) / 2) > 0)
            Pager = Pager + "<a href='" + Path + (FromPage - 1).ToString() + "'>{prev}</a> ";


이전 2개를 표시 하기위해서는  CurrentPage 값이3일경우    ((CurrentPage-1)/2) 의 값이 0보다 크다 앞의 1,2 폐이지가 존재하므로  {prev}를 출력한다.
스트링문자열로 <a> 태그를 구성한다.

폐이지 네비게션을 표시합니다.
시작 폐이지번호부터 마지막 폐이지 번호까지 루프를 돌면서 폐이지 카운터 스트링을 작성합니다. 이때 현제 폐이지번호의 숫자는 <a>태그를 구성하지 않는 처리를 해줍니다.
 for (int i = FromPage; i <= ToPage; i++)
        {
            if (i == CurrentPage)
                Pager += "<b>[" + i.ToString() + "]</b>";
            else
                Pager = Pager + "<a href='" + Path + i.ToString() + "'>" +
                    "[" + i.ToString() + "]</a> ";
        }




//다음 10개를 표시하기위해서
     if (ToPage < PageCount)
        {
            Pager = Pager + " <a href='" + Path + (ToPage + 1).ToString() + "'>{next}</a>";
        }  
        Pager = Pager + "</font>";

마지막폐이지의 번호보다 전체페이지의수가 많다면 폐이징 스트링에 {next}링크를 추가합니다.
이때 <a>태그 안에 href다음에는 '싱글쿼토로 링크의 주소 끝단을 감싸줍니다.

그리고는 폐이징을 출력할 라벨에 생성한 폐이징네비게이션 스트링을 대입합니다.
이전보기, 다음보기, 글쓰기 버튼을 링크버튼 속성중 NavigateUrl을 설정해줍니다.       

        this.lPage.Text = Pager;

        if (CurrentPage > 1)
            Prev.NavigateUrl = Path + (CurrentPage - 1).ToString();
        if (CurrentPage < ToPage)
            Next.NavigateUrl = Path + (CurrentPage + 1).ToString();

        Write.NavigateUrl = "Write.aspx?page=" + CurrentPage.ToString();

여기서 중요한 내용을 언급하면 현재 폐이징네비게이션이 출력되는 곳은 DataList 컨트롤에 <FooterTemple> 부분입니다. 데이터리스트의 템플릿영역은 코드 비하인드에서 직접적인 접근이 불가능합니다.
그러므로 이러한 경우는 DataBindg 전이나 후의 이벤트에서 처리를 해주어야 합니다.
후자의 경우를 이용하여 처리 하기로 하였으며 ItemDataBound 이벤트를 생성하고  다음과 같은 변수를 선언합니다.
    private Label lPage;
    private HyperLink Write;
    private HyperLink Prev;
    private HyperLink Next;
다음과 같이 FindContrl()메서드를 사용하여 처리를 해준다음 코딩을 해주면 해결됩니다.
 

사용자 삽입 이미지

DisplayContent() 메서드는 게시글의 내용중 개행문자를 HTML 태그로 보기좋게 바꿔줍니다.
   public string DisplayContent(string Content)
    {
        //본문내용중 행바꿈문자를 <br> 태그로 변환
        Content = Content.Replace("\r\n", "<br>");
        return Content;
    }

사용자 삽입 이미지
Comments