Perlprog - BBS 5
Description
게시판 만들기 5 - 답변
BBS 만들기 5
게시판 만들기 다섯번째 야그 입니다. 이번엔 답변을 쓰는 것에 대해 알아 보도록 하겠습니다.
여기서 특별히 신경을 쓰실 부분은 원래의 답과, 그 글에 대한 답변을 어떻게 구성할거냐 하는 겁니다. 그냥 앞 부분에(지금 저의 게시판처럼) '[답변]' 이렇게 하구, 맨 끝의 목록에 붙인다면 신경쓸것이 전혀 없죠.
그냥 쓰기 폼에, 제목부분을 따서 입력시켜 놓으면 그만이죠. 하지만 이렇게 하지 않겠습니다. 답변을 하게 되면, 원래의 제목줄 밑으로 '└▷' 표시와 함께 넣겠습니다. 음... 이렇게 되면 인덱스 번호를 어떻게 붙일까여...
'1'번에 답변을 쓰게 되면 '1-1'이 되고, '1-1'에 답변을 쓰게 되면 '1-1-1'이 되고, '1'번의 두번째 답변은 '1-2'가 되고 등등등....
위의 인덱스 파일을 보면
1
1-2
1-1
1-1-1
이렇게 되는 거죠. 여기에 1-1에 또 답변을 한다면...
1
1-2
1-1
1-1-2
1-1-1
이렇게 되는 겁니다. 그럼 찬찬히 가겠습니다.
board_reply.cgi - 답변하기 - 폼
#! /usr/local/bin/perl require "./basic.pl"; require "./board.conf"; &parse_input(*ReplyData); if (!$ReplyData{'reply'}) { &show_reply_form($ReplyData{'num'}); } else { &save_reply_data; }
이것이 board_reply.cgi 메인 루틴 입니당.. ^^;;; 너무 짧다구여? 그렇지 않습니다. 서브루틴안에 서브루틴이 많이 존재합니다.
위의 코드를 잠시 보죠. 지난번 board_view.cgi에서 답변에 링크를 어떻게 걸었는지 보겠습니다.
<a href=\"./board_reply.cgi?num=$file_number&virtual=$head_number\" onMouseOver=\"window.status=('Reply data $head_number'); return true;\">$REPLAY_IMAGE</a>
위와 같죠. 넘기는 변수로는 파일의 번호(실제 인덱스 번호), 가상번호. 이렇게 두 개입니다.
우선 reply의 값을 검사하죠. 없으면 폼을 보여주고, 있으면 자료를 저장합니다. board_view.cgi에서 reply라는 변수명과 값은 없습니다. 그러니 폼을 보여주죠. 하지만 폼에는 hidden 속성으로 reply 변수와 값이 존재합니다.
그렇기 때문에 처음 답변 버튼을 누르면, 폼이 뜨고, 그상태에서 저장 단추를 누르면 그 데이터가 저장이 됩니다. 왜 이렇게 했을까여.... 그건 board_reply.cgi에서 폼을 보여주는 것과 저장하는것, 이 두 가지를 모두 처리하기 위해서입니다.
show_reply_form()
그럼 show_reply_form()
을 보겠습니다. 이 함수의 인자로는 실제의 파일 번호가 넘어갑니다. 그건 원래 글의 타이틀과 내용을 추려내기 위해서입니다.
sub show_reply_form { my ($num) = @_; my ($ReplyFileName, @ReplyData, $re_key, $re_value, $re_list, @Reply, $re_body); $ReplyFileName = $data_dir."/board$num".".dat"; open (FILE, "$ReplyFileName") || die &error_message("$ReplyFileName을 열수 없습니다."); @ReplyData = <FILE>; close (FILE); #### 답변하고자 하는 실제 파일을 정의하고 엽니다. for $re_list(0..7) { ($re_key, $re_value) = split(/=/, $ReplyData[$re_list]); $re_value =~ tr/+/ /; $re_value =~ s/%(..)/pack("c", hex($1))/ge; $Reply{$re_key} = $re_value; } #### 우리가 필요한건 타이틀만 꺼내면 되죠. 폼에 자동으로 입력시켜 주기 위해서. 그리고 아래의 폼대로 입력폼을 작성합니다. #### board_write.cgi 와 거의 같습니다. 단 form action 부분이 틀리죠. 그리고, 제목과, 내용란에 기존의 내용을 넣습니다. #### 내용을 넣을때 기존에 작성된 데이터 앞에 '>'를 넣어줍니다. &print_header; print " <html> <head> <title></title> <script language=JavaScript> function frm_send() { var strvl = new Array(5); strvl[0] = document.frm.title.value; strvl[1] = document.frm.name.value; strvl[2] = document.frm.email.value; strvl[3] = document.frm.body.value; strvl[4] = document.frm.pawd.value; if (strvl[0] == \"\") { alert(\"제목을 쓰셔야 되여\"); } else { if (strvl[1] == \"\") { alert(\"이름을 쓰세요\"); } else { if (strvl[2] == \"\") { alert(\"전자우편을 써주세요\"); } else { if (strvl[3] == \"\") { alert(\"내용은 필수 입니다\"); } else { if (strvl[4] == \"\") { alert(\"암호를 써주세여\"); } else { document.frm.submit(); return (true); } } } } } } </script> </head> <body bgcolor=white> <form action=\"./board_reply.cgi\" method=post name=frm>\n"; &print_content; print " <table border=0 width=550 bgcolor=white> <tr>"; #### 제목란에 위에서 분리해낸 제목을 넣습니다. print " <td align=right width=150><font size=2 color=black><b>제 목 : </b></font></td> <td><input type=text name=title size=35 maxlength=80 value=\"Re:$Reply{'TITLE'}\"></td> </tr> <tr> <td align=right><font size=2 color=black><b>이 름 : </b></font></td> <td><input type=text name=name size=16 maxlength=5></td> </tr> <tr> <td align=right><font size=2 color=black><b>전자우편 : </b></font></td> <td><input type=text name=email size=25 maxlength=80></td> </tr> <tr> <td align=right><font size=2 color=black><b>홈페이지 : </b></font></td> <td><input type=text name=homepage size=30 maxlength=80 value=http://></td> </tr> <tr> <td align=right valign=top><font size=2 color=black><b>내 용 : </b></font></td> <td><textarea name=body rows=20 cols=60>\n"; #### 기존의 내용을 분리하고 앞에 '>'를 넣어서 출력을 합니다. 답변하시는 분은 그 뒤에 이어서 답변을 하면 됩니다. ($re_key, $re_body) = split(/=/, $ReplyData[8]); chomp($re_body); print ">$re_body"; for $re_list(9..$#ReplyData) { chomp($ReplyData[$re_list]); print ">$ReplyData[$re_list]"; } print " </textarea></td> </tr> <tr> <td align=right><font size=2 color=black><b>비밀번호 : </b></font></td> <td><input type=password name=pawd size=8 maxlength=8><font size=2 color=black>(8자 까지)</font></td> </tr> <tr> <td></td> <td><font size=2 color=blue>(참고사항 : HTML은 지원 되지 않습니다.)</font></td> </tr> <tr> <td colspan=2><hr width=100% color=#bedaff size=3></td> </tr> <tr> <td colspan=2 align=center><a href=\"javascript:frm_send()\" onMouseOver=\"window.status=('Send Data'); return true;\">$WRITE_IMAGE</a></td> </tr> </table>\n"; &about_board; #### reply변수명과 값을 hidden속성으로 다음 cgi프로그램에 전달합니다. 이건 위에서 설명했죠? 그와 더불어 답변을 선택한 데이터의 인덱스번호도 hidden속성으로 전달합니다. print " <input type=hidden name=reply value=1> <input type=hidden name=idx value=$num> </form> </body> </html>\n"; }
board_reply.cgi - 답변 저장
폼 출력이 끝났습니다. 여기서 우리가 저장을 누르면, board_reply.cgi가 호출되고, reply=1
이라는 값이 있기에 save_reply_data()
함수를 호출하겠죠.
그럼 위의 함수를 살펴봅시다. 여기서 우리가 신경 쓸 부분은 새로운 인덱스 생성과, 그 새로이 생성된 인덱스 번호를 인덱스 파일에 저장하는 부분입니다. 맨 윗줄에 저장하면 안 되져.
위에서 보셨듯이
1
1-2
1-1
이런 식으로 답변을 하고자 했던 데이터의 밑에 저장이 되어야 합니다.
물론 인덱스 파일만 이렇게 하면 안됩니다. 패스워드를 저장한 파일도 이렇게 해야 됩니다.
save_reply_data()
sub save_reply_data { my (@ReIdx, @ReTemp, $re_idx, $re_file_name, $RePass, @RePwdData, $re_date, $re_i); my ($re_total, $re_now); #### 인덱스파일과 패스워드 파일내의 전체 배열에서 우리가 선택한 인덱스번호의 배열 번호를 찾기 위한 get_list() 함수를 호출합니다. 이유는 그 번호 바로 아래에 답변한 데이터가 들어가야 하기 때문입니다. #### 넘기는 인수는 실제 인덱스 번호입니다. 그리고 저장시 필요한 ip, 시간을 얻어 냅니다. $re_now = &get_list($ReplyData{'idx'}); $re_ip = $ENV{'REMOTE_ADDR'}; $re_date = &get_date; #### 바로 아래의 부분이 답변을 한 데이터의 인덱스 번호를 추출해내는 함수인 get_reply_idx() 함수를 호출하는 부분입니다. 요건 아래서.... $re_idx = &get_reply_idx($re_now, $ReplyData{'idx'}); #### 그리고 파일명을 정하고, 인덱스 파일을 열어서, 답변을 하고자 했던 데이터의 바로 아랫줄에 인덱스 데이터를 저장합니다. $re_file_name = $data_dir."/board$re_idx.dat"; open (FILE, "$idx_file") || die &error_message("$idx_file을 열수 없습니다."); @ReIdx = <FILE>; close (FILE); open (FILE, ">$idx_file") || die &error_message("$idx_file을 열수 없습니다."); $re_i = 0; foreach (@ReIdx) { @ReTemp = split(/::/, $_); if ($re_i eq $re_now) { print FILE $_; print FILE $re_idx,"::",$ReplyData{'name'},"::",$ReplyData{'title'},"::","0","::",$re_date,"\n"; } else { print FILE $_; } $re_i++; } close (FILE); #### 암호를 저장합니다. 저장하는 방식은 인덱스 파일을 저장할때와 같습니다. $RePass = &get_password($ReplyData{'pawd'}); open (FILE, "$pwd_file") || die &error_message("$pwd_file을 열수 없습니다."); @RePwdData = <FILE>; close (FILE); open (FILE, ">$pwd_file") || die &error_message("$pwd_file을 열수 없습니다."); $re_i = 0; foreach (@RePwdData) { @ReTemp = split(/::/, $_); if ($re_i eq $re_now) { print FILE $_; print FILE $re_idx, "::", $RePass, "\n"; } else { print FILE $_; } $re_i++; } close (FILE); #### 그리구, 전체 데이터를 저장합니다. open (FILE, ">$re_file_name") || die &error_message("$re_file_name을 열수 없습니다."); print FILE "IDX=$re_idx\n"; print FILE "COUNT=0\n"; print FILE "DATE=$re_date\n"; print FILE "HOMEPAGE=$ReplyData{'homepage'}\n"; print FILE "NAME=$ReplyData{'name'}\n"; print FILE "IP=$re_ip\n"; print FILE "EMAIL=$ReplyData{'email'}\n"; print FILE "TITLE=$ReplyData{'title'}\n"; print FILE "BODY=$ReplyData{'body'}\n"; close (FILE); #### 리스트를 보여주는 view_list.cgi로 옮깁니다. print "Location: $url/cgi-bin/view_list.cgi\n\n"; }
이렇게 하면 일련의 작업이 끝난겁니다. 그럼 위에 있던 get_list()
, get_reply_idx()
함수에 관해서 살펴보도록 하겠습니다.
get_list()
함수는 그리 어려운 내용이 없습니다. 인덱스 파일을 열고, 넘어온 인덱스 번호가 배열의 몇 번째인가 하는 것만 알아내면 됩니다. 우리는 1,2,3.... 펄은 0,1,2 이기 때문에 마지막에 1을 더한 수를 리턴합니다.
get_list()
sub get_list { my ($get_idx) = @_; open (FILE, "$idx_file") || die &error_message("$idx_file을 열수 없습니다."); $get_count = 0; while (<FILE>) { @temp = split(/::/, $_); if ($temp[0] eq $get_idx) { last; } $get_count++; } close (FILE); return $get_count++; }
답변의 인덱스 번호를 생성하는 과정도 그리 어려운건 아니지만, 잘 이해해야 만이 쉽습니다.
- 우선 답변을 하고자 했던 인덱스 번호에 '-1'을 붙여서 임의의 인덱스 번호를 생성합니다.
- 그리고 이 임의의 인덱스 번호를, 원래 답변을 하고자 했던 인덱스 번호 바로 아래의 인덱스 번호와 비교를 합니다.
- 그 다음, 두 개의 인덱스 번호의 길이를 검사합니다. 길이가 다르다면, 그 글의 답변은 지금이 처음이기 때문에, 임시 인덱스 번호가 실제 인덱스 번호가 됩니다. 이 인덱스 번호를 리턴합니다.
- 만약 길이가 같다면, '-'이 있는지 없는지부터 검사합니다. '-'이 없으면, 임시 인덱스 번호가 실제 인덱스번호가 되는거죠.
- '-'이 있다면, 이미 그 글에 대한 답변이 있는거니깐, 그 인덱스번호에 '1'을 더해서 실제 인덱스번호로 리턴합니다.
왜 위와 같은 과정을 거치는지는 잘 생각해 보세요. 다른 방법도 있겠지만.
get_reply_idx()
sub get_reply_idx { my ($now_num, $now_num_idx) = @_; my (@NowNum, @NowNumIdx, @NowNumIdxNum, $IdxEndNum); open (FILE, "$idx_file") || die &error_message("$idx_file을 열수 없습니다."); @NowNum = <FILE>; close (FILE); @NowNumIdx = split(/::/, $NowNum[$now_num + 1]); @NowNumIdxEnd = split(/-/, $NowNumIdx[0]); $IdxEndNum = $now_num_idx."-1"; if (length($IdxEndNum) eq length($NowNumIdx[0])) { if ($NowNumIdx[0] =~ /-/) { return $IdxEndNum = $now_num_idx."-".($NowNumIdxEnd[$#NowNumIdxEnd] + 1); } else { return $IdxEndNum; } } else { return $IdxEndNum; } }
NOTES
끝났습니다. board_reply.cgi.
이로서 답변하는 기능까지 구현을 하였습니다. 삭제, 편집, 검색, 관리자.. 이렇게 남았군여. 그럼 담에 또 한가지의 기능을 구현하도록 하겠습니다.
의문나는 점이나 이상한 점을 발견하였을 시는, 주저마시고 게시판에 남겨주세요.
'Common Gateway Interface > Perl' 카테고리의 다른 글
[옛 강좌] 28. Perlprog - BBS 7 (0) | 2015.04.29 |
---|---|
[옛 강좌] 27. Perlprog - BBS 6 (0) | 2015.04.29 |
[옛 강좌] 25. Perlprog - BBS 4 (0) | 2015.04.29 |
[옛 강좌] 24. Perlprog - BBS 3 (0) | 2015.04.29 |
[옛 강좌] 23. Perlprog - BBS 2 (0) | 2015.04.29 |