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

강좌 목록으로 돌아가기

필자의 잡담~

이제 테이블을 한번 설계해 볼까요? ^^

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

이번에는 말입니다. 계층형 게시판을 위한 테이블을 설계해 볼까 합니다..

뭐 단일 테이블이니 설계라는 거창한 단어를 사용할 필요까지도 없겠습니다만... 뭔가 스케일 크게 인생을 살아보는게 희망이기에 단어도 스케일 크게 사용해 보았습니다. 하핫핫~ 

1. 테이블 구조
(이 부분은 문제점이 제기되어, 2003년 8월 25일 수정하였습니다)

테이블 구조는 이전 강좌에서 설명했다시피, thread 라는 컬럼을 계층형을 위한 컬럼으로 사용합니다. 그리고, 또한 각 글들이 몇번째 답변인지(이후 뎁쓰-depth-라고 부르겠슴다)를 저장해 두는 컬럼으로 depth 라는 컬럼도 사용합니다. 이러한 2개의 컬럼을 제외하면 사실 계층형 게시판의 구조는 일반 게시판과 다를 것이 전혀 없습니다. 계층형 게시판이라는 것은 일반 게시판이 계층적으로 보여진다는 것을 제외하면 사실 다를 것이 없으니까요 ^^; 해서, 그렇게 Thread와 Depth 컬럼이 추가된 계층형 게시판을 위한 테이블 구조는 다음과 같게 됩니다. (참고로, 다음 테이블 구조는 MS SQL 서버를 기준으로 작성된 것입니다. 다른 데이터베이스는 그에 맞게 적절히 약간만 바꾸시면 됩니다.)

필드이름형식길이설명기본값
seqint(숫자)키 값(기본키) 
threadint(숫자)글번호 
depthint(숫자)답변위치 
writervarchar(문자열)20글쓴이 
pwdvarchar(문자열)20비밀번호 
emailvarchar(문자열)100메일주소 
titlevarchar(문자열)100제목 
modebit글형식(0-text, 1-html) 
ipvarchar(문자열)15아이피 
readcountint조회수(0)
transdatedatetime입력한 날짜getdate()
contenttext내용 

기본 키 컬럼은  seq 컬럼으로 지정하도록 합니다. Thread 컬럼으로 각각의 레코드를 구분할 수 있다고 하여도, 그 값 자체는 변경될 수 있는 값이기에, 각각의 레코드를 고유하게 나타내는 키 컬럼은 반드시 필요하죠!! 해서, Thread 컬럼과는 별도로 각각의 레코드를 고유하게 나타내는 seq 컬럼을 둔 것입니다. seq 컬럼이 필요한 경우는 오직 content.aspx 에서 정도이겠네요. 사용자가 해당한 바로 그 글을 선택해서 보여줄 경우 말입니다. 그 외에는 거의 thread 컬럼을 사용하겠죠???  ^^ 

기본 키를 thread로 잡는 것이 어떠냐는 질문이 있을 수도 있는데요. 그 부분은 차후에 시간을 두고 논의해 보도록 하겠습니다. 누누히 강조드리지만, 더 나은 방향은 얼마든지 있을 수 있습니다. 그리고, 실제로 차후에는 그러한 부분들을 개선해 나가야 할 것입니다. 버뜨, 지금은 이렇게 진행하도록 하겠습니다. ^^  (강좌에서는 thread에 인덱스를 거는 것으로 진행할 예정입니다)

그리고,조회수 컬럼인 readcount 컬럼의 기본 값을 0 으로 주도록 하세요. 새 글이 입력될경우, 기본적으로 그 컬럼에 값이 들어오지 않아도 기본적으로 글의 조회수가 0에서부터 시작하도록 하기 위해서말입니다. 그리고, 글이 입력되는 날짜인 transdate 컬럼에도 기본값을 주도록 하는데요. 그 기본 값으로는 getdate()를 주도록 합니다 그렇게하면, 그 컬럼에 지정된 값이 없이 insert 구문이 실행되더라도 기본값으로 현재 일/시(즉, 글쓴 시간)가 입력될 것입니다. 이것은MS SQL 서버를 사용하기에 getdate()로 지정한 것인데요. getdate() 라는 것이 MS SQL 서버 자체에 내장되어져 있는 함수로써 현재의시간을 반환하는 역할을 하기 때문입니다. 다시 말해, SQL 서버에서는 getdate()가 현재의 날짜와 시간을 의미하는 함수라고 보시면 됩니다.

그렇게 모두 설정했다면, 테이블의 구조는 다음과 같을 것입니다. (그림에서는 기본값도 윈도우에 나타나게 한 상태로 캡춰해 보았습니다)

그리고, 이것을 스크립트로써 작성한다면 다음과 같습죠. 만일, 여러분이 테이블을 직접 구성하기가 귀찮으시다면 다음 스크립트를 그대로 카피해서, 쿼리 분석기에 붙여넣고 실행하시기만 하면 됩니다. 그럼, 지정된 데이터베이스에 ThreadBoard 테이블이 자동으로 만들어질 겁니다. (데이터베이스는별도로 만드시거나, 아니면 기존에 사용하시던 데이터베이스를 이용하셔도 됩니다. 참고로, 저는 TEST라는 데이터베이스를 만들어서 그것을 이용하고 있습니다)

자... 이제 게시판을 위한 테이블은 준비가 되었죠? ^^
(사실, 이 테이블은 차후 게시판의 정책(?)에 따라 몇몇 컬럼이 더 추가될 수도 있습니다. ^^. 예를 들면, 게시물을 html 태그가 적용된 상태로 나타낼 수 있게 한다거나, 답변이 달려져 있는 질문 글이 지워질 경우는 실제로 그 글을 지우는 것이 아니라 삭제된 글인 것처럼 표시하여 출력하도록 하고자 한다거나 할 경우 말입니다. 이 부분은 나중에 별도로 언급해 보는 시간을 갖도록 하겠습니다)

아참. thread 컬럼에 인덱스를 걸어주는 것을 빼 먹을 뻔 했네요. 그 부분은 성능향상을 위해서 반드시 필요합니다. 고로, 테이블 작성이 끝나면다음과 같이 thread 컬럼에 인덱스를 잡아주도록 하세요 ^^

CREATE UNIQUE INDEX uix_thread ON ThreadBoard( thread )
GO

그렇다면, 이제 실질적인 게시판의 제작에 앞서 몇가지를 고민해 보도록 하겠습니다.

이미 앞에서도 언급했다시피, 계층형 게시판이라는 것은 단지 글이 계층적으로 출력된다는 것을 제외하면, 일반 게시판과 다를 것이 전혀 없습니다. 해서, 우리의 테이블 디자인도 계층적인 구조를 만들기 위해 필요한 2개의 컬럼(thread, depth)을 제외하면 기존의 일반 게시판과 다를 바가 없지요.

자. 그렇다면, 이제 테이블도 준비가 되었겠다. 프로그래밍에 들어가기에 앞서 사용할 쿼리들을 점검해 보도록 합시다. 가장 기본적으로 필요한 쿼리가 있다면 그것은 바로 데이터를 입력하는 쿼리와 모든 데이터들을 계층적으로 불러오는 쿼리이겠지요? 한번에 하나씩 멋드러지게 한번 같이 고민해 나가 봅시다. 음핫핫

먼저, 데이터를 입력하는 쿼리입니다. 저는 다음과 같이 2개의 Insert 문장을 작성해 보았습니다.

INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (1000, 0, 'admin', 'pwd', '첫번 째 글입니다', 1, '본문 내용입니다')

INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (2000, 0, 'admin', 'pwd', '두번 째 글입니다', 1, '본문 내용입니다')

그렇습니다. ThreadBoard 테이블의 thread, depth, writer, pwd, title, mode, content 컬럼에다가 데이터를 입력하는 것이지요. 누누히 강조하지만, 이 컬럼 중 thread와 depth 컬럼을 제외하면 사실 일반 게시판에서 사용하는 Insert  문과 다를 것이 없습니다.(정말 끈질기게 강조하죠? -_-;)

일단, thread 컬럼의 값을 1000 단위로 값을 부여해 보았는데요. 그러니깐, 다시 말하면 첫번째로 올라오는 질문 글은 thread 값으로 1000 을 무조건 지정하구요. 두번째로 올라오는 질문 글은 thread 값을 무조건 2000 으로, 세번째 올라오는 질문 글은 thread 값을 무조오오오~건 3000 으로 지정한다는 겁니다. 이 말은 하나의 질문에 대해서 최대 1000 개까지의 답변이 가능하도록 제한을 하는 것이죠. 1000개라... 왠지 적어보이나요? 절대 그렇지 않습니다. 100개로 제한해도 문제가 거의 없습니다. 그 정도로 답변이 달리는 글은 어디서도 아직 본적이 없거든요. ^^. 어쨌거나, 일단 위의 쿼리에서는 두 개의 질문 글만을 넣어보았는데요. 질문글은 depth가 무조건 0 이니깐 두 레코드 모두 depth 컬럼의 값을 0 으로 준 것이랍니다. ^^

이 상태에서 만일 두번째 글에 답변이 하나 달린다! 하면 그 레코드는 어떻게 값을 주면 되느냐? 그것은 저번 강좌에서 설명드렸듯이, 자기가 답변으로 달릴 레코드의 thread 값보다 하나 작은 값을 주면 되는 것이죠. 예를 들면, 다음과 같이 말입니다.

INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (1999, 1, 'admin', 'pwd', '두번 째 글의 답변입니다', 1, '본문 내용입니다')

꾸어리(쿼리)를 보시면 알겠지만, depth에 대해서는 자신이 답변달릴 원본 글의 depth 값에 +1 을 한 값으로 지정하고 있습니다. 이거는 반드시 그래야만 하는 눌(rule) 입니다. 발음 잘 안되지만, 그래도 눌입니다. 누~~울!

자. 쿼리가 준비되었으니 이제 한번 이 쿼리들을 [쿼리 분석기]를 통해서 한번 실제로 입력하고 결과를 보도록 하시죠. 다음과 같이 말입니다.

자. 그리고 이제 현재까지 들어있는 데이터를 검사해 보면 다음과 같이 들어가 있는 것을 볼 수 있을 겁니다.

데이터는 계층적인 구조와는 무관하게, 순차적으로 쌓여들어갈 것입니다. ^^. 이제 우리가 해야 하는 일은 그 데이터들을 계층적인 순서에 맞게 가져오는 SELECT 쿼리를 만드는 일인데요. 사실, 그 쿼리는 의외로 간단합니다. 단지, 다음과 같이 Thread 컬럼의 역순으로 정렬해서 가져오기만 하면 되니까요

SELECT * FROM ThreadBoard
ORDER BY Thread DESC

다음은 이 쿼리로 결과를 보았을 경우의 모습입니다. 순서가 계층 구조와 맞게 나열되고 있죠?(아!~ 물론, 지금은 답변 글이 안으로 들여써져서 나타나고 있지는 않습니다만, 나중에 ASP로 출력할 때는 그렇게 보이게 할 겁니다. ^^.. 지금은 출력순서만 살펴보도록 하세요)

이렇게 간단할 수 있는 이유는 우리가 새로운 레코드를 넣을 때, 미리 계층적인 구조에 맞게 Thread 값을 구해서 넣었기 때문입니다. 사실, 위에서는 질문 글에 하나의 답변을 달았기 때문에 INSERT 하는 쿼리가 그리 어렵지 않았지만 말입니다. 실제로는 그 부분이 단순하게 하나의  INSERT로 끝날 수 있는 부분이 아닙니다. 최소한 한번의 Update 와 한번의 INSERT 가 필요하죠. 해서, 사실 이번 계층형 게시판을 위해서 가장 중요한 쿼리는 새로운 글이나 답변 글을 Insert 하기 위한 쿼리가 됩니다. 그렇다면, 이제부터 그 부분을 더욱 깊에 파고 들어가 보도록 하겠습니다.

먼저, 이 로직의 또 하나의 룰을 알려드리면요. 최근에 오는 답변은 원래의 답변 트리에서 위쪽으로 붙는다는 겁니다. 즉, 현재의 상태에서. 이미 답변이 있는 두번째 글에 또 다른 답변이 붙는다면, 그 답변이 두번째 질문의 트리에서 가장 밑에 놓이는 것이 아니라 가장 위에 놓여야 한다는 것입니다. 다음 그림처럼 말입니다.

이렇게 되어야 한다고 제가 말씀을 드렸더니, 참으로 많은 항의 엽서가 왔더군요 ^^

"에???... 왜 그래야만 되죠? 누구 맘대로 그렇게 하는 겁니까?"           - 최은아(여) 21세, 강남
"첫번째 그림처럼 나와야 하는게 정상이 아니냐? 너 정상 아니지~~"    -김가람(남) 34세, 역삼
"난 이 게시판의 로직이 맘에 들지 않습니다. 바꿔주세요"                   - 이수천(남) 22세, M대학
"중 3이 몰 알겠어. 게시판 좀 만들어 볼라구 하니 좀 알려주쇼"           - 강해종(남) 15세, S중학

그래서, 한 말씀을 올리면 말입니다. 여러분의 기대대로 첫번째 그림처럼 출력되도록 할 수 없는 것은 아닙니다. 물론, 그렇게 하는 것이 가능하기는 하지요(디게 어려울 것 같다는 예상은 듭니다). 하지만, 이 즈음에서 묻고 싶네요. 왜 꼭 그렇게 출력이 되어야 하죠? 그렇게 출력되지 않으면 혹시 사용하기에 불편하다던가, 혼란스러운가요? 이전의 크레이지 보드나 여러 대형 커뮤니티의 게시판들도 이러한 계층구조를 사용하고 있었는데 말입니다.

첫번째 그림처럼 출력되도록 구현하는 것이 불가능한 것은 아닙니다만, 그렇게 할 경우 상당히 많은 데이터베이스 부하가 발생하구요. 로직도 상당히 복잡해지게 됩니다. 해서, 그러한 구현은 하지 않으려 합니다. 서버의 성능을 희생해가면서까지 그러한 기능을 구현해야 하는가?라는 질문에 대한 답을 아무리 생각해도 얻을 수가 없어서요 ^^. (해서, 제 사이트 게시판도 이러한 계층구조랍니다)

자. 그러므로, 우리의 답변 계층형 게시판은 답변들이 달릴 경우, 동일한 Depth에 여러 답변이 달릴 경우는 최근에 올라온 답변보다 위쪽에 출력되는 Descending 형태가 된다는 사실을 기억하시기 바랍니다. 다 만들어보시고 나면 이 방법이 훨씬 더 엘레강스하고, 퍼뽀몬쓰에서 좋다는 사실을 알게 되실 겁니다. ^^

자. 그렇다면, 이제 위의 그림처럼 thread 번호가 2000 인 "두번 째 글입니다" 밑에 또 다른 답변('두번째 글의 답변2입니다')이 달린다면 어떻게 질의(Query)를 해야할까요?

그렇습니다. 이전 강좌에서 말씀드린 것처럼 일단 현재 자신이 답변을 달게될 원본 글의 thread 값보다 작은 thread 값들은 모두 -1을 해줘야 합니다. 그리고, 그를 위해서 필요한 Update 쿼리는 다음과 같구요

UPDATE ThreadBoard
SET thread = thread - 1
Where thread < 부모 글의 thread 값(예제의 경우, 2000)
          and thread > 이전 질문 글의 thread 값(우리의 경우, 1000)

이렇게 하면, 현재까지의 테이블의 정보는 다음과 같이 될 것이구요 ^^
(빨간색 부분이 쿼리에 의해 영향을 받은 레코드입니다)

threaddepthtitle
20000두번 째 글입니다
19981두번 째 글의 답변입니다
10000 첫번 째 글입니다

그 다음에, 현재의 답변 글은 자신이 답변을 달게 되는 글의 thread 값 -1을 해서 thread 값을 주면 되는 것입니다.(현재의 경우는 2000-1=1999 가 되겠죠?) 해서, 다음과 같은 쿼리를 사용해서 넣어주면 됩니다.

INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (1999, 1, 'admin', 'pwd', '두번 째 글의 답변2입니다', 1, '본문 내용')

그러면, 최종 결과는 다음과 같게 되죠 ^^
(빨간색 부분이 지금까지 영향을 받은 레코드들이며, 굵은 부분은 이번 쿼리로 INSERT된 레코드입니다.)

thread

depth

title

20000두번 째 글입니다
19991두번 째 글의 답변2입니다
19981두번 째 글의 답변입니다
10000 첫번 째 글입니다

여기서 thread 값이 1999 인 것은, 그 값이 부모 글의 thread 값에서 -1을 뺀 값이 되어야 하기 때문이구요. depth가 1인 것인 부모글의 depth가 0인데 그것에 +1을 했기 때문이랍니다. 즉, 이 쿼리의 핵심은...

INSERT INTO ThreadBoard (thread, depth, ... )
Values (부모 글의 thread - 1 , 부모글의 depth + 1, ... )

이 되는 것이죠.... 해서, 데이터를 INSERT 할 경우에 위와 같은 눌~을 지켜서 데이터를 입력하기만 한다면, 계층형 구조를 만들어내는 것은 그리 어렵지 않아집니다 ^^

기왕 한 김에 하나만 더 해볼까요?

이번에는 위의 예제에서, '두번 째 글의 답변2입니다'라는 글 밑에 답변을 하나 추가한다고 가정해 봅시다. 그러면, 우리의 눌(~) 대로라면 먼저 어떤 작업을 수행해야 하나요? 그~~ 렇습니다!!!  우선 현재의 부모글(즉, '두번 째 글의 답변2입니다'라는 글)의 thread 값보다 작은 thread 값은 모두 -1을 해주어야 합니다. 해서 다음과 같은 쿼리를 만들 수가 있겠죠?

UPDATE ThreadBoard
SET thread = thread - 1
Where thread < 부모 글의 thread 값(예제의 경우, 1999)
          and thread > 이전 질문 글의 thread 값(우리의 경우, 1000)

그러면, 이 Update 쿼리의 결과로는 다음과 같이 데이터가 변경될 것임을 기대할 수 있구요.

thread

depth

title

2000 0두번 째 글입니다
19991두번 째 글의 답변2입니다
1997 1두번 째 글의 답변입니다
1000 0첫번 째 글입니다

이어지는 다음과 같은 INSERT 구문에 의해서

INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (1998, 2, 'admin', 'pwd', '두번 째 글의 답변2에 대한 답변입니다', 1, '본문 내용')

최종 결과는 다음과 같이 될 것입니다.

thread

depth

title

2000 0두번 째 글입니다
19991두번 째 글의 답변2입니다
1998 2두번 째 글의 답변2에 대한 답변입니다
1997 1두번 째 글의 답변입니다
1000 0첫번 째 글입니다

어때요? 다 알고나니 사실 그렇게 어렵지도 않지요? 그럼 정리해 보도록 하겠습니다.

1. 입력되는 글이 질문 글인 경우

: 현재 데이터베이스에 존재하는 thread 값중 가장 큰 값에 +1000 을 한 값으로 Thread 값을 준다.
  depth는 무조건 0으로 준다.

  예) INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content) 
       Values (3000, 0, 'admin', 'pwd', '세번 째 글입니다', 1, '본문 내용입니다')

2. 입력되는 글이 답변 글인 경우

: 자신의 부모글의 thread 값보다 -1이 작고, 이전 질문 글의 thread 값 사이에 위치한 모든 글의
   thread 값을 -1 한다. 그리고 난 다음, 현재 입력되는 글의 thread 값을 부모글의 thread 값 -1 로
   지정한다. depth는 부모글의 depth 값에 +1한 값으로 준다.

  예) UPDATE ThreadBoard 
       SET thread = thread - 1
       Where thread < 부모 글의 thread 값 and thread > 이전 질문 글의 thread 값

       INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content) 
       Values (부모 글의 thread 값-1, 0, 'admin', 'pwd', '세번 째 글입니다', 1, '본문 내용입니다')

이 방법을 사용하면 하나의 글을 입력하기 위해서는 최대 999건의 데이터만을 UPDATE 하면 되기 때문에 부하가 예전에 사용했던 로직(전체를 업데이트했었던... -ㅛ-)보다는 훨씬 나을 것이구요. 게다가 사실상, 하나의 글에 답변이 달린다고 해봐야, 대부분 많아봐야 3-4개정도가 대부분이기에 실제 UPDATE는 3-4건 정도밖에 일어나지 않을 것이라 기대할 수 있기에, 그렇게 성능에 나쁘지도 않을 것입니다.

그렇다면, 게시물들의 목록을 불러올 경우는 어떻게 쿼리하면 될까요? 일단 가장 간단하게라면.. 다음과 같이만 하셔도 됩니다. (참고로, 이 쿼리는 페이징이 적용되지 않은 쿼리입니다. 차후, 페이징 강좌에서 그렇게 동작하도록 이 쿼리를 바꾸어 보도록 하겠습니다)

SELECT * FROM ThreadBoard
ORDER BY thread DESC

그리고, 다음 결과는 위의 SELECT 쿼리로 현재까지 들어있는 데이터를 확인한 결과입니다.

눈에 보이기에는 계층적이지 않지만, 출력순서는 계층적이라는 것에 주목하세요. 실제 이 데이터들을 웹 페이지에 출력할 경우에는 depth 값을 사용해서 들여쓰기를 할 것이기에, 그 때는 진짜 계층적으로 보이게 될 것입니다.

 

그럼, 다음 강좌에서는 이 쿼리를 저장 프로시저로 한번 만들어볼까 합니다. 그러면, 이후에는 머리 아프게 이러한 로직을 다시 생각할 필요없이그냥 저장 프로시저만 사용하면 되니까요 ^^

그리고 난 다음에, 서서히 하나씩 하나씩 우리의 게시판을 만들어 나가 봐여~~
그럼 다음주에 뵐께요. 좋은 하루 되세요.


authored by


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

로딩 중입니다...

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