저번 글에서는 글 목록 및 글쓰기 기능을 구현하는 코드에 대하여 설명하였다. 이번 글에서는 글 페이지 및 글 수정, 삭제를 구현하는 코드에 대하여 설명하겠다. 글 페이지에는 댓글 쓰기, 수정, 삭제 기능도 포함되어있다. 모든 디자인은 부트스트랩을 사용했다.
1. 댓글 DB 생성 및 설정
댓글 작성 시 댓글 정보가 저장되는 DB를 생성한다. 댓글 내용은 300자로 제한한다.
CREATE TABLE `board_reply` ( `no` INT(11) NOT NULL AUTO_INCREMENT, `board_no` INT(11) NOT NULL, `reply_content` VARCHAR(300) NOT NULL COLLATE 'utf8_general_ci', `reply_writer` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci', `reply_writer_id` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci', `reply_date` DATETIME NOT NULL, `delYn` ENUM('Y','N') NOT NULL DEFAULT 'N' COLLATE 'utf8_general_ci', PRIMARY KEY (`no`) USING BTREE ) COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=7 ;
2. 함수 추가 작성
저번 글에서 작성한 get_board_data 함수의 foreach문 내에 글 목록에서 출력할 댓글 수를 불러오는 코드를 추가한다.
글 페이지 조회 시 조회수를 업데이트하고 COOKIE 값에 글 조회 여부를 저장하는 함수를 추가하고 댓글 데이터를 불러오는 함수도 추가한다.
<?php // get_board_data 함수의 foreach문 내에 추가 $reply_cnt_sql = "select count(*) as cnt from board_reply where delYn = 'N' and board_no = '".$row["no"]."'"; $reply_cnt_result = $mysqli->query($reply_cnt_sql); $reply_cnt_row = $reply_cnt_result->fetch_array(); $row["board_reply_cnt"] = $reply_cnt_row["cnt"]; ?>
<?php function update_board_cnt($no){ global $mysqli; $sql = "UPDATE board set board_cnt=board_cnt+1 where no='".$no."'"; $result = $mysqli->query($sql); setcookie("viewed".$no, "viewed", time() + 60*60*24, "/"); } function get_reply_data($where){ global $mysqli; $sql = "select * from board_reply where delYn = 'N' ".$where." order by no asc"; $result = $mysqli->query($sql); if($result){ while($row = $result->fetch_array()){ $rows[] = $row; } } $data = array(); $data["rows"] = $rows; return $data; } ?>
3. 글 목록 폼 댓글 수 추가 작성
저번 글에서 작성한 글 목록 폼에서 제목을 불러오는 코드 다음에 댓글 수를 추가한다.
<span class="fs-5 col-9"><b><?=$row["board_title"]?></b><span class="opacity-85"> <?php if(!empty($row["board_reply_cnt"])) echo "[".$row["board_reply_cnt"]."]"; ?></span></span>
4. 글 페이지 작성
COOKIE 값의 여부를 검사해서 조회수를 업데이트하고 글을 불러온다.
SESSION 값에 저장되어있는 id가 작성자의 id와 일치한다면 글 수정 및 삭제 버튼을 출력한다. 그 다음 댓글 목록과 작성 폼을 불러온다. 댓글도 글과 같이 SESSION 값으로 확인해서 댓글 작성자면 댓글 수정 및 삭제 버튼을 출력한다.
Display none으로 숨긴 부분은 댓글 수정 폼이며 댓글 수정 버튼 클릭 시 표시되도록 동작 구현 파일에서 구현할 것이다.
<?php if(!$_COOKIE["viewed".$_GET["no"]]){ $_COOKIE["viewed".$_GET["no"]] = ""; update_board_cnt($_GET["no"]); } $where = " and no = '".$_GET["no"]."'"; $row = get_board_data($where, "view"); if(!empty($row)){ ?> <div class="list-group w-auto mb-2"> <div class="list-group-item list-group-item-action py-3"> <div class="d-flex w-100 justify-content-between"> <div class="w-100"> <b class="fs-2"><?=$row["board_title"]?></b><br> <span class="fs-5 opacity-85"><?=$row["board_writer"]?></span><br> <span class="fs-6 opacity-85" style="margin-right: 9px;"><?=$row["board_date"]?></span><span class="fs-6 opacity-85">조회 <?=$row["board_cnt"]?></span><br><hr> <span id="post_content" class="mb-0 opacity-85"><?=$row["board_content"]?></span> </div> </div> </div> </div> <?php } ?> <?php if($_SESSION["id"] == $row["board_writer_id"]){ ?> <div class="d-flex justify-content-end"> <button type="button" class="btn btn-success" onclick="location.href='글쓰기 폼 경로?no=<?=$_GET['no']?>'">수정</button> <button type="button" class="btn btn-secondary" id="del_btn" del_no="<?=$_GET["no"]?>">삭제</button> </div> <?php } ?> <div class="mb-2"> <span class="fs-3">댓글</span> </div> <div class="list-group w-auto"> <div class="list-group-item list-group-item-action py-3"> <div class="d-flex w-100 justify-content-between"> <div class="form-group row w-100"> <?php $where = " and board_no = '".$_GET["no"]."'"; $data = get_reply_data($where); if(!empty($data["rows"])){ ?> <hr> <?php foreach($data["rows"] as $reply_row){ ?> <div class="mb-3"> <div class="mb-2"> <span class="fs-5 opacity-85"><?=$reply_row["reply_writer"]?></span> </div> <div id="mod_reply_form_org_<?=$reply_row["no"]?>"> <span class="fs-5 opacity-85" id="reply_content_no_<?=$reply_row["no"]?>" style="white-space:pre;"><?=$reply_row["reply_content"]?></span><br> <span class="fs-6 opacity-85"><?=$reply_row["reply_date"]?></span><br> </div> <div id="mod_reply_form_<?=$reply_row["no"]?>" style="display:none;"> <div class="input-group"> <textarea class="form-control" id="mod_reply_content_<?=$reply_row["no"]?>" rows="2"><?=$reply_row["reply_content"]?></textarea> <button type="button" id="mod_reply_btn_<?=$reply_row["no"]?>" class="btn btn-success" board_no="<?=$_GET["no"]?>">수정</button> <button type="button" id="mod_reply_cancel_btn_<?=$reply_row["no"]?>" class="btn btn-secondary">취소</button></div> </div> <?php if($_SESSION["id"] == $reply_row["reply_writer_id"]){ ?> <div id="reply_btn_form_<?=$reply_row["no"]?>" class="d-flex justify-content-end"> <button type="button" class="btn btn-success btn-sm" id="mod_reply_form_btn_<?=$reply_row["no"]?>">수정</button> <button type="button" class="btn btn-secondary btn-sm" id="del_reply_btn_<?=$reply_row["no"]?>" board_no="<?=$_GET["no"]?>">삭제</button> </div> <?php } ?> </div> <hr> <?php } } ?> <div class="mb-2"> <span class="fs-5 opacity-85"><?=$_SESSION["name"]?></span> </div> <div class="input-group"> <textarea class="form-control" id="reply_content" rows="2" placeholder="댓글을 남겨보세요."></textarea> <button type="button" id="reply_btn" class="btn btn-primary" board_no="<?=$_GET["no"]?>">등록</button> </div> </div> </div> </div> </div> <script type="text/javascript" src="글 페이지 동작 구현 파일 경로"></script>
5. 글쓰기 폼 수정
저번 글에서 작성한 글쓰기 폼에 글 수정 버튼 클릭 시 글 데이터를 불러올 수 있게 수정한다. 작성자가 아닌 회원이 URL로 들어올 수 있으므로 작성자가 아니라면 접근을 제한한다.
<?php if($_GET["no"]){ $where = " and no = '".$_GET["no"]."'"; $row = get_board_data($where, "view"); if($row["board_writer_id"] != $_SESSION["id"] || $row["board_writer"] != $_SESSION["name"]){ echo "<script>alert('잘못된 접근입니다.');history.back();</script>"; } } ?> <script type="text/javascript" src="HuskyEZCreator.js 경로"></script> <form id="writefrm" action="데이터 처리 파일 경로" method="post" enctype="multipart/form-data"> <?php if($_GET["no"]){ ?> <input type="hidden" name="mod_no" value="<?=$row["no"]?>"> <?php } ?> <textarea class="form-control mb-1" name="board_title" id="board_title" rows="1" maxlength="100" placeholder="제목을 입력해 주세요."><?php if($_GET["no"]) echo $row["board_title"];?></textarea> <textarea class="form-control" name="board_content" id="ir1" rows="20" style="width:100%;"><?php if($_GET["no"]) echo $row["board_content"];?></textarea> </form><br> <div class="pagination justify-content-end"> <button type="button" class="btn btn-secondary" onclick="location.href='글 목록 폼 경로'">취소</button> <button type="button" id="write_btn" class="btn <?php if($_GET["no"]){ echo "btn-success"; }else{ echo "btn-primary"; } ?> "> <?php if($_GET["no"]){ echo "수정"; }else{ echo "등록"; } ?> </button> </div> <script type="text/javascript" src="글쓰기 동작 구현 파일 경로"></script>
6. 글 페이지 동작 구현 파일 작성
요소의 id 값 뒤에 있는 글, 댓글 번호를 불러와서 각 요소를 따로 제어한다. 삭제 버튼 클릭 시 메시지를 띄우고, 확인을 클릭하면 계속 진행한다. 필요한 값들은 임의의 폼을 만들어 모두 전송한다.
댓글 수정 버튼 클릭 시 다른 요소들은 모두 숨기고 댓글 수정 폼을 표시한다. 댓글 수정 취소 버튼 클릭 시 반대로 댓글 수정 폼을 숨기고 나머지 요소들은 다시 표시한다.
$("#del_btn").click(function(){ if(confirm("글을 삭제하시겠습니까?")){ var del_no = $("#del_btn").attr("del_no"); var delForm = $("<form></form>"); delForm.attr("method", "Post"); delForm.attr("action", "데이터 처리 파일 경로"); delForm.append($("<input/>", {type: "hidden", name: "del_no", value: del_no})); delForm.appendTo("body"); delForm.submit(); } }); $('#reply_btn').click(function(){ if($("#board_reply").val() == ""){ alert("댓글을 입력해 주세요."); return false; } var board_no = $("#reply_btn").attr("board_no"); var reply_content = $("#reply_content").val(); var replyForm = $("<form></form>"); replyForm.attr("method", "Post"); replyForm.attr("action", "데이터 처리 파일 경로"); replyForm.append($("<input/>", {type: "hidden", name: "board_no", value: board_no})); replyForm.append($("<input/>", {type: "hidden", name: "reply_content", value: reply_content})); replyForm.appendTo("body"); replyForm.submit(); }); $(document).on("click", "button[id^='mod_reply_form_btn_']", function(){ var reply_no = $(this).attr("id").split("mod_reply_form_btn_"); $("#mod_reply_form_org_"+reply_no[1]).hide(); $("#reply_btn_form_"+reply_no[1]).hide(); $("#reply_btn_form_"+reply_no[1]).removeClass("d-flex"); $("#mod_reply_form_"+reply_no[1]).show(); }); $(document).on("click", "button[id^='mod_reply_cancel_btn_']", function(){ var reply_no = $(this).attr("id").split("mod_reply_cancel_btn_"); $("#mod_reply_form_org_"+reply_no[1]).show(); $("#reply_btn_form_"+reply_no[1]).show(); $("#reply_btn_form_"+reply_no[1]).addClass("d-flex"); $("#mod_reply_form_"+reply_no[1]).hide(); }); $(document).on("click", "button[id^='mod_reply_btn_']", function(){ var mod_reply_no = $(this).attr("id").split("mod_reply_btn_"); var mod_reply_content = $("#mod_reply_content_"+mod_reply_no[1]).val(); var board_no = $("#del_reply_btn_"+mod_reply_no[1]).attr("board_no"); var modForm = $("<form></form>"); modForm.attr("method", "Post"); modForm.attr("action", "데이터 처리 파일 경로"); modForm.append($("<input/>", {type: "hidden", name: "mod_reply_no", value: mod_reply_no[1]})); modForm.append($("<input/>", {type: "hidden", name: "reply_content", value: mod_reply_content})); modForm.append($("<input/>", {type: "hidden", name: "board_no", value: board_no})); modForm.appendTo("body"); modForm.submit(); }); $(document).on("click", "button[id^='del_reply_btn_']", function(){ if(confirm("댓글을 삭제하시겠습니까?")){ var del_reply_no = $(this).attr("id").split("del_reply_btn_"); var board_no = $("#del_reply_btn_"+del_reply_no[1]).attr("board_no"); var delForm = $("<form></form>"); delForm.attr("method", "Post"); delForm.attr("action", "데이터 처리 파일 경로"); delForm.append($("<input/>", {type: "hidden", name: "del_reply_no", value: del_reply_no[1]})); delForm.append($("<input/>", {type: "hidden", name: "board_no", value: board_no})); delForm.appendTo("body"); delForm.submit(); } });
7. 데이터 처리 파일 수정
저번 글에서 작성한 데이터 처리 파일을 다음과 같이 수정 및 추가한다. mod_no, mod_reply_no에 대한 예외 처리는 반드시 한다. 예외 처리를 하지 않으면 쓰기와 수정이 동시에 실행된다.
글 수정, 삭제 및 댓글 쓰기, 수정, 삭제 시 값이 모두 잘 들어왔는지 확인 후 DB에 데이터를 저장 또는 업데이트하고 글 목록 폼 또는 글 페이지로 이동한다.
<?php global $mysqli; if(empty($_POST["mod_no"])){ if(!empty($_POST["board_title"]) && !empty($_POST["board_content"])){ $column = ""; $values = ""; foreach($_POST as $key => $value){ $column .= $key.", "; $values .= "'".$value."', "; } $sql = "insert into board (".$column." board_writer, board_writer_id, board_date) values (".$values." '".$_SESSION["name"]."', '".$_SESSION["id"]."', now())"; $result = $mysqli->query($sql); if($result){ echo "<script>alert('글이 등록되었습니다.');location.replace('글 목록 폼 경로');</script>"; }else{ echo "<script>alert('문제가 발생했습니다. 관리자에게 문의해 주세요.');</script>"; } } } if(!empty($_POST["mod_no"])){ if(!empty($_POST["board_title"]) && !empty($_POST["board_content"])){ $update_sql = ""; foreach($_POST as $key => $value){ if($key == "mod_no") continue; $update_sql .= $key."='".$value."', "; } $update_sql = substr($update_sql, 0, -2); $sql = "update board set ".$update_sql." where no = '".$_POST["mod_no"]."'"; $result = $mysqli->query($sql); if($result){ echo "<script>alert('글이 수정되었습니다.');location.replace('글 페이지 경로?no=".$_POST["mod_no"]."');</script>"; }else{ echo "<script>alert('문제가 발생했습니다. 관리자에게 문의해 주세요.');</script>"; } } } if(!empty($_POST["del_no"])){ $sql = "update board set delYn = 'Y' where no = '".$_POST["del_no"]."'"; $result = $mysqli->query($sql); if($result){ echo "<script>alert('글이 삭제되었습니다.');location.replace('글 목록 폼 경로');</script>"; }else{ echo "<script>alert('문제가 발생했습니다. 관리자에게 문의해 주세요.');</script>"; } } if(empty($_POST["mod_reply_no"])){ if(!empty($_POST["reply_content"]) && !empty($_POST["board_no"])){ $column = ""; $values = ""; foreach($_POST as $key => $value){ $column .= $key.", "; $values .= "'".$value."', "; } $sql = "insert into board_reply (".$column." reply_writer, reply_writer_id, reply_date) values (".$values." '".$_SESSION["name"]."', '".$_SESSION["id"]."' ,now())"; $result = $mysqli->query($sql); if($result){ echo "<script>alert('댓글이 등록되었습니다.');location.replace('글 페이지 경로?no=".$_POST["board_no"]."');</script>"; }else{ echo "<script>alert('문제가 발생했습니다. 관리자에게 문의해 주세요.');</script>"; } } } if(!empty($_POST["mod_reply_no"])){ if(!empty($_POST["reply_content"]) && !empty($_POST["board_no"])){ $update_sql = ""; foreach($_POST as $key => $value){ if($key == "mod_reply_no" || $key == "board_no") continue; $update_sql .= $key."='".$value."', "; } $update_sql = substr($update_sql, 0, -2); $sql = "update board_reply set ".$update_sql." where no = '".$_POST["mod_reply_no"]."'"; $result = $mysqli->query($sql); if($result){ echo "<script>alert('댓글이 수정되었습니다.');location.replace('글 페이지 경로?no=".$_POST["board_no"]."');</script>"; }else{ echo "<script>alert('문제가 발생했습니다. 관리자에게 문의해 주세요.');</script>"; } } } if(!empty($_POST["del_reply_no"]) && !empty($_POST["board_no"])){ $sql = "update board_reply set delYn = 'Y' where no = '".$_POST["del_reply_no"]."'"; $result = $mysqli->query($sql); if($result){ echo "<script>alert('댓글이 삭제되었습니다.');location.replace('글 페이지 경로?no=".$_POST["board_no"]."');</script>"; }else{ echo "<script>alert('문제가 발생했습니다. 관리자에게 문의해 주세요.');</script>"; } } ?>
댓글 쓰기 후 페이지 이동이 잘 되는지 확인하고 글 및 댓글 수정, 삭제도 잘 동작하는지 확인한다.