반응형

 
이 포스트는 월간 마이크로소프트웨어에 기고한 원고를 재편집한 포스트입니다. 그러므로, 본의 아니게 반말로 진행되고 있습니다. 원래 싸가지가 없어서 그런 것이 아니니 무한 용서를... ^^;;
또한, .NET Framework 3.5 SP1이 적용되기 이전의 소스이므로, 현재의 개발 환경과 다를 수 있습니다. 마소에 제출한 블로그 소스는 블로그 소스 다운로드에서 다운로드하실 수있습니다.

덧글 수정 및 삭제 페이지 구현

포스트 조회 화면에서 보이는 덧글 목록의 “[Modify]", "[Delete]" 버튼을 클릭하게 되면, 방문자는 입력한 덧글에 대한 수정 및 삭제를 할 수 있게 된다. 덧글의 수정 및 삭제를 위한 ”CommentMenu.aspx" 페이지를 웹 사이트에 새롭게 추가한 후, MultiView 컨트롤과 View 컨트롤을 이용하여 화면을 구성한다. 덧글 목록의 “[Delete]" 버튼을 클릭하게 되면, 덧글을 작성할 때 입력한 비밀번호를 입력하는 화면이 보이게 된다. 비밀번호를 입력 후 ”확인“ 버튼을 클릭할 때, 정확한 비밀번호를 입력하였으면 포스트 조회 화면이 새로고침되게 되며, 틀린 비밀번호를 입력하였으면, 비밀번호가 틀렸다는 메시지 창이 뜨게 된다. ”[Modify]" 버튼을 클릭시에도 "[Delete]" 버튼과 동일한 화면이 보여지게 되지만, 정확한 비밀번호를 입력하게 되면 덧글을 수정할 수 있는 화면이 나타나게 된다. <화면 6>은 ”[Modify]" 버튼을 클릭시 표시되는 화면과, 정확한 비밀번호를 입력하였을 때 표시되는 수정 화면을 보여주고 있다.


<화면 6> 덧글 수정시에 표시되는 화면

블로그 우측 메뉴의 구성

블로그 우측 메뉴에서 최근 포스트 목록 및 최근 덧글 목록 정보를 표시하기 위해, 몇 개의 포스트 및 덧글을 작성하도록 한다. 몇 개의 데이터를 입력한 뒤에 블로그 우측 메뉴에서 보여지는 최근 포스트 목록과 최근 덧글 목록 정보를 조회하여 출력하는 코드를 블로그의 마스터 페이지인 “BlogMasterPage.master" 및 ”BlogMasterPage.master.cs"에 추가하도록 한다. 최근 포스트 목록 정보와 최근 덧글 목록 정보를 조회하는 코드는 동일하며, 단지 테이블명만 다를 뿐이기에 최근 포스트 목록 정보를 가져오는 코드에 대해서만 설명하도록 하겠다. 최근 포스트 목록 정보를 가져오는 GetRecentlyPostInfo() 메소드를 Page_Load 이벤트에 정의한 후, <리스트 6>과 같이 코드를 작성한다.

    // 최근 포스트 목록 정보 가져오기
    private void GetRecentlyPostInfo()
    {
        BlogDataClassesDataContext blogDataContext 
            = new BlogDataClassesDataContext();
 
        List<BLOG_POSTS> blogPostList =
                            (from c in blogDataContext.BLOG_POSTS
                                where c.deletedate == null
                                orderby c.createdate descending
                                select c).Skip(0).Take(5).ToList();
 
        LvwLicentlyPost.DataSource = blogPostList;
        LvwLicentlyPost.DataBind();
 
        blogDataContext = null;
    }
<리스트 6> 최근 포스트 목록 정보를 가져오는 코드

최근 포스트 목록 정보와 최근 덧글 목록 정보는 화면에 5개씩만을 보여줄 것이므로, 등록일 순으로 정렬하여 최근 5개의 목록만을 가져온다. 최근 포스트 목록 정보를 조회하여, “BlogMasterPage.master"에 이미 정의된 ListView 컨트롤에 데이터를 표시한다. 최근 포스트 목록 정보가 표시될 때 포스트의 제목을 클릭 시 이동하기 위해서(또는, 최근 덧글 목록 정보가 표시될 때 덧글의 내용을 클릭 시 이동하기 위해서), ListView 컨트롤에 데이터가 바인딩되면서 발생하는 이벤트인 ”OnItemDataBound" 이벤트를 이용하여 ListView 컨트롤에 포함되어 있는 HyperLink 컨트롤의 NavigateUrl 속성에 할당된 값을 재정의하게 된다. ListView 컨트롤의 ”OnItemDataBound" 이벤트를 이용한 코드는 <리스트 7>과 같다.

    protected void LvwLicentlyPost_ItemDataBound
        (object sender, ListViewItemEventArgs e)
    {
        if (e.Item.ItemType == ListViewItemType.DataItem)
        {
            HyperLink hlnkPost 
                = (HyperLink)e.Item.FindControl("hlnkRecentlyPost");
            hlnkPost.NavigateUrl 
                = "/Post/" + hlnkPost.NavigateUrl.ToString() + ".aspx";
        }
    }
<리스트 7> ListView 컨트롤의 OnItemDataBound 이벤트를 이용한 코드

블로그의 우측 메뉴 화면에서 보여지는 공지사항이나 Archives 목록, 그리고 카테고리 정보를 표시하기 위한 코드도 <리스트 6>과 <리스트 7>에 보여지는 코드와 동일하게 구성을 하였으며, 이에 대한 코드는 이달의 디스켓을 참고하도록 한다. 이와 같이 구성된 블로그의 우측 메뉴 화면은 <화면 7>과 같이 보여지게 된다.

<화면 7> 블로그 우측 메뉴 화면

블로그의 우측 메뉴에서 보여지는 Archives 목록은 "BLOG_ARCHIVES" 테이블에서 조회하게 된다. 그러므로, 포스트를 작성할 때와 삭제할 때, "BLOG_ARCHIVES" 테이블에 필요한 값을 추가, 제거해야 하지만, 마소 3월호에는 필자가 그 부분을 제외하였기 때문에 "BLOG_ARCHIVES" 테이블에 값을 입력하는 코드를 추가해야 한다.

“Admin.aspx.cs"의 ”btnPostWrite_Click" 이벤트에 정의된 코드의 가장 아래에 SetArchivesInfo(1)이라는 메소드 호출 코드를 추가하고, ”btnPostDelete_Click" 이벤트에 정의된 코드의 가장 아래에 SetArchivesInfo(-1)이라는 메소드 호출 코드를 추가한다. SetArchivesInfo() 메소드 안의 인자는 "BLOG_ARCHIVES" 테이블에 등록된 ArchivesDate 필드와 일치하는 ArchivesCount 필드의 값을 메소드 안의 인자만큼 증가시키거나 감소시키기 위한 값이 들어가게 된다. SetArchivesInfo 메소드는 저장 프로시져인 “dbo.usp_SetArchivesCount"를 호출하게 되며, 이 저장 프로시져는 <리스트 8>과 같은 코드로 되어 있다.

CREATE PROCEDURE [dbo].[usp_SetArchivesCount]
(
    @archivesdate    VARCHAR(7)
,    @archivescount    INT
)
AS
BEGIN
IF EXISTS 
(SELECT ArchivesDate FROM BLOG_ARCHIVES WHERE ArchivesDate = @archivesdate)
BEGIN
    UPDATE BLOG_ARCHIVES SET ArchivesCount = ArchivesCount + @archivescount
    WHERE ArchivesDate = @archivesdate
 
    IF 0 = 
    (SELECT ArchivesCount FROM BLOG_ARCHIVES WHERE ArchivesDate = @archivesdate)
    BEGIN
        DELETE FROM BLOG_ARCHIVES WHERE ArchivesDate = @archivesdate
    END
END
ELSE
BEGIN
    INSERT INTO BLOG_ARCHIVES VALUES (@archivesdate, @archivescount)
END

<리스트 8> usp_SetArchivesCount 저장 프로시져의 코드


RSS 페이지의 구성

필자가 마소 1월호에 기고한 “ASP.NET AJAX로 만드는 RSS 뷰어”에서 RSS에 대해서 간략하게 다룬 적이 있고, RSS에 관련해서는 인터넷 검색을 통하여 정보를 쉽게 얻을 수 있기 때문에 RSS에 대한 설명 및 구성 원리에 대한 설명은 생략하도록 한다. 블로그 상단 메뉴의 RSS 메뉴를 클릭하면 “Rss.aspx" 페이지로 이동되게 되며, ”Rss.aspx.cs" 페이지에서 RSS 정보를 내보내기 위해 블로그 및 포스트의 정보를 Xml 형식으로 구성하게 된다.
RSS 페이지를 만드는 방법은 XmlDocument 클래스를 이용하여 XmlNode를 생성하여 RSS 형식에 맞는 태그로 구성하는 방법이 일반적이지만, 필자의 경우는 좀 더 간편하게 Xml 형식을 구성하기 위해서 StringBuilder 클래스를 사용하였으며, XmlDocument 클래스의 LoadXml() 메소드를 통해, 구성된 StringBuilder 클래스의 정보를 추가하는 것으로 RSS 페이지를 구성하였다. RSS 페이지에 관련된 소스는 이달의 디스켓을 참고하도록 하며, 상단 메뉴의 RSS 메뉴를 클릭하였을 때 표시되는 화면은 <화면 8>과 같다.


<화면 8> RSS가 표시된 화면

카테고리에 속한 포스트 목록 페이지의 구현

블로그 우측 메뉴 영역의 Categories 아래에 표현되는 카테고리를 누르거나, 포스트 조회 페이지에서 카테고리 항목을 누를 경우에는 선택한 카테고리에 등록되어 있는 포스트의 제목이 나타나는 목록 및 포스트를 한꺼번에 볼 수 있는 포스트 목록 페이지로 이동하게 된다. 이렇게 선택한 카테고리에 등록되어 있는 포스트의 목록을 구현하기 위해서, 웹 사이트 이름에서 우측 마우스를 클릭하여 “새 항목 추가(Add New Item...)" 메뉴를 선택하여, "Category.aspx"라는 이름의 페이지를 추가한다. "Category.aspx"에서는 "BLOG_POSTS" 테이블의 CategoryID 필드와 쿼리스트링으로 CategoryID값이 동일한 포스트의 목록을 조회하여 ListView 컨트롤 및 DataPager 컨트롤을 이용하여 화면에 정보를 출력하게 한다. 먼저 Web.Config의 <rewriter> 섹션에 Category.aspx 페이지로 URL을 재작성하기 위한 <rewrite> 요소를 추가한다.

<rewrite url="/Category/(.+).aspx" to="/Category.aspx?categoryid=$1" />

그리고 "Category.aspx.cs" 파일의 Page_Load 이벤트에서 쿼리스트링으로 받은 CategoryID값을 바탕으로 카테고리명과, 현재 카테고리에 등록되어 있는 전체 포스트의 개수를 검색하기 위한 GetCategoryInfo() 메소드를 호출한다. GetCategoryInfo() 메소드의 코드는 <리스트 9>와 같다.

    // 카테고리 정보 가져오기
    private void GetCategoryInfo()
    {
        BlogDataClassesDataContext blogDataContext 
            = new BlogDataClassesDataContext();
 
        List<BLOG_CATEGORIES> blogCategory =
                            (from c in blogDataContext.BLOG_CATEGORIES
                             where c.deletedate == null
                             orderby c.sortkey ascending
                             select c).ToList();
 
        LvwCategory.DataSource = blogCategory;
        LvwCategory.DataBind();
 
        blogDataContext = null;
    }
<리스트 9> GetCategoryInfo() 메소드의 코드

또한 필자가 마소 2월달에 만든 “VIEW_POSTLIST" 뷰를 사용하는 LinqDataSource 컨트롤을 추가하여, 카테고리에 속한 포스트의 목록을 구현하는 데이터 소스 컨트롤로 사용하도록 한다. 이 데이터 소스 컨트롤을 포스트의 목록을 출력하기 위한 ListView 컨트롤과 연결시키도록 하며, ListView 컨트롤의 페이징을 위해 DataPager 컨트롤을 사용하며, DataPager 컨트롤의 PagedControlID 속성을 ListView 컨트롤의 ID로 설정함으로써, ListView 컨트롤의 페이징을 구현하도록 한다. 지금까지 설명한 내용을 바탕으로 한 코드는 <리스트 10>과 같다.

<div class="list">
    <div class="sub">
        "<asp:Label ID="lblCategoryName" runat="server"></asp:Label>"에 해당되는 글 
        <asp:Label ID="lblCategoryCount" runat="server"></asp:Label>
        <hr />
        <asp:ListView ID="LvwPostList" runat="server" 
            DataSourceID="LinqDataSourcePostList"
            onitemdatabound="LvwPostList_ItemDataBound" 
            ondatabinding="LvwPostList_DataBinding">
            <LayoutTemplate>
                <ul>
                    <asp:PlaceHolder ID="itemPlaceholder" runat="server"></asp:PlaceHolder>
                </ul>
            </LayoutTemplate>
            <ItemTemplate>
                <li><span>
                    <%#Convert.ToDateTime(Eval("CreateDate").ToString()).ToShortDateString()%>
                </span>
                <asp:HyperLink ID="hlnkPost" runat="server" NavigateUrl='<%#Eval("PostID")%>' 
                Text='<%#Eval("PostName")%>'></asp:HyperLink></li>
            </ItemTemplate>
        </asp:ListView>
        <div style="text-align:center">
            <asp:DataPager ID="DpPostList" runat="server" PagedControlID="LvwPostList" 
                PageSize="4">
                <Fields>
                    <asp:NumericPagerField ButtonType="Link" ButtonCount="5" />
                </Fields>
            </asp:DataPager>
        </div>
    </div>
</div><br />
 
<asp:LinqDataSource ID="LinqDataSourcePostList" runat="server" 
        ContextTypeName="BlogDataClassesDataContext" 
        Select="new (postid, postname, createdate, categoryid)" 
        TableName="VIEW_POSTLISTs" OrderBy="createdate desc" 
    Where="categoryid == @categoryid">
    <WhereParameters>
        <asp:ControlParameter ControlID="hidCategoryID" Name="categoryid" 
            PropertyName="Value" Type="Int32" />
    </WhereParameters>
</asp:LinqDataSource>
<리스트 10> 포스트 목록 페이지의 구현 코드

<리스트 10>에서 작성된 코드를 통해 표시되는 화면은 <화면 9>와 같다.

<화면 9> 포스트 목록이 표시된 화면

<리스트 10>에서 보여지는 코드 중에 ListView 컨트롤에서 사용되는 OnDataBounding 이벤트를 사용하는 이유는 DataPager 컨트롤을 사용할 경우에 선택한 해당 페이지의 포스트의 목록 화면 아래에 보여지게 되는 포스트의 정보를 별도로 LINQ 쿼리를 통하여 보여주기 위해서 사용하는 것이며, OnDataBounding 이벤트에서는 포스트의 정보를 보여주는 GetPostInCategoryList() 메소드를 호출하게 된다. GetPostInCategoryList() 메소드의 코드는 <리스트 11>과 같다.

    private void GetPostListInCategory()
    {
        blogDataContext = new BlogDataClassesDataContext();
        _pageIndex = DpPostList.StartRowIndex / _pageCount;
 
        List<BLOG_POSTS> blogPostList =
            (from c in blogDataContext.BLOG_POSTS
             where c.categoryid == int.Parse(_categoryID) && c.deletedate == null
             orderby c.createdate descending
             select c).Skip(_pageIndex * _pageCount).Take(_pageCount).ToList();
 
        LvwPostInCategory.DataSource = blogPostList;
        LvwPostInCategory.DataBind();
 
        blogDataContext = null;
    }
<리스트 11> 선택한 페이지의 포스트 목록 출력 화면 코드

<리스트 11>에서 보는 것과 같이 _pageIndex라는 전역 변수를 선언한 후, DataPager 컨트롤의 StartRowIndex 속성을 통해 DataPager 컨트롤의 시작행을 알아온 후에, 한 화면에서 보여지는 _pageCount(필자가 설명하는 블로그에서는 한 화면에서 보여줄 개수를 4로 지정하였다.)로 나눈 값을 _pageIndex 변수에 할당하도록 한다. 그러므로, DataPager 컨트롤의 1페이지를 선택할 경우 _pageIndex값에는 0값이, 3페이지를 선택할 경우에는 _pageIndex값에 2의 값이 들어가게 된다. 이렇게 정의되는 _pageIndex값과 _pageCount 값을 가지고, Skip() 메소드와 Take() 메소드를 이용하여 화면에 출력해야 하는 포스트의 정보를 조회한 후 ListView 컨트롤을 이용하여 화면에 출력하게 된다. 화면에 출력하는 코드는 <리스트 12>와 같다.

<asp:ListView ID="LvwPostInCategory" runat="server" 
    onitemdatabound="LvwPostInCategory_ItemDataBound">
    <LayoutTemplate>
        <asp:PlaceHolder ID="itemPlaceholder" runat="server"></asp:PlaceHolder>
    </LayoutTemplate>
    <ItemTemplate>
        <h1><asp:HyperLink ID="hlnkSubject" runat="server" 
            NavigateUrl='<%#Eval("PostID")%>' 
            Text='<%#Eval("PostName")%>'></asp:HyperLink></h1>
        <h5>작성일 : <asp:Label ID="lblCreateDate" runat="server" 
            Text='<%#Eval("CreateDate")%>'></asp:Label> 
        | 조회수 : <asp:Label ID="lblViewCount" runat="server" 
            Text='<%#Eval("ViewCount")%>'></asp:Label>
        </h5><br />                    
        <asp:Label ID="lblPostContent" runat="server" 
            Text='<%#Eval("PostContent").ToString().Replace("\r\n", "<br />")%>'>
            </asp:Label><br /><br />
        <div class="taginfo">
            태그 : <asp:Label ID="lblPostTag" runat="server" 
            Text='<%#DisplayTagList(Eval("Tag").ToString())%>'></asp:Label>
        </div>
        <div class="otherinfo">
            트랙백 : 0개, 덧글 : <asp:Label ID="lblCommentCount" runat="server" 
            Text='<%#Eval("CommentCount")%>'></asp:Label>
        </div>
        <br /><br /><br />
    </ItemTemplate>
</asp:ListView>
<리스트 12> 선택한 페이지의 포스트 정보 출력 코드

이상으로, 선택한 카테고리에 등록된 포스트의 목록 및 포스트의 목록에서 보여지게 되는 포스트의 정보가 출력되는 “Category.aspx" 페이지에 대한 설명을 마치도록 하겠다. 완성된 ”Category.aspx" 페이지의 화면은 <화면 10>과 같다.


<화면 10> “Category.aspx" 페이지의 전체 화면

이로써 포스트 조회화면에 대한 설명과 덧글의 목록 정보 및 덧글의 추가/수정/삭제, 그리고 블로그 우측 메뉴의 정보 출력 및 RSS 페이지를 구성하는 방법에 대해서 설명하였다. 다음 호는 4개월동안 연재되는 블로그 프로그래밍의 마지막 회로써, 구현하지 못한 검색과 약간의 관리자가 사용할 수 있는 기능에 대해서 설명하도록 하겠다.


Creative Commons License
저작물크리에이티브 커먼즈 코리아 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
반응형

+ Recent posts