login register Sysop! about ME  
qrcode
    최초 작성일 :    2002년 10월 02일
  최종 수정일 :    2002년 10월 02일
  작성자 :    cassatt
  편집자 :    Taeyo (김 태영)
  읽음수 :    37,669

강좌 목록으로 돌아가기

필자의 잡담~

페이지 추가/컨트롤 설정

지난 강좌에선 게시판을 목록으로 표시해주는 페이지 ( List.aspx ) 를 했었습니다. 목록 페이지에서 제목을 클릭하면, 해당 글의 내용을 표시해 주어야 할텐데요, 이번 강좌가 글의 내용을 확인하는 페이지인, View.aspx 를 만들 차례입니다.

솔루션 탐색기에서 지난 List.aspx 추가할때와 같은 방식으로 웹 폼 페이지를 하나 추가하고요, 이름을 View.aspx로 합니다. 그리고 마찬가지로 페이지 레이아웃을 FlowLayout으로 합니다.

그리고 HTML 란에서 Table을 끌어다 놓고 행/열을 조정하면서, Web Forms 란에서 Label 네개, HyperLink 네개를 끌어다 놓습니다. 속성은 다음 처럼 바꿉니다.

컨트롤 (ID) Text NavigateUrl 설명
Label lblWriter (공백) (입력안함) 글쓴이
Label lblWriteDate (공백) (입력안함) 글쓴일자/조회수
Label lblTitle (공백) (입력안함) 글제목
Label lblContent (공백) (입력안함) 글내용
HyperLink lnkPrev (공백) (입력안함) 이전글 링크
HyperLink lnkNext (공백) (입력안함) 다음글 링크
HyperLink lnkModify [수정] (입력안함) 글수정 링크
HyperLink lnkDelete [삭제] (입력안함) 글삭제 링크
HyperLink lnkList [목록으로] List.aspx 글목록으로 링크

표에서 빨간색 바탕으로 된 것이 입력해야 할 부분입니다. 다음 그림처럼 만듭니다.

디자인과 관련된 부분입니다만, lblContent 자리에서 오른쪽 클릭, 속성 선택해서 '<TD> 속성페이지' 창에서 크기란 높이를 300으로 주고, 맞춤란 세로를 top 으로 주면 그림처럼 내용 표시하는 란이 넓어집니다. ( 물론, HTML 모드에서 해당 란의 <td> 태그 찾아서 <td valign=top height=300>이라 입력해도 됩니다 ) 이렇게 하면 글이 몇줄 안되도 넓게 나와서 저는 좋더군요.


DB관련 개체 설정

이것으로 글쓰기 페이지, 화면에 보이기 위해 필요한 컨트롤들은 모두 갖춘 셈이고, DB에서 해당 글을 가져올 DB관련 개체를 설정할 차례입니다. 도구상자 데이터 란에서 SqlConnection 하나와 SqlCommand 네개를 끌어다 놓습니다.

SqlConnection은 DB연결을 위한 객체입니다. List.aspx 경우와 똑같이 설정합니다. (Name) 을 dbConnection으로 하고, Dynamic Properties 란 확장, ConnectionString 란의 [...] 버튼을 클릭, '동적 속성' 창에서 '구성 파일의 키에 속성 매핑' 체크박스를 선택해서 dbConnection.ConnectionString 키에 매핑하고, '데이터'란의 ConnectionString에 값이 자동으로 들어가는지 확인합니다.

SqlCommand 는 글 내용을 가져오기 위한것, 글의 조회수를 하나 증가시키기 위한 것, 이전글/다음글 정보를 가져오는 것 등입니다. 네개 모두 설정하는 방법은 동일하고, SQL문 ( CommandText ) 부분만 다릅니다.

첫번째 SqlCommand 는 테이블에서 해당 글의 내용을 가져오기 위한 것입니다. 다음처럼 설정합니다.

  • (Name) : dbCommandGetArticle
  • Connection : dbConnection
  • CommandText :
      select writer,email,title,mode,content,readed, writeDate
      from cstVSBoard
      where seq=@seq
  • Parameters : 하나 추가
    ParameterName :@seq
    SqlDbType : int

두번째 SqlCommand 는 글의 조회수를 하나 증가시키기 위한 것입니다.

  • (Name) : dbCommandUpdateReadCount
  • Connection : dbConnection
  • CommandText :
    update cstVSBoard set readed=readed+1 where seq=@seq
  • Parameters : 하나 추가
    ParameterName :@seq
    SqlDbType : int

세번째, 네번째 SqlCommand 는 이전글/다음글 링크를 위한 것입니다. 이전글 가져오는 것은 다음과 같습니다.

  • (Name) : dbCommandGetPrevArticle
  • Connection : dbConnection
  • CommandText :
    select top 1 seq, title from cstVSBoard 
    where seq>@seq order by seq asc
  • Parameters : 하나 추가
    ParameterName :@seq
    SqlDbType : int

다음글 가져오는 것은 다음과 같습니다.

  • (Name) : dbCommandGetNextArticle
  • Connection : dbConnection
  • CommandText :
    select top 1 seq, title from cstVSBoard 
    where seq<@seq order by seq desc
  • Parameters : 하나 추가
    ParameterName :@seq
    SqlDbType : int

DB관련 객체들도 설정이 끝났습니다.


소스 편집

이제 지금껏 설정한 객체들을 이용해서 자료를 가져오고, 자료를 화면에 출력하는 부분을 작성할 차례입니다. 코드 보기로 전환해서, View.aspx.cs 파일 메인 클래스( 그대로 따라했다면 클래스 이름이 View 일겁니다 )에 다음처럼 소스를 메서드를 추가합니다. Page_Load 경우엔 기존에 정의되어있는 부분에만 소스를 넣습니다. 소스 첫부분, using  란에는 이번에 추가할 소스에서 필요한 네임스페이스인 System.Data.SqlClient와 System.Text.RegularExpression을 추가합니다.  아래 소스의 파란색 부분입니다.

using System;
...(생략)...
using System.Data.SqlClient;
using System.Text.RegularExpressions;

namespace MessageBoard
{
...(생략)...
  public class View : System.Web.UI.Page
  {
    ...(생략)...
    
    string LinkURL(string str,string target)
    {
      Regex rex = new Regex("http\\:\\/\\/\\S+");
      return rex.Replace(str,"<a href='$0' target='"+target+"'>$0</a>");
    }

    string LinkEmail(string str)
    {
      Regex rex=new Regex("\\S+\\@\\S+");
      return rex.Replace(str,"<a href='mailto:$0'>$0</a>");
    }

    string RemoveInvalidTags( string src, params string[] tags )
    {
      string expr="";
      for( int i=0; i<tags.Length; i++) 
      {
        expr+="|"+tags[i];
      }
      expr=expr.Substring(1);
      expr="(\\<\\/?\\s*)("+expr+")([^\\>]*\\>)";
      Regex rex = new Regex(expr);
      return rex.Replace( src,"$1_$2$3");
    }

    void Die(string msg) 
    {
      Response.Write(
        "<script language='javascript'>"+
        "alert('"+msg+"');history.back();"+
        "</script>");
      Response.End();
    }

    private void Page_Load(object sender, System.EventArgs e)
    {
      int seq = 0;
      String errMsg = "";
      try 
      {
        seq=int.Parse(Request.QueryString["n"]);
      }
      catch(Exception ) 
      {
      }
      if(seq==0)
        errMsg = "잘못된 글번호입니다";
      else 
      {
        dbConnection.Open();
        dbCommandGetArticle.Parameters["@seq"].Value=seq;
        SqlDataReader drArticle = 
          dbCommandGetArticle.ExecuteReader(CommandBehavior.SingleRow);
        if(drArticle.Read()) 
        {
          string writer = Server.HtmlEncode((string)drArticle["writer"]);
          string email = Server.HtmlEncode((string)drArticle["email"]);
          string title = Server.HtmlEncode((string)drArticle["title"]);
          int mode = (byte)drArticle["mode"];
          int readed = (int)drArticle["readed"];
          string writeDate = 
            String.Format("{0:yyyy년 M월 d일 H시 m분}",
                            (DateTime)drArticle["writeDate"]);
          string content = (string)drArticle["content"];
          drArticle.Close();

          if(email!="")
            writer=String.Format("<a href='mailto:{0}'>{1}</a>", 
                                  email, writer);
          lblWriter.Text="작성자 : "+writer;
          lblTitle.Text="<b>"+title+"</b>";
          lblWriteDate.Text="작성일 : "+writeDate+", 조회수 : "+readed;

          if(mode==0) {
            content=Server.HtmlEncode(content);
            content=content.Replace("  ","&nbsp; ");
            content=LinkEmail( content );
            content=LinkURL( content, "_blank" );
          }
          else 
          {
            content=RemoveInvalidTags( content, "xmp", "table", "tr", "td" );
            content="<!--\"'>>_<<'\"-->"+content+"<!--\"'>>_<<'\"-->";
          }
          content=content.Replace("\n","\n<br>");

          lblContent.Text=content;

          // 조회수 증가
          dbCommandUpdateReadCount.Parameters["@seq"].Value=seq;
          dbCommandUpdateReadCount.ExecuteNonQuery();

          // 이전글
          dbCommandGetPrevArticle.Parameters["@seq"].Value=seq;
          SqlDataReader drPrevArticle = 
            dbCommandGetPrevArticle.ExecuteReader(CommandBehavior.SingleRow);
          if(drPrevArticle.Read()) 
          {
            lnkPrev.NavigateUrl = "View.aspx?n="+drPrevArticle["seq"];
            lnkPrev.Text = Server.HtmlEncode((string)drPrevArticle["title"]);
          }
          else
            lnkPrev.Visible=false;
          drPrevArticle.Close();

          // 다음글
          dbCommandGetNextArticle.Parameters["@seq"].Value=seq;
          SqlDataReader drNextArticle = 
            dbCommandGetNextArticle.ExecuteReader(CommandBehavior.SingleRow);
          if(drNextArticle.Read()) 
          {
            lnkNext.NavigateUrl = "View.aspx?n="+drNextArticle["seq"];
            lnkNext.Text = Server.HtmlEncode((string)drNextArticle["title"]);
          }
          else
            lnkNext.Visible=false;
          drNextArticle.Close();

          // 수정, 삭제 링크

          lnkModify.NavigateUrl="Write.aspx?m=1&n="+seq;
          lnkDelete.NavigateUrl="Delete.aspx?n="+seq;
        }
        else 
        {
          errMsg="없거나 삭제된 글입니다";
        }
      }
      dbConnection.Close();
      if(errMsg!="")
        Die(errMsg);
    }

    ...(생략)...
  }
}
DB 객체가 정의 되어있으니, 이 소스 부분은 여러분들도 나름대로의 방식으로 하실수 있을듯 합니다. 위 소스는, 어지간한 상황에서도(?) 잘 작동하도록 하기 위해 약간은 복잡하게 되어있습니다. 게시판은 사용자가 직접 입력하는 부분이기 때문에, 아무렇게나 입력하는 경우에도 에러 없이 잘 작동해야 할겁니다. 특히 HTML 태그도 허용하도록 할때면 주의가 필요합니다.

우선, 앞부분에 정의된 네개의 메서드에 대해 설명드리겠습니다.

  • LinkUrl : 문자열과, 타겟 윈도우 이름을 주면 그 문자열 내의 url 형태의 단어 ( http://cassatt.pe.kr 처럼 http://가 앞에 붙는 단어 )에 자동으로 링크를 겁니다.
  • LinkEmail : 문자열 내에 cassatt@hanmir.com 처럼 중간에 @기호가 들어간 단어가 있으면, 그것을 메일주소로 인식해서 자동으로 링크를 겁니다.
  • RemoveInvalidTags : 문자열내에 파라메터로 준 태그들이 있으면, 태그 앞에 언더라인(_)을 붙여서 태그가 실행되지 못하도록 합니다. 예를 들어 <table> 경우라면, table 앞에 _ 가 붙어서 <_table> 처럼 바뀌어 태그가 실행이 안됩니다. 소스에선 <xmp>, <table>, <tr>, <td> 같은 태그들이 실행되지 않도록 했습니다.
  • Die : 주어진 문자열로 메시지박스를 띄우고, 이전 페이지로 돌아가는 자바스크립트를 출력한 후 실행을 마칩니다(Response.End()). 에러처리용입니다.

Page_Load 이벤트의 경우엔, 우선 앞 강좌의 List.aspx에서 제목을 클릭하면 View.aspx?n=10 처럼 글의 seq 필드 값을 n 이란 파라메터로, GET 방식으로 호출하도록 했었습니다. 그래서, 그 값을 알아내서, dbCommandGetArticles 를 호출해서 SqlDataReader로 한줄 읽어들입니다.

가져온 값을 앞에서 정의했던 lblWriter, lblWriteDate, lblTitle, lblContent 같은 라벨 컨트롤 Text 값에 넣어주면 표현되겠는데요, 그전에 약간씩 변형이 필요합니다. 메일주소를 입력했다면 글쓴이 이름에 메일주소를 링크해야 하고요, 입력일이라면 원하는 포맷의 날짜 형태로 바꿔줘야 하고, 제목란이라면 Server.HtmlEncode 를 이용해서 HTML 태그가 먹지 않도록 해야 합니다.
글의 내용이라면, 글쓰기(Write.aspx)란에서 글이 TEXT 형식인지, HTML 태그를 허용하는지 선택할수 있도록 했었는데요, 그에 맞추어 바꿔야 합니다.
TEXT 형식이라면, Server.HtmlEncode로 태그가 실행되지 않도록 하고, 공백 두개("  ") 를 &nbsp; + 공백 하나 로 바꿔서, 글 앞에 공백을 입력해도 표시가 잘 되도록 합니다. 그리고 글 내용중에 url 이나 email 주소가 있으면 링크를 겁니다.
HTML 형식이라면, 태그가 실행되어야 하므로 특별한 처리는 하지 않지만 앞서 정의했던 RemoveInvalidTags 메서드로 요주의(?) 태그들은 먹지 않도록 합니다. ( 특히 xmp 같은 태그가 먹으면 다 망가집니다 ) 그리고 내용 앞뒤에  <!--"'>>_<<'"--> 를 붙이면 사용자가 입력한 글의 링크가 깨져있는 경우 ( 예를 들어 <a href="....> 처럼 따옴표를 닫지 않은 경우 )에도 어느 정도는 막아줍니다. CrazyWWWBoard가 그렇게 하더군요.
그리고 두 형식 모두, 리턴 코드("\n") 를 <br> 태그로 바꿔서 행바꿈 되도록 합니다.

이후엔 글의 조회수를 하나 증가시키고, 이전글/다음글을 가져와서 표시하고, 글수정, 글 삭제 링크에 url을 넣는 등의 작업을 합니다.

약간은 복잡한 소스였나 모르겠습니다. 실제로 게시판을 운영하시다 보면 다양한 경우를 만나게 될겁니다. 소스중 일부를 빼고, 넣고 하시면서 아무렇게나 입력하면서 테스트 해보세요.

Page_Load 메서드, 뒷부분 보시면, 수정/삭제 링크의 URL로 설정하는 것을 보실수 있습니다.

lnkModify.NavigateUrl="Write.aspx?m=1&n="+seq;
lnkDelete.NavigateUrl="Delete.aspx?n="+seq;

글 수정의 경우, 맨 처음 만들었던 Write.aspx 에 기능을 추가해서 구현할 예정입니다. 글 수정이나, 글쓰기나 거의 같은 UI 라서요.
그리고 삭제 버튼을 누르면 Delete.aspx 에 seq 값을 넘겨, 그 글을 삭제하도록 하고 있습니다. Delete.aspx 에선 비밀번호를 입력받아서, 해당 글의 비밀번호와 비교해서 삭제하면 되겠습니다. 다음 강좌에선 그 글 삭제 페이지( Delete.aspx )를 만들어보겠습니다.


authored by


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

로딩 중입니다...

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