2008. 8. 14. 15:46

게시판 답글 달기 정의

답글은 게시판 구조 중에서 가장 복잡한 부분이다. 그것은 답글이라는 형식이 관계형 데이터베이스 가지는 형태에서 다소 벗어나는 부분이 있기 때문이다. 이러한 답글의 형식을 살펴보고 그 구현방법을 연구해 보도록 하자.

우선 답글이라는 형식이 어떻게 사용되는지 살펴보자. 게시판을 써본 사람은 알겠지만 답글은 언뜻 보기에 트리 구조처럼 보인다. 즉 하나의 글이 있으면 그 글에 대해서 답글을 달고 답글은 또 다른 다수의 답글을 가질 수 있기 때문이다. 이러한 트리 구조는 관게형 데이터베이스에서 구현하기는 매우 까다로운 것이다. 관계형 데이터베이스는 순차형 구조를 그 기본으로 하고 있기 때문이다.

그렇다면 정말 게시판은 트리 구조일까? 그렇지도 않다. 왜냐하면 게시판은 페이지를 나눠서 게시물을 보여줄 수 있어야 하기 때문이다. 이 페이지를 나눌 때 트리구조를 고려하지는 않으며 단순히 게시물의 숫자를 세어서 페이지당 20개가 나와야 한다면 답글의 중간에서도 얼마든지 자를 수 있다. 트리 구조는 이러한 형태로 트리의 일부를 자르는 경우는 없다.

결국 게시판은 트리구조가 아니다. 순차형 구조에 약간의 변형이 있을 뿐이다. 어떤 변형인가? 우선 게시물이 표시되는 순서상의 문제다. 답글이 아닌 시작 글의 경우에는 통상 시간의 역순으로 표시되는데 답글의 경우에는 나중에 작성된 글임에도 불구하고 그 부모 글 보다 늦게 나타나게 된다.

위의 그림에서 “두번째 글의 답글”은 두번째 글보다 당연히 나중에 작성되었지만 두번째 글보다 아래쪽에 나오는 것이다. 즉 게시판의 게시물의 작성시간 순으로 표시되는 것이 아니라 뭔가 다른 순서가 필요한 것이다. 즉 사용자에게 보여지는 글 번호 외에 게시물 표현 순서를 나타내는 번호가 하나 더 필요한 것이다.

위의 그림은 내부적으로 사용되는 게시물의 보여지는 순서를 표시하는 글 번호도 같이 표시한 그림이다. 체크박스 바로 다음의 번호가 보여지는 순서를 나타내는 번호다. 여기에 두 번째 줄의 글(RE:두번째 글의 세번째 답글입니다.)에 답글을 달아서 어떻게 변하는지 살펴보자.

글 번호 7번이 들어가면서 내부번호 1998번부터 1996번까지의 번호를 하나씩 줄이고 글 번호 7번이 내부번호 1998번으로 들어갔다. 즉 답글이 추가되면 그 자리 이전의 게시물의 내부번호를 모두 하나씩 줄여줘야 하는 것이다. 그렇다면 만약 게시물이 100만개가 있는데 어떤 사용자가 맨 처음의 게시물에 답글을 달면 어떻게 될 것인가? 100만개의 게시물 전체의 내부번호를 하나씩 줄여주는 작업을 해야 한다. 이것은 데이터베이스에 상당한 무리를 줄 수 있다. 그래서 위의 그림을 잘 보면 알 수 있지만 답글이 아닌 글의 경우에는 내부 번호가 순차적으로 증가하지 않고 10000단위로 증가하는 것을 볼 수 있다. 즉 답글이 생길 것을 생각하여 미리 숫자를 비워두는 것이다. 이렇게 하면 답글이 추가 되어도 그 답글들의 그룹 내에서만 내부번호를 갱신하게 되므로 훨씬 효율적으로 답글을 추가할 수 있게 된다. 단 하나의 약점은 한 게시물에 대하여 추가할 수 있는 답글의 총수가 9999개를 넘을 수 없다는 것이다. 그러나 현실적으로 그렇게 많은 답글이 만들어지는 경우는 없다고 보아야 할 것이다.

본론으로 들어가서 구현을 해보자. 우선 데이터베이스에 몇 개 행을 추가해야 한다.

CREATE TABLE [dbo].[DNBBSPost] (
 [PostSystemID] [int] IDENTITY (1, 1) NOT NULL ,
 [BBSID] [int] NOT NULL ,
 [PostNo] [int] NOT NULL ,
 [Subject] [nvarchar] (300) COLLATE Korean_Wansung_CI_AS NOT NULL ,
 [BodyText] [ntext] COLLATE Korean_Wansung_CI_AS NULL ,
 [CreatorID] [int] NOT NULL ,
 [CreatorName] [nvarchar] (50) COLLATE Korean_Wansung_CI_AS NOT NULL ,
 [CreateDate] [datetime] NOT NULL ,
 [SysPostNo] [int] NOT NULL ,
 [PostIndent] [int] NOT NULL

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

굵게 된 부분이 추가된 부분이다. SysPostNo가 게시물을 보여주는 순서를 결정하는 내부번호이고 PostIndent는 작성된 답글의 깊이를 결정하는 번호다. 즉 첫 번째 답글은 1 답글의 답글은 2,,, 이런 식이다. 이것은 게시물을 표시할 때 들여쓰기를 하기 위해서 필요하다.

               private void lbSavePostReply_Click(object sender, EventArgs e)

               {

                       PostController pc = new PostController();

 

                       PostInfo piParent = pc.Get((int)Session["CPostSystemID"]);

 

                       PostInfo pi = new PostInfo();

 

                       pi.Subject = tbSubjectReply.Text;

                       pi.BodyText = tbBodyTextReply.Text;

 

                       pi.CreatorID = 1;

                       pi.CreatorName = "Tester";

                       pi.BBSID = 1;

                       pi.PostIndent = piParent.PostIndent + 1;

 

                       BBSController bc = new BBSController();

                       BBSInfo bi = bc.Get(pi.BBSID);

                       pi.PostNo = ++bi.LastPostNo;

                       pi.SysPostNo = pc.MakeRoomForReplyPost(piParent);

 

                       bc.Update(bi);

                       pc.Add(pi);

 

                       tbSubjectReply.Text = "";

                       tbBodyTextReply.Text = "";

 

                       Response.Redirect(Request.RawUrl);

               }

 

               public int MakeRoomForReplyPost(PostInfo pi)

               {

                       SqlConnection myConnection = new SqlConnection(myConnString);

                       myConnection.Open();

 

                       string myQuery = string.Format(

                              "update DNBBSPost set SysPostNo=SysPostNo-1" +

                              "where SysPostNo<{0} and SysPostNo>{1}", pi.SysPostNo, ((pi.SysPostNo - 1) / 10000 * 10000));

                       SqlCommand myCommand = new SqlCommand(myQuery, myConnection);

                       int nUpdateRow = myCommand.ExecuteNonQuery();

 

                       int newSysPostNo = pi.SysPostNo - 1;

                       if (nUpdateRow == 0)

                       {

                              myQuery =

                                      string.Format("select MIN(SysPostNo) from DNBBSPost " +

                                      "where SysPostNo<={0} and SysPostNo>{1}", pi.SysPostNo, ((pi.SysPostNo - 1) / 10000 * 10000));

 

                              myCommand = new SqlCommand(myQuery, myConnection);

                              SqlDataReader dr = myCommand.ExecuteReader();

                              int minSysPostNo = pi.SysPostNo;

                              if (dr.Read())

                              {

                                      minSysPostNo = dr.GetInt32(0);

                              }

                              dr.Close();

                              newSysPostNo = minSysPostNo - 1;

                       }

 

                       myConnection.Close();

 

                       return newSysPostNo;

               }

MakeRoomForReplyPost에서 결정적인 일을 한다. 코드는 간단하므로 이해하는데 별 어려움이 없을 것이다. 앞에서 설명한 답글을 달기 위해서 내부번호를 하나씩 빼주는 처리를 하는 부분이다.

지난번 아티클 이후 사소한 UI가 변경된 것이 있는데 게시물을 지우는 부분이다. 각 게시물에 링크버튼을 달아서 버튼을 누르면 지우는 방식에서 각 게시물에 체크박스를 달고 체크된 게시물을 일괄 지우는 방식으로 변경하였다. 이것은 일반적인 게시물 UI를 따르기 위해서 이며 게시물을 표시할 때 “삭제”라는 말이 줄줄이 나오는 지저분함을 없애는 효과도 있다. 이를 위하여 다음 함수가 추가되었다.

삭제 버튼을 누르면 실행되는 루틴인데 PostList DataGrid의 각 Item을 돌면서 체크박스에 체크가 된 경우에는 삭제하는 것이다.  PostSystemID를 구하는 방법도 약간 변경이 되었는데 이전에 Item의 컨트롤 행렬에서 가져오던 방식에서 FindControl을 사용하는 방법으로 바꾸었다. 이는 UI변경 시 코드를 바꾸지 않기 위해서 이다. 알아두면 쓸모가 많은 테크닉이므로 유심히 봐두기 바란다.

'My work space > Java' 카테고리의 다른 글

HashTable & HashMap 설명  (0) 2008.08.14
상하위 클래스에서 생성자간의 관계  (0) 2008.08.14
JVM의메모리구조  (0) 2008.08.14
오라클DB에서 페이지 나누기  (0) 2008.08.14
게시판 답글 방법 정리  (0) 2008.08.14