login register Sysop! about ME  
qrcode
    최초 작성일 :    2003년 09월 03일
  최종 수정일 :    2003년 09월 03일
  작성자 :    taeyo
  편집자 :    Taeyo (김 태영)
  읽음수 :    110,319

강좌 목록으로 돌아가기

필자의 잡담~

강좌가 드문드문 올라옴에도 불구하고, 관심을 가져주시는 분들이 많네요~~ 감사합니다.

대상 : ASP.NET을 이용하여 스스로 일반 게시판이 작성가능하거나,
         Taeyo's ASP.NET v1.0 서적을 통해서 게시판 만들기를 이미 공부하신 분

저번 강좌를 통해서 데이터를 데이터베이스에 저장할 수 있었으니까요 ^^; 이번 강좌에서는 list.aspx 페이지를 구성해 보도록 하겠습니다. 하지만, 일단은 이 페이지를 간단하게만 구성해 보도록 하겠습니다. 이 list.aspx 페이지는 차후 페이징 기능의 추가 및 다양한 효과를 적용할 것이기에 자못 복잡해질 가능성이 있거든요. 해서, 현재는 간단하게만 작성해 보구요. 이후에, 더 많은 양념을 넣어서 멋지게 구성해 보려 합니다.

아참. 그리고, 그 사이에 게시판의 로직에 대해서 많은 분들이 의견을 주셨습니다. 그 중에는 성능에 대한 조언도 있었구요. 현재 게시판 로직의 문제점을 꼬집으신 의견도 있었습니다. 그 중 가장 큰 문제로 대두된 것은 각각의 레코드를 식별하기 위한 컬럼으로 thread 컬럼을 사용하는 부분인데요. 이렇게 되면, 각 레코드에 대한 고유한 값이 없는 것이나 마찬가지 이기에, 문제가 생길 수 있다는 의견이 있었습니다.

사실, 그 말씀이 맞습니다. (ㅠ_ㅠ)해서, 제 사이트에는 thread 컬럼과는 별도로 seq 컬럼을 두고 있고, 그 컬럼을 identity로 구성해서 각 레코드가 고유한 값을 갖도록 만들어 두었었죠. 하지만, 이 강좌를 진행하면서는 무슨 생각에서였는지 그 부분을 제가 빼버렸더군요. 나이가 들더니 기억력도 깜박깜박하는지, 스스로가 스스로를 이해할 수 없어서...  ㅠ_ㅠ 눈물의 밤을 지새웠습니다. 엉엉엉 ㅠ_ㅠ

해서, 이전 강좌를 보시면 테이블 설계부분을 수정하여 seq 라는 컬럼을 하나 더 추가한 것을 볼 수 있을 것입니다. 그것도 빨간 색으로요~~  여러분도 그러한 컬럼을 추가해 주시기 바랍니다. -_-; 번거롭게 해드려서 정말로 죄송합니다. 부디 함만 봐주시어요~~~

자. 그럼 이제... 계속해서 진행해 보도록 하겠습니다. 일단, list.aspx 페이지를 만들기 위해서 가장 먼저 필요한 것은 SQL 프로시저 일 것입니다. 지금까지 우리가 작업한 스타일을 보면요..

1. 페이지에 쓰일 SQL 프로시저를 만든다.
2. 페이지의 사용자 인터페이스(UI)를 만든다
3. 코드 비하인드 페이지를 작성하며, SQL 프로시저를 사용한다

정도의 수순을 따르고 있어요~. 이 수순은 이번 작업에서도 예외는 아닐 것 같네요 ^^

그럼, 먼저 프로시저를 구성해 보도록 하겠습니다. 이 부분의 쿼리는 차후 페이징 기능과, 성능 향상을 고려하는 부분을 넣으면서 훨씬 복잡해질 수 있습니다만... 일단은 간단하게만 구성해 보도록 하겠습니다. 계층적으로 데이터를 가져오기 위해 일단은 필요한 만큼만 쿼리를 작성하겠다는 것이지요.. 쿼리는 다음과 같습니다.

CREATE PROC UP_SELECT_BOARDLIST
AS
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SET NOCOUNT ON

    SELECT seq, thread, depth, writer, title, readcount, transdate FROM ThreadBoard
    ORDER BY thread DESC
GO

그렇습니다. 쿼리는 매우 단순합니다. 단지, 테이블의 데이터들을 thread 컬럼을 기준으로 역순 정렬해서 가져오기만 하면 되는 것입니다. 그러면, 그 자체가 계층적인 구조로 데이터를 가져오는 것이나 마찬가지이지요. 우리가 데이터를 입력할 경우, 이를 예상하여 데이터를 넣었으니까요~~ 

List.aspx 페이지에서 보여질 필요가 있는 컬럼은 Title, writer, ReadCount, transdate 정도이겠지만,  각각의 레코드들이 계층적으로 들여져서 나타나도록 하기 위해서는 그를 위한 depth 컬럼이나 thread 도 필요합니다. 그리고, 각각의 레코드를 고유하게 나타내는 Seq 컬럼도 필요하겠구요. 해서, 프로시저에는 그러한 컬럼도 넘겨주도록 작성되어져 있습니다. ^^

프로시저를 위와 같이 작성하셨다면, [쿼리 분석기]로 이를 실행해 주세요~~

자. 그럼 일단 간단한 버전이기는 하지만, UP_SELECT_BOARDLIST 프로시저를 만들었습니다. 그렇다면, 이제 이 프로시저를 사용하는  List.aspx 페이지를 작성해 보도록 하겠습니다.

참고

List.aspx는 DataGrid 컨트롤을 사용해서 목록을 출력하려 합니다. 해서, 이 강좌를 위해서는 DataGrid 컨트롤을 먼저 익혀두셔야 합니다. ^^

일단, UI의 디자인을 보여드리도록 하겠습니다. 대략 다음과 같은 결과가 나오도록 UI를 구성해 보려 합니다.

그리고, 페이지의 HTML은 다음과 같습니다. ^^;

<%@ Page language="c#" Codebehind="list.aspx.cs" AutoEventWireup="false" Inherits="AspNetTest.ThreadBoard.list" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
    <HEAD>
        <title>list</title>
        <LINK href="css/main.css" type="text/css" rel="stylesheet">
    </HEAD>
    <body MS_POSITIONING="FlowLayout">
        <form id="list" method="post" runat="server">
            <table cellSpacing="0" cellPadding="0" width="520" border="0">
                <tr><td bgColor="#336699"></td></tr>
                <tr><td bgColor="#668fcd"></td></tr>
            </table>
            <table cellSpacing="2" cellPadding="0" width="520" border="0">
                <tr class="txt01">
                    <td height="25">  글 목록</td>
                    <td align="right"><a href="insert.aspx"><img src="images/b_write.gif" border="0"></a></td>
                </tr>
            </table>
            <table cellSpacing="0" cellPadding="0" width="520" border="0">
                <tr class="txt01"><td bgColor="#f0f0f0"></td></tr>
                <tr class="txt01"><td bgColor="#a6a6a6"></td></tr>
                <tr class="txt01"><td bgColor="#dadada" height="2"></td></tr>
            </table>
            <table cellpadding="0" cellspacing="0" width="520" border="0">
                <tr>
                    <td>
                        <asp:DataGrid id="DataGrid1" runat="server" CssClass="txt01" AutoGenerateColumns="False"
                            BorderColor="#CCCCCC" BorderStyle="None" BorderWidth="1px" BackColor= "White"
                             CellPadding="4" GridLines="None">
                           <SelectedItemStyle Font-Bold="True" ForeColor= "White" BackColor="#669999">
                            </SelectedItemStyle>
                           <ItemStyle ForeColor="#000066"></ItemStyle>
                           <HeaderStyle Font-Bold="True" ForeColor= "White" BackColor="#336699"></HeaderStyle>
                            <FooterStyle ForeColor="#000066" BackColor="White"></FooterStyle>
                            <Columns>
                                <asp:HyperLinkColumn DataNavigateUrlField="seq"  DataTextField="Title" 
                                    DataNavigateUrlFormatString="Content.aspx?seq={0}" HeaderText="제목">
                                    <HeaderStyle HorizontalAlign="Center" Width="430px"></HeaderStyle>
                                    <ItemStyle HorizontalAlign="Left"></ItemStyle>
                                </asp:HyperLinkColumn>
                                <asp:BoundColumn DataField="writer" HeaderText="작성자">
                                    <HeaderStyle HorizontalAlign="Center" Width="70px"></HeaderStyle>
                                    <ItemStyle HorizontalAlign="Center"></ItemStyle>
                                </asp:BoundColumn>
                                <asp:BoundColumn DataField="transdate" HeaderText="작성일"
                                    DataFormatString="{0:yyyy-MM-dd}">
                                    <HeaderStyle HorizontalAlign="Center" Width="80px"></HeaderStyle>
                                </asp:BoundColumn>
                                <asp:BoundColumn DataField="readcount" HeaderText="읽음">
                                    <HeaderStyle HorizontalAlign="Center" Width="40px"></HeaderStyle>
                                    <ItemStyle HorizontalAlign="Center"></ItemStyle>
                                </asp:BoundColumn>
                            </Columns>
                        </asp:DataGrid>
                    </td>
                </tr>
            </table>
        </form>
    </body>
</HTML>

UI 페이지에서 중요한 부분은 DataGrid를 적절하게 꾸미는 부분이 되겠네요. 저의 경우는 그리드의 모양새를 위해 VS.NET에서 [자동 서식]으로 [전문가 2]를 지정해 보았구요. 그 상태에서 몇몇 태그들을 조금 변경해 보았습니다. 여러분은 여러분이 원하시는대로 그 모습을 꾸며주시면 되겠습니다. 중요한 부분은 그리드 구역내의 <Columns> 이니까요.

<Columns> 구역을 살펴보면, 총 4개의 컬럼을 화면에 나타내는 것을 알 수 있습니다. 글의 제목과 글을 쓴 사람, 입력 날짜와 읽음 수 정보가 바로 그것이지요 ^^. 글 제목은 하이퍼링크를 연결하여, 글의 세부정보를 확인할 수 있는 Content.aspx 페이지로 이동할 수 있게 해 보았습니다. 그 외의 다른 부분들은 단순 출력일 뿐이지요. 해서, 하이퍼링크가 필요한 제목 컬럼에는 HyperLinkColumn을 사용해 보았구요. 단순 출력을 요구하는 컬럼들에는 BoundColumn을 사용해 보았습니다. 물론, 이 부분은 지극히 주관적인 취향에 따라 결정되는 부분이기는 합니다만 말입니다. (여러분의 경우는 TemplateColumn을 사용하실 수도 있을 겁니다 ^^)

그러면, 이제는 DataGrid에 ThreadBoard 테이블의 레코드를 바인딩하는 코드가 필요하겠네요. 실제적인 데이터 연결 코드가 말입지요~  자. 다음은 바로 그러한 작업을 수행하는 코드 비하인드 페이지의 코드입니다. 이 코드는 이미 Repeater 및 DataList 강좌를 통해서 다루어 본 것과 거의 동일한 쿼리이기에 이해하는 데에 어려움은 없을 겁니다. ^^

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace AspNetTest.ThreadBoard
{
    /// <summary>
    /// list에 대한 요약 설명입니다.
    /// </summary>
    public class list : System.Web.UI.Page
    {
        protected System.Web.UI.WebControls.DataGrid DataGrid1;
    
        private void Page_Load(object sender, System.EventArgs e)
        {
            // 여기에 사용자 코드를 배치하여 페이지를 초기화합니다.
            if(!IsPostBack)
            {
                Listing();
            }
        }

        private void Listing()
        {
            string ConnectStr = "server=(local);database= TEST;userid=sa;password=";

            SqlConnection Con = new SqlConnection(ConnectStr);
            SqlCommand Cmd = new SqlCommand();
            Cmd.Connection = Con;
            Cmd.CommandText = "UP_SELECT_BOARDLIST";
            Cmd.CommandType = CommandType.StoredProcedure;

            SqlDataAdapter adp = new SqlDataAdapter(Cmd);
            DataSet ds = new DataSet();

            adp.Fill(ds, "ThreadBoard");

            DataGrid1.DataSource = ds;
            DataGrid1.DataBind();
        }

        Web Form Designer generated code
    }
}

간단하지 않습니까? 그렇습니다. 이것은 자주 접해보았던 소스일 것이기에 어려울 것은 없어보입니다. 그래도 간단하게 설명하자면, 이 코드는 UP_SELECT_BOARDLIST 프로시저를 Command 개체로 실행하여, 그로써 얻어지는 결과집합을 DataSet으로 받아서, 그 DataSet과 DataGrid를 바인드하는 것이랍니다. 너무 간단했나요???  -_-;;;

일단은 여기까지만, 작성하고 결과를 확인해 보도록 하겠습니다. 어떤 결과가 나타나나요... 다음과 같은 결과가 나타나겠죠?

물론, 레코드들이 제대로 출력되기는 할 것입니다. 목록은 제대로 나타나지만, 그 모습이 계층적이지는 않다는 것이 문제이기는 하지만요. ^^ 이제부터 할 일은 그것을 계층적으로 보이게 하기 위해서, 각각의 레코드들을 조금 손 보아야 한다는 것입니다~~ 이를 위해서는 일단 2개의 이미지가 필요합니다. 하나는 Reply를 의미하는 화살표 모양의 이미지이구요. 다른 하나는 들여쓰기를 위해 필요한 투명한 이미지 입니다. 이 투명 이미지의 너비를 각 레코드의 depth를 기준으로 늘려서 나타내면, 각 글의 depth에 따라 더 들여지거나, 덜 들여지게 만들 수가 있는 것입니다. 다음 그림처럼 말이지요 ^^

그렇다면, 여러분은 무엇보다 먼저 각각을 위한 이미지가 필요하겠죠? 뽀로샵을 잘 다루시거나, 잘 다루시는 그래픽 툴이 있다면 이러한 이미지를 직접 만드셔도 되겠구요. 그게 귀찮으시다면 제가 사용하는 이미지를 사용하셔도 무관합니다. 제 사이트에서 사용하는 이미지를 다운로드 받고 싶다면 다음 링크를 클릭하세요 ^^..  이 Zip 파일 안에는 blank.gif와 re.gif 파일, 2개가 들어있습니다. 그것을 현재 디렉터리의 images 폴더에 넣어주시면 되겠네요. 감사합니다.

계층형 게시판 들여쓰기용 이미지

이제, 해야할 일은 데이터 그리드에 ThreadBoard 의 레코드들이 바인드 되는 시점에, 그러니깐 정확하게 말하자면 각각의 레코드(Row)가 바인드되는 시점에 각 레코드 단위로 그 레코드의 depth를 검사하는 작업입니다. 만일, 현재의 레코드의 depth가 0보다 크다면 그것은 이 레코드가 답변 글이라는 것을 의미하는 것이기에, 공백 이미지(blank.gif)와 Re 이미지를 적절히 제목 앞에 추가해야 하기 때문이지요.

이러한 작업을 하기에 가장 적절한 곳은 DataGrid  컨트롤이 제공하는 ItemDataBound 이벤트 구역입니다. 이 이벤트는 데이터 그리드의 각 항목(즉, 행)이 바인드된 다음에 발생하는 이벤트이니까요 ^^

각 행이 바인드 된 직 후에는 ItemDataBound 이벤트를 무조건적으로 거치게 되기에, 이 이벤트는 각 행단위로 어떤 추가적인 작업을 해 주고자 할 경우에 상당히 유용한 수단이 됩니다. 그렇다면, 이 이벤트 안에서 우리는 어떠한 작업을 해야할까요??

그렇습니다. 먼저 현재 행의 컬럼값들 중에서 depth에 해당하는 컬럼의 값을 읽어오고요~ 그 다음으로 그 값이 0 보다 큰 값인지를 검사합니다. 해서, 만일 그 값이 0보다 크다면 blank.gif 이미지와 re.gif 이미지에 해당하는 Image 개체를 동적으로 생성해서, 현재 항목(DataGrid의 현재 Row)의 첫번째 Cell(즉, '제목' 이 출력되는 셀)에 추가해 주면 되는 것이지요. 먼저, 소스를 보시고 계속 설명을 드려보겠습니다.

ItemDataBound 이벤트를 웹 폼에 추가하기 위해서, VS.NET의 디자인 모드로 전환하도록 하세요. 그리고, 웹 폼에서 DataGrid 컨트롤을 선택한 다음, [속성 창]에서 번개 모양의 버튼을 클릭하여, DataGrid의 이벤트 목록이 [속성 창]에 나타나도록 합니다. 그러면, 그 목록 중에 ItemDataBound가 있거든요. 그 항목의 우측 구역을 더블클릭하시면 됩니다. 그렇게 하면 자동으로 이벤트 함수가 코드 비하인드 페이지에 만들어지게 되니까요 ^^

이제, ItemDataBound 이벤트가 코드 비하인드 구역에 생성되었다면, 그 안을 다음과 같은 코드들로 채워주도록 하세요 ^^

private void DataGrid1_ItemDataBound(object sender,
                                System.Web.UI.WebControls.DataGridItemEventArgs e)
{
    if(e.Item.ItemType == ListItemType.Item ||
                                              e.Item.ItemType == ListItemType.AlternatingItem)
    {
        int depth = (int)((DataRowView)e.Item.DataItem)["depth"];

        if(depth > 0 )
        {
            System.Web.UI.WebControls.Image blankImg;
            blankImg = new System.Web.UI.WebControls.Image();
            blankImg.ImageUrl = "Images/blank.gif";
            blankImg.Height = Unit.Pixel(0);
            blankImg.Width = Unit.Pixel(depth * 15);

            e.Item.Cells[0].Controls.AddAt(0, blankImg);

            blankImg = new System.Web.UI.WebControls.Image();
            blankImg.ImageUrl = "Images/re.gif";

            e.Item.Cells[0].Controls.AddAt(1, blankImg);
        }
    }
}

태오의 Taeyo's ASP.NET with C# 이라는 베수투셀러 책(-_-!!)을 이미 섭렵하신 분이라면 위의 코드가 그리 생소하게 느껴지시지는 않을 것입니다. 책에서 게시판을 만들 때, '최근에 올라온 글 부각하기' 섹션에서 이와 비슷한 내용을 다루었었으니까요. (은근한 책 광고에 질린 분들께는 매우 죄송하게 생각합니다. ㅠ_ㅠ)

에... -_-+++  그럼..  어색한 분위기를 전환하기 위해서라도... 위의 코드를 살펴보도록 하죠.

ItemDataBound 이벤트가 각 항목이 바인드된 후에 일어나는 이벤트라고는 이미 말씀을 드렸습니다. 해서, 이 이벤트에서는 먼저 다음과 같은 if 문을 통해서, 현재 바인드가 된 항목(행)이 Item 항목이거나, AlternatingItem 항목인지를 검사합니다. ItemDataBound 이벤트는 헤더나 푸터 항목이 바인드되는 경우에도 발생하기에 그러한 경우를 거를 필요가 있기 때문이지요.

해서, 만일 현재 항목이 Item  이거나 AlternatingItem 이라면... 그 때에만 이하의 코드가 동작하도록 하는 겁니다. 그렇다면, 그러한 경우에 어떠한 작업을 해야 할까요???

그렇습니다. 우선 현재 바인드된 행에서 depth 컬럼의 값이 어떻게 되는지를 알아와야 합니다. 해서, 사용하는 코드가 바로..

int depth = (int)((DataRowView)e.Item.DataItem)["depth"];

입지요. 코드가 좀 복잡해 보이죠? 여러번의 캐스팅(casting, 형 변환)이 사용되기에 그렇게 보이는 것일 뿐이예요. 잘 뜯어보면 그리 어려운 코드는 아니랍니다. 코드는 먼저, 현재 행에 해당하는 DataItem을 가져옵니다. 간단하게라면, 이 DataItem이 바로 현재 바인드 된 레코드를 의미한다고 봐도 좋습니다. 어쨌든, 현재의 행을 의미하는 DataItem에 접근했다면, 다음으로 이를 DataRowView 형으로 형 변환할 필요가 있습니다. DataItem 자체는 Object 이기에, 그것을 코드에서 사용하려면 먼저 적절한 타입으로 변환할 필요가 있기 때문이지요. 그렇게 DataRowView 타입으로 형 변환을 하시면 그 이후에는 각각의 컬럼에 접근해서 값을 얻어올 수가 있게 되는 것입니다. 이해가 잘 안가신다??? 음...  -_-+++ 그럼 일단은 그냥 외우시면 됩니다. (핫핫핫 ^^;;;)

즉, ((DataRowView)e.Item.DataItem) 라는 것은 현재 행의 레코드 자체를 의미하는 것이 되구요. 그 현재의 레코드에서 depth 컬럼의 값을 가지고 오고 싶다면 ((DataRowView)e.Item.DataItem)["depth"]와 같이 접근하시면 되는 것입니다. 하지만, 중요한 것은 이 컬럼의 값을 우리는 int 형의 타입으로 사용하고자 하는데, 기본적으로는 이 값도 역시 Object 이기에 여기서, 컬럼 값에 대한 적절한 형변환이 한번 더 필요한 것이지요. 해서, 전체 구문 앞에 (int)를 사용해서 이 컬럼의 값을 int 형으로 변환하고 있음을 볼 수 있습니다. 좀 복잡하기는 하지만, 이로써 우리는 현재 레코드의 depth 컬럼 값을 integer 값으로 얻어올 수가 있게 되는 것이지요. 그리고, 코드에서는 그 값을 depth라는 지역변수에 담아두고 있구요 ^^

depth 라는 변수에 담긴 값은 현재 레코드의 depth 컬럼 값이라는 것은 이제 알 수 있을 것입니다. 이제 이어져야 하는 작업은 이 값이 0보다 크냐? 아니냐? 라는 의문이 되겠죠? 해서.. 그 값이 0이라면 질문인 글이기에 별다른 추가작업없이 진행하시면 되겠고, 만일 그 값이 0보다 크다면 이 레코드는 답변 글이기에, 제목 앞에 들여쓰기를 위한 blank.gif 이미지를 넣어주어야 하고, 답변임을 의미하는 re.gif 이미지도 넣어주어야 할 것입니다.

해서, 이어지는 코드는 바로 그러한 작업을 하고 있습니다. depth 값이 0 보다 큰지를 비교해서, 그렇다면 두 개의 이미지 개체를 만들어 "제목"이 출력되는 셀안에 그 이미지들을 Add 하고 있는 것이지요.

여기서 주의해야 할 점은 blank.gif 이미지의 너비를 우리가 depth 에 따라 늘려줘야 한다는 것입니다. 그래야, 답변의 depth가 큰 것일수록 더 많이 들여써지게 되니까요. blank.gif 이미지는 투명이미지이기에 그 너비를 우리가 마구 늘려도 사용자에게 보일 때는 아무런 문제가 없답니다. ^^ 아참... Height도 고정적으로 그 값을 주기는 하셔야 합니다. 안 그러면 너비가 늘어남에 따라 높이도 따라 늘어나기에, 출력되는 게시판이 보기 싫어질 수가 있거든요... 어떻게 보기 싫어지는지가 궁금하시면 코드 중에서 다음 부분을 주석처리 하시거나, 삭제하신 다음 실행해 보세요. ^^;

 blankImg.Height = Unit.Pixel(0);

이제 blank.gif 이미지에 대한 개체가 생성되고, 설정도 끝났다면 이를 데이터 그리드에 추가할 필요가 있습니다. 해서, 이어지는 코드는 바로 이거죠

e.Item.Cells[0].Controls.AddAt(0, blankImg);

"제목"이 출력되는 셀은 현재의 Item의 Cells 중에서 첫 번째 셀이며, 그 인덱스는 0 이기에, 우리는 e.Item.Cells[0] 라는 코드를 통해서 현재 행의 "제목" 셀을 찾아갈 수 있습니다. 그리고, 그 안을 구성하고 있는 Controls 중에 제일 앞에, 즉 인덱스 0인 위치에 현재 생성된 이미지를 추가하는 것이지요. ^^

여기서 AddAt 메서드가 참으로 중요한 역할을 합니다. 이 메서드는 우리가 개체를 삽입할 위치를 지정할 수 있게 해주는 메서드이니까요. 비슷한 메서드로는  Add 메서드가 있는데요. 이는 우리가 위치를 지정할 수가 없어요. 언제나 셀의 가장 마지막에 개체를 추가할 뿐이죠 ^^

자.. 이로써, 투명 이미지는 추가가 된 듯 합니다. 이제는 re.gif 이미지를 추가할 차례죠 ^^ 이 부분도 투명 이미지를 추가할 경우와 동일합니다. 하지만, 자원 절감 차원에서 이 이미지 개체는 기존에 만들어 놓은 이미지 개체 변수를 재 사용하도록 해 보았습니다. 해서, 소스에서는 re.gif 이미지를 blankImg 변수에 다시 할당한 다음, 마찬가지로 "제목"이 출력되는 셀 안에 추가하고 있지요. AddAt() 메서드를 사용해서 말입니다.

주의할 점은 이번에 추가되는 re.gif 이미지가 삽입되는 위치는 1번째 인덱스, 즉 셀 안에서 두번째 컨트롤 위치에 들어가야 한다는 점입니다. 왜냐하면 공백 투명 이미지가 re 이미지보다 먼저 나와야 하기 때문이지요. 코드를 만들어 놓고 보니, 이럴 바에는 차라리 re 이미지를 먼저 0 번째 위치에 삽입하고, 투명 이미지를 나중에 다시 0번째 위치에 삽입하는 것이 보기에 나을 것 같다는 생각이 들기도 하네요 ^^. 뭐 반드시 그래야 하는 것은 아닙니다만 말입니다...  바꾸고 싶긴 한데... 음.. 그러면, 위에서 쓴 글도 다 바꾸어야 하니깐.. 저는 그냥 진행할랍니다. 히히~~~~

자. 이로써 완성 되었습니다. 그렇다면, 이제 실행하고 결과를 보아야 하겠죠??? 죠쬬쬬~~~~ 여기까지 코드를 추가하고 실행을 하게 되면.. 짜잔, 다음과 같이 기대한대로의 결과가 멋드러지게 출현하는 것이옵니다.

그리고, 스타일시트를 약간 건드려서 하이퍼링크의 스타일을 조금 바꾸어주면 다음과 같이 깔끔해지게 되겠죠???

그렇습니다.

아??? 네???  넹???? 말 나온 김에 '최근 글 강조하기' 기능도 달아달라구요??? 그거는 제 책에서 이미 다룬 부분인데.. 굳이 여기서 다시 다룰 필요가......  엇??? 네????? 

아니, 네??? 여보세요???  네...  그 기능을 달고 싶으면 책을 사서 보라는 거냐구요???  아니요.. 그런 것은 아닌데요.. 뭔가 오해가...  허거걱!!!  아니.. 그게요... 잠시만요~~ 말을 좀 끝까지 들어주세요... 그러니깐, 굳이 변명을 드리자면요... 여보세요??  여보세요???

"뚜뚜뚜-------"

이렇게 전화를 끊으시면 곤란하지요.. 전 아직 해명을 못했단 말입니다... ㅠ_ㅠ   그렇다면, 오명을 벗기 위해서라도... 그 기능을 추가로 달아드려야 할 것 같네요. 그렇죠? 그렇습니다. 유치한 개그를 잠시 봐 주신 여러분께 대단한 감사의 말씀을 드리면셔~~~

기능 들어갑니다.~~~~

사실. 그 기능은 방금했던 작업과 크게 다르지 않습니다. 각각의 행이 바인드 되는 시점에(ItemDataBound 이벤트) 현재 행의 transdate 컬럼 값을 가져와서, 그 값(즉, 글 입력시간)과 현재의 시간을 비교하는 것이지요. 해서, 만일 그 시간 차이가 24시간 이내라면 비교적 최근에 올라온 글이니 글 제목 뒤에 "new" 라는 이미지를 나타나도록 하는 것입니다. 코드를 보면 이해가 쉬울 것 같네요. 다음은 ItemDataBound  이벤트 코드에 그러한 부분을 추가한 모습입니다.

private void DataGrid1_ItemDataBound(object sender,
                                System.Web.UI.WebControls.DataGridItemEventArgs e)
{
    if(e.Item.ItemType == ListItemType.Item ||
                                              e.Item.ItemType == ListItemType.AlternatingItem)
    {
        int depth = (int)((DataRowView)e.Item.DataItem)["depth"];

        if(depth > 0 )
        {
            System.Web.UI.WebControls.Image blankImg;
            blankImg = new System.Web.UI.WebControls.Image();
            blankImg.ImageUrl = "Images/blank.gif";
            blankImg.Height = Unit.Pixel(0);
            blankImg.Width = Unit.Pixel(depth * 15);

            e.Item.Cells[0].Controls.AddAt(0, blankImg);

            blankImg = new System.Web.UI.WebControls.Image();
            blankImg.ImageUrl = "Images/re.gif";

            e.Item.Cells[0].Controls.AddAt(1, blankImg);
        }

        // 24시간 이내에 올라온 글은 글 제목 뒤쪽에 이미지를 달아준다.
        DateTime orginDate = (DateTime)((DataRowView)e.Item.DataItem)["transDate"];

        TimeSpan gap = DateTime.Now - orginDate;

        if(gap.TotalMinutes < 1440)
        {
            Literal l = new Literal();
            l.Text = " <img src=images/lastest.gif>";

            e.Item.Cells[0].Controls.Add(l);
        }
    }
}

추가된 소스는 이전에 설명드린 소스와 사실상 하는 일은 비슷합니다. 중간 과정이 조금 달라서 그렇죠...

DateTime orginDate = (DateTime)((DataRowView)e.Item.DataItem)["transDate"];

그렇습니다. 먼저 현재 행에서 trasdate 값을 가져옵니다. 이 값은 데이터베이스 내에 smalldatetime 타입, 즉 DateTime 형식이기에, 그에 맞게 형 변환을 해서 DateTime  타입의 변수에 담고 있는 것을 보실 수 있습니다. ^^

그리고, 그 날짜와.. 현재의 날짜(DateTime.Now) 사이의 차이를 구하는 것이지요. 이 차이는 TimeSpan이라는 구조체 값을 반환하는데요. 이 구조체는 시간 간격을 나타내는 역할을 합니다.  단, 이 값은  11일, 13시간, 46분, 40초와 같은 식으로 그 값을 가지고 있기에, 이 값을 우리의 예제에서 사용하기 전에는 이 시간 간격을 통일된 단위로 먼저 전환할 필요가 있어요. 예를 들면, 분이나 초로 말이죠~~

해서, 소스에서는 그 시간 간격을 TotalMinutes 속성을 사용하여 분 단위의 시간으로 바꾼 다음, 그 값을 1440 과 비교하고 있습니다. 1440 은 하루를 의미하는 총 분(Minute)이죠???

 if(gap.TotalMinutes < 1440)

해서, 위의 비교문은 현재 행의 글 입력시간(transdate)과 현재 시간과의 차이가 하루이내 인지를 검사하는 것이구요. 만일, 하루 이내라면 new 라는 이미지를 제목의 뒤에 추가하려는 것이랍니다. ^^

이번 예제에서는 new 이미지를 추가하기 위해서, 이미지 개체를 사용하기 보다는 단순 레이블을 사용하고 있어요. 이미지 개체를 사용해도 되지만, 뭐.... 이번에는 이것을 사용해 보았습니다. 해서, 레이블에는 이미지 태그를 문자열로 지정하고 있구요. 그것을 Add 메서드를 통해서 첫번째 셀(제목이 출력되는 셀)의 제일 마지막에 추가하고 있는 것이지요 ^^

그리 어렵지 않지요????  그렇다면, 이제 페이지를 실행하고 결과를 보도록 해요~~~~ ^^

아참. 이제 이렇게 다 구성하였다면, 저번 강좌에서 만든 insert.aspx 페이지의 코드를 약간 손 볼 필요도 있답니다. 그러니깐... 왜 글을 입력한 다음에, SAVE 버튼을 누르면 입력된 글을 서버에 저장하잖아요??? 바로 그 부분인데요... PostButton_Click 이벤트 처리의 마지막 부분.. 즉, 글을 입력한 다음 자바스크립트로 "저장되었습니다" 라고 나타나게 해 주었던 다음 부분을 이제... 글 입력 후 자동으로 list.aspx 페이지로 이동하게끔 만들자는 것이지요.

string script = "<script>alert('저장되었습니다');</script>";
Page.RegisterClientScriptBlock("done", script);

다음과 같이 말입니다....

Response.Redirect("list.aspx");

이러면, 글 입력 후 자동으로 list.aspx 페이지로 이동하겠죠??

자.. 이제 대략적인 목록 페이지의 골격은 갖춘 것 같네요 ^^; 앞으로 더욱 기능이 추가되어야 하겠습니다만 말입니다... 페이징, 검색 등등....

다음 강좌에서는 Content.aspx 페이지를 만들어 보도록 하구요. 그를 이어서.. Insert.aspx 페이지를 답변까지 가능하도록 구성해 보겠습니다..  ^^ 그리고, 저는 드뎌 이번 주에 한국으로 컴백을 합니다. 4달간의 미국 생활을 마치고서 말입니다. 영어는 0.000745 % 밖에 늘지 않았지만서두.. 느낀 것은 조금 있었습니다... 느낀 것이 무엇이냐 물으신다면... "가족의 소중함" 그리고, "친구들의 소중함" 이었습니다.

"어이~~ 기둘리~~~ 내가 지금 간다~~~~  ^^"

9월 2째주에 있을 벙개때 뵙겠습니다. 감사합니다.

좋은 하루 되세요.


authored by

  dark1288
  2015-06-02(15:48)
뒤늦게 닷넷을 공부하는중인데요
본 강좌대로 따라가는 중인데요.
DataGrid1.DataSource = ds;
DataGrid1.DataBind();
부분에서 NullReferenceException 예외오류가 나길래

try
{
DataGrid1.DataSource = ds;
DataGrid1.DataBind();
}
catch (System.NullReferenceException e)
{
System.Console.WriteLine(e.Message);
}
finally
{

}


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})