프로그래밍/- 남굼성의 Spring 교육
spring 게시판 댓글기능
즐겁게 하하하
2022. 2. 19. 00:29
728x90
먼저 테이블을 만든다..
CREATE TABLE `board` (
`idx` INT NOT NULL AUTO_INCREMENT,
`writer` VARCHAR(50) NOT NULL COMMENT '작성자' COLLATE 'utf8_general_ci',
`writer_id` VARCHAR(50) NOT NULL COMMENT '작성자' COLLATE 'utf8_general_ci',
`title` VARCHAR(50) NOT NULL COMMENT '제목' COLLATE 'utf8_general_ci',
`content` LONGTEXT NULL COMMENT '내용' COLLATE 'utf8_general_ci',
`view` INT NULL DEFAULT '0',
`req_dt` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록시간',
PRIMARY KEY (`idx`) USING BTREE
)
COMMENT='자유게시판'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=35
;
CREATE TABLE `board_reply` (
`no` INT NOT NULL AUTO_INCREMENT,
`bno` INT NOT NULL COMMENT '댓글이 속한 게시글 번호 (받아와야 하는 값)',
`grp` INT NOT NULL COMMENT '댓글 그룹 번호 (모댓글과 거기에 속한 댓글은 같은 grp를 가짐)',
`grpl` INT NOT NULL DEFAULT '0' COMMENT '댓글 깊이 (0: 모댓글) (1: 댓글)',
`grps` INT NOT NULL COMMENT '그룹 내 댓글 순서 (오래된글 ~ 최신글 오름차순)',
`writer` VARCHAR(100) NOT NULL COLLATE 'utf8_general_ci',
`content` VARCHAR(1000) NULL COMMENT '댓글 삭제시 content 공백처리하기' COLLATE 'utf8_general_ci',
`wdate` DATETIME NOT NULL,
PRIMARY KEY (`no`) USING BTREE,
INDEX `FK_board_reply_board` (`bno`) USING BTREE,
INDEX `FK_board_reply_user` (`writer`) USING BTREE,
CONSTRAINT `FK_board_reply_board` FOREIGN KEY (`bno`) REFERENCES `house`.`board` (`idx`) ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT `FK_board_reply_user` FOREIGN KEY (`writer`) REFERENCES `house`.`user` (`user_id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COMMENT='게시판 댓글'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=74
;
CREATE TABLE `user` (
`user_no` INT NOT NULL AUTO_INCREMENT,
`user_id` VARCHAR(45) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
`user_pw` VARCHAR(225) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
`user_nm` VARCHAR(45) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
`user_tel` VARCHAR(45) NULL DEFAULT '' COLLATE 'utf8_general_ci',
`user_email` VARCHAR(225) NULL DEFAULT '' COLLATE 'utf8_general_ci',
`user_isshow` ENUM('Y','N') NOT NULL DEFAULT 'Y' COMMENT '사용, 미사용' COLLATE 'utf8_general_ci',
`user_salt` VARCHAR(100) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
`fail_count` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '실패 횟수',
`modify_pw` DATE NOT NULL COMMENT '최근 비밀번호 변경일',
`req_dt` DATE NOT NULL,
PRIMARY KEY (`user_no`) USING BTREE,
UNIQUE INDEX `user_id` (`user_id`) USING BTREE,
UNIQUE INDEX `user_email` (`user_email`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=28
;
파일 구조
view.jsp
<%@ page contentType="text/html;charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page isELIgnored="false" %>
<%@ include file="../inc/adminHeader.jsp" %>
<script type="text/javascript" src="<c:url value="/resources/js/board/view.js" />" ></script>
<%
String referer = request.getHeader("REFERER");
System.out.println(referer);
if(referer == null){
%>
<script>
alert("URL을 직접 입력해서 접근하셨습니다.\n정상적인 경로를 통해 다시 접근해 주세요.");
document.location.href="/";
</script>
<%
return;
}
%>
<section>
<div class="col-xl-10 col-lg-9 col-md-8 ml-auto bg-light fixed-top py-2 top-navbar">
<div class="row align-items-center">
<div class="col-md-4">
<h4 class="text-secondary text-uppercase mb-2 mt-2">자유게시판 - 상세보기</h4>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-xl-10 col-md-8 col-lg-9 ml-auto">
<div class="row pt-md-5 mt-md-3 mb-5">
<div class="col-xl-12 col-md-12 col-sm-12 p2">
<input type="hidden" id="chk_board_no" value="${boardList.idx}" />
<input type="hidden" id="login_id" value="${sessionScope.user_id}" />
<table class="table table-bordered mt-1">
<colgroup>
<col style="width:15%;">
<col>
<col style="width:15%;">
<col>
</colgroup>
<thead>
<tr>
<th colspan="4">
<div class="col-md-12">
<span>${boardList.title}</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<th>작성자</th>
<td>${boardList.writer}</td>
<th>등록일</th>
<td>
<fmt:parseDate value="${boardList.req_dt}" pattern="yyyy-MM-dd'T'HH:mm:ss" var="parsedDateTime" type="both" />
<fmt:formatDate pattern="yyyy-MM-dd HH:mm:ss" value="${parsedDateTime}" />
</td>
</tr>
<tr>
<th>조회수</th>
<td><fmt:formatNumber value="${boardList.view}" /></td>
<td colspan="2">
<c:if test="${boardList.writer_id eq sessionScope.user_id }" >
<button class="btn btn-info btn-md" onclick="boardModify()">수정</button>
<button class="btn btn-danger btn-md" onclick="boardDelete()">삭제</button>
</c:if>
<button class="btn btn-primary btn-md" onclick="history.back(-1)">목록으로</button>
</td>
</tr>
<tr>
<td colspan="4">
<div class="col-md-9">
<div class="board-contents" style="min-height: 430px;">
${boardList.content}
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="card card-body">
<c:if test="${not empty sessionScope.user_id}"> <!-- 댓글 작성 -->
<div class="row reply_write mb-5">
<div class="col-2">
${sessionScope.user_id}
</div>
<div class="col-8" class="input_reply_div">
<input class="w-150 form-control" id="input_reply0" type="text" placeholder="댓글입력..." maxlength="1000">
</div>
<div class="col-2">
<button type="button" class="btn btn-success btn-md write_reply" onclick="WriteReReply(0)">등록</button>
</div>
</div>
</c:if>
<div class="reply-list reply-list${boardList.idx}"> <!-- 댓글이 목록이 들어가는 곳 -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<%@ include file="../inc/adminFooter.jsp" %>
</body>
</html>
view.js
/** 게시판->자유게시판 : 읽기 */
let submitFlag = false;
$(document).ready(function (){
replyList($("#chk_board_no").val());
})
function open_fun( no ){
document.getElementById("link"+ no ).innerHTML = "<a href='#' data-toggle='collapse' data-target='#re_reply"+ no +"' aria-expanded='false' aria-controls='collapseExample' onclick=\"close_fun("+ no +")\">댓글▲</a>";
}
function close_fun( no ){
document.getElementById("link"+ no ).innerHTML = "<a href='#' data-toggle='collapse' data-target='#re_reply"+ no +"' aria-expanded='false' aria-controls='collapseExample' onclick=\"open_fun("+ no +")\">댓글▼</a>";
}
function replyList(chk, section = 0){
$.ajax({
type: "GET",
enctype: 'multipart/form-data',
processData: false,
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
url: "/board/replyList?chk_board_no=" + chk,
dataType: "JSON",
async : false,
success : function(result) {
// 댓글 목록을 html로 담기
let listHtml = "";
let data = result.boardReplyList;
for(const i in data){
let no = data[i].no;
let bno = data[i].bno;
let grp = data[i].grp;
let grps = data[i].grps;
let grpl = data[i].grpl;
let writer = data[i].writer;
let content = data[i].content;
let wdate = data[i].wdate;
//console.log(grpl); // 모댓글일땐 0, 답글일땐 1
if(content == ""){
listHtml += " <table class='col-12'>";
listHtml += " <tr>";
listHtml += " <td class='col-1' style='padding:0px;'></td>";
listHtml += " <td colspan='11'> (삭제된 댓글입니다) </td>";
listHtml += " </tr>";
listHtml += " </table>";
}else{
if(grpl == 0){ // 모댓글일때
listHtml += "<div class='rereply-content'>";
listHtml += " <table class='col-12'>";
listHtml += " <tr>";
listHtml += " <td class='col-1' style='padding:10px;'>";
listHtml += " <a href='#'> ";
listHtml += " <img style='width:40px;height:40px;' src='/resources/images/none_profile.png'/>";
listHtml += " </a> ";
listHtml += " </td>";
listHtml += " <td class='col-2' style='padding:0px;'>";
listHtml += " <span>";
listHtml += " <b>"+ writer.substring(0,2)+ "*****" +"</b>";
listHtml += " <BR><span id='link"+ no +"'><a href='#' data-toggle='collapse' data-target='#re_reply"+ no +"' aria-expanded='false' aria-controls='collapseExample' onclick=\"open_fun("+ no +")\">댓글▼</a></span>";
listHtml += " </span>";
listHtml += " </td>";
listHtml += " <td>";
listHtml += " <span class='col-7 ml-2'>";
listHtml += content;
listHtml += " </span>";
listHtml += " </td>";
listHtml += " <td class='col-2'>";
listHtml += wdate;
//현재 사용자가 이 댓글의 작성자일때 삭제 버튼이 나온다.
if($("#login_id").val() == writer){
listHtml += " <a href='javascript:;' onclick=\"deleteReply("+ grp +","+ no +")\">삭제</a>";
}
listHtml += " </td>";
listHtml += " </tr>";
listHtml += " </table>";
listHtml += "</div>";
}else{ // 답글일때
listHtml += " <table class='col-12'>";
listHtml += " <tr>";
listHtml += " <td class='col-1' style='padding:0px;'></td>";
listHtml += " <td class='col-1' style='padding:10px;'>";
if($("#login_id").val() == writer){
listHtml += " <a href='javascript:;'><img style='width:40px;height:40px;' src='/resources/images/none_profile3.png'/></a> ";
}else{
listHtml += " <a href='javascript:;'><img style='width:40px;height:40px;' src='/resources/images/none_profile2.png'/></a> ";
}
listHtml += " </td>";
listHtml += " <td class='col-2' style='padding:0px;'>";
listHtml += " <span>";
listHtml += " <b>"+ writer.substring(0,2)+ "*****" +"</b>";
listHtml += " </span>";
listHtml += " </td>";
listHtml += " <td>";
listHtml += " <span class='col-6 ml-2'>";
listHtml += content;
listHtml += " </span>";
listHtml += " </td>";
listHtml += " <td class='col-2'>";
listHtml += wdate;
//현재 사용자가 이 댓글의 작성자일때 삭제 버튼이 나온다.
if($("#login_id").val() == writer){
listHtml += " <a href='javascript:;' onclick=\"deleteReply("+ grp +","+ no +")\">삭제</a>";
}
listHtml += " </td>";
listHtml += " </tr>";
listHtml += " </table>";
}
}
// ================================
if(grpl == 0){// 모댓글 의 하위 댓글 div 구역의 생성
listHtml += " <div class='collapse row rereply_write' id='re_reply"+ no +"'>";
listHtml += " <hr class='col-11' style='border: solid 1px lightblue;'>";
}
let k = parseInt(i);
if( k < data.length-1 && grp != data[k+1].grp ){
listHtml += " <table class='col-12'>";
listHtml += " <tr>";
listHtml += " <td class='col-1' style='padding:10px;'></td>";
listHtml += " <td class='col-2' style='padding:10px;'>"+ $("#login_id").val() +"</td>";
listHtml += " <td class='col-6'><input class='w-150 form-control' id='input_reply"+ grp +"' type='text' placeholder='댓글입력...' maxLength='1000'></td>";
listHtml += " <td class='col-3'><button type='button' class='btn btn-success btn-md' onclick=\"WriteReReply("+ grp +")\" >등록 </button></td>";
listHtml += " </tr>";
listHtml += " </table>";
listHtml += " <hr class='col-11' style='border: solid 1px lightblue;'>";
listHtml += " </div>";
}else if(i == data.length-1){
listHtml += " <table class='col-12'>";
listHtml += " <tr>";
listHtml += " <td class='col-1' style='padding:10px;'></td>";
listHtml += " <td class='col-2' style='padding:10px;'>"+ $("#login_id").val() +"</td>";
listHtml += " <td class='col-6'><input class='w-150 form-control' id='input_reply"+ grp +"' type='text' placeholder='댓글입력...' maxLength='1000'></td>";
listHtml += " <td class='col-3'><button type='button' class='btn btn-success btn-md' onclick=\"WriteReReply("+ grp +")\" >등록 </button></td>";
listHtml += " </tr>";
listHtml += " </table>";
listHtml += " <hr class='col-11' style='border: solid 1px lightblue;'>";
listHtml += " </div>";
}
};
// 댓글 리스트 부분에 받아온 댓글 리스트를 넣기
$(".reply-list"+ chk).html(listHtml);
// 댓글에 댓글인 경우 댓글 불러오고 해당 구역 open
if (section > 0) $("#re_reply"+ section).addClass("show");
}, error : function() {
alert('서버 에러');
}
});
}
// 답글 달기 버튼 클릭
const WriteReReply = function(grp) {
var content = $.trim($("#input_reply" + grp).val());
if(content == ""){
showModal("alertModal","글을 입력하세요!");
}else{
let mode = "parent";
if(grp > 0){ mode = "child"}
$.ajax({
url : "/board/replyInsert",
type : 'GET',
data : {
"mode" : mode,
"bno" : $("#chk_board_no").val(),
"grp" : grp,
"content" : content
},
dataType : "text",
success : function(result) {
if (result == "error"){
showModal("alertModal","ERROR!");
return false;
}else{
$("#input_reply" + grp).val("");
replyList($("#chk_board_no").val() , grp);
return false;
}
},
error : function() {
showModal("alertModal","서버 에러!");
}
});
};
};
// 댓글 삭제
const deleteReply = function(grp, no){
$.ajax({
url : '/board/replyDelete',
type : 'GET',
data : {
no : no,
grp : grp
},
dataType : "text",
success : function(result) {
if (result == "error"){
showModal("alertModal","ERROR!");
return false;
}else{
replyList($("#chk_board_no").val() , grp);
return false;
}
},
error : function() {
alert('서버 에러');
}
});
};
function boardModify(){ // 글 수정
if(submitFlag){ return false; }
submitFlag = true;
let chk_board_no = $("#chk_board_no").val();
location.href="/board/modifyPage?chk_board_no=" + chk_board_no;
setTimeout(function (){submitFlag = false;}, 3000);
}
function boardDelete(){ // 글 삭제
if(submitFlag){ return false; }
submitFlag = true;
$.ajax({
type: "GET",
enctype: 'multipart/form-data',
processData: false,
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
url: "/board/boardDelete?chk_board_no=" + $("#chk_board_no").val(),
dataType: "text",
async : false,
success: function (result) {
showModal("alertModal" , "삭제되었습니다.");
sleep2(2500).then(() => location.href="/board/listPage" );
setTimeout(function (){submitFlag = false;}, 3000);
},
error: function () {
showModal("alertModal" ,"페이지에 에러가 있습니다!");
setTimeout(function (){submitFlag = false;}, 3000);
}
});
}
BoardController
@RequestMapping("/board")
@Controller
public class BoardController extends AdminController{
@Autowired
private BoardService boardService;
/** 게시판 - 자유게시판 page 이동 */
@GetMapping("/listPage")
public ModelAndView boardPage(HttpSession session , Criteria cri , HttpServletRequest request ) throws Exception{
BoardDTO boardDTO = new BoardDTO(); // 검색 param
String search_nm = request.getParameter("search_nm");
String search_title = request.getParameter("search_title");
boardDTO.setWriter(search_nm);
boardDTO.setTitle(search_title);
String user_id = (String)session.getAttribute("user_id");
Map<String, List<MenuDTO>> menu = gnbList(user_id); // 로그인 한사람의 메뉴 및 권한 loading
Map<String,Object> map = boardService.listBoard(cri , boardDTO); // 사용자 list loading
PageMaker pageMaker = new PageMaker(cri,(Integer)map.get("total_count"));
ModelAndView mv = new ModelAndView();
mv.addObject("setMenu",menu);
mv.addObject("totalCount",map.get("total_count"));
mv.addObject("listBoard",map.get("listBoard"));
mv.addObject("pageMaker",pageMaker);
//search
if (search_nm != null) { mv.addObject("search_nm",search_nm); }
if (search_title != null) { mv.addObject("search_content",search_title); }
mv.setViewName("board/list");
return mv;
}
/** 게시판 - 자유게시판 쓰기 page 이동 */
@RequestMapping("/writePage")
public ModelAndView boardWrite(HttpSession session) throws Exception{
String user_id = (String)session.getAttribute("user_id"); // 로그인 한사람의 메뉴 및 권한 loading
Map<String, List<MenuDTO>> menu = gnbList(user_id);
ModelAndView mv = new ModelAndView();
mv.addObject("setMenu",menu);
mv.setViewName("/board/write");
return mv;
}
/** 게시판 - 자유게시판 쓰기 :: 서머노트 이미지 파일 저장 */
@RequestMapping(value="/uploadSummernoteImageFile", produces = "application/json; charset=utf8")
@ResponseBody
public String uploadSummernoteImageFile( @RequestParam("file") MultipartFile multipartFile, HttpServletRequest request ) throws Exception{
JsonObject jsonObject = new JsonObject();
/* String fileRoot = "C:\\summernote_image\\"; // 외부경로로 저장을 희망할때. */
String fileRoot = request.getServletContext().getRealPath("resources/images/board/"); // 내부경로로 저장
String originalFileName = multipartFile.getOriginalFilename(); // 오리지널 파일명
String extension = originalFileName.substring(originalFileName.lastIndexOf(".")); // 파일 확장자
final String[] ALLOW_EXTENSION = {".gif",".GIF", ".jpg",".JPG",".png",".PNG",".jepg",".JEPG"}; // 확장자 검사
if(!Arrays.asList(ALLOW_EXTENSION).contains(extension)){
jsonObject.addProperty("responseCode", "extension");
String a = jsonObject.toString();
return a;
}
String savedFileName = UUID.randomUUID() + extension; // 파일 이름이 한글로 들어왔을 때 그걸 다시 영어와 숫자로 이루어진 문자열로 만들기 UUTID
File targetFile = new File(fileRoot + savedFileName);
try {
InputStream fileStream = multipartFile.getInputStream();
FileUtils.copyInputStreamToFile(fileStream, targetFile);
// 파일 저장
jsonObject.addProperty("url", "/resources/images/board/"+ savedFileName); // contextroot + resources + 저장할 내부 폴더명
jsonObject.addProperty("responseCode", "success");
} catch (IOException e) {
FileUtils.deleteQuietly(targetFile); // 저장된 파일 삭제
jsonObject.addProperty("responseCode", "error");
e.printStackTrace();
}
String a = jsonObject.toString();
return a;
}
/** 게시판 - 자유게시판 쓰기 :: 글 저장 */
@ResponseBody
@PostMapping("/insertBoard")
public String insertBoard( HttpSession session, @ModelAttribute("boardVo") BoardDTO boardDTO) throws Exception{
String user_id = (String)session.getAttribute("user_id");
String user_nm = (String)session.getAttribute("user_nm");
boardDTO.setWriter(user_nm);
boardDTO.setWriter_id(user_id);
int idx = boardService.insertBoard(boardDTO); // write transaction
if(idx > 0){ return "success"; }
else{ return "fail"; }
}
/** 게시판 - 자유게시판 글 읽기 */
@GetMapping("/view")
public ModelAndView viewBoard( HttpSession session, @RequestParam(required=false, value="chk_board_no") int chk_board_no ) throws Exception{
String user_id = (String)session.getAttribute("user_id");
Map<String, List<MenuDTO>> menu = gnbList(user_id); // 로그인 한사람의 메뉴 및 권한 loading
BoardDTO boardDTO = new BoardDTO();
boardDTO.setIdx(chk_board_no);
boardService.boardViewUpdate(boardDTO); // 조회수 +1
BoardDTO boardList = boardService.searchBoardOne(boardDTO); // content data loading
ModelAndView mv = new ModelAndView();
mv.addObject("setMenu",menu);
mv.addObject("boardList",boardList);
mv.setViewName("/board/view");
return mv;
}
/** 게시판 - 자유게시판 글 수정 page 이동 */
@RequestMapping("/modifyPage")
public ModelAndView modifyPage(HttpSession session , @RequestParam(required=false, value="chk_board_no") int chk_board_no) throws Exception{
String user_id = (String)session.getAttribute("user_id"); // 로그인 한사람의 메뉴 및 권한 loading
Map<String, List<MenuDTO>> menu = gnbList(user_id);
BoardDTO boardDTO = new BoardDTO();
boardDTO.setIdx(chk_board_no);
BoardDTO boardList = boardService.searchBoardOne(boardDTO); // content data loading
ModelAndView mv = new ModelAndView();
mv.addObject("setMenu",menu);
mv.addObject("boardList",boardList);
mv.setViewName("/board/modify");
return mv;
}
/** 게시판 - 자유게시판 글 삭제 */
@ResponseBody
@GetMapping("/boardDelete")
public String modifyPage( @RequestParam(required=false, value="chk_board_no") int chk_board_no) throws Exception{
BoardDTO boardDTO = new BoardDTO();
boardDTO.setIdx(chk_board_no);
int result = boardService.deleteBoard(boardDTO); // delete transaction
if(result > 0){ return "success"; }
else{ return "fail"; }
}
/** 게시판 - 자유게시판 수정 :: 글 수정 */
@ResponseBody
@PostMapping("/updateBoard")
public String updateBoard( @ModelAttribute("boardVo") BoardDTO boardDTO ) throws Exception{
LocalDateTime currentDateTime = LocalDateTime.now(); // write time
boardDTO.setReq_dt(currentDateTime);
int idx = boardService.updateBoard(boardDTO);
if(idx > 0){ return "success"; }
else{ return "fail"; }
}
/** 게시판 - 자유게시판 댓글리스트 */
@ResponseBody
@GetMapping("/replyList")
public Map<String,Object> boardReplyList( @RequestParam(required=false, value="chk_board_no") int chk_board_no ) throws Exception{
BoardDTO boardDTO = new BoardDTO();
boardDTO.setIdx(chk_board_no);
List<BoardReplyDTO> boardReplyList = boardService.boardReplyList(boardDTO); // content reply loading
Map<String,Object> map = new HashMap<>();
map.put("boardReplyList",boardReplyList);
map.put("result","success");
return map;
}
/** 게시판 - 자유게시판 댓글 저장 */
@ResponseBody
@GetMapping("/replyInsert")
public String replyInsert( HttpSession session,
@RequestParam(required = false , value = "mode") String mode,
@ModelAttribute("boardReplyVO") BoardReplyDTO boardReplyDTO ) throws Exception{
boardReplyDTO.setWriter((String)session.getAttribute("user_id"));
int result = 0;
if(mode.equals("parent")){ result = boardService.parentReplyInsert(boardReplyDTO); }
else{ result = boardService.childReplyInsert(boardReplyDTO); }
if(result > 0) return "success";
else return "error";
}
/** 게시판 - 자유게시판 댓글 삭제 */
@ResponseBody
@GetMapping("/replyDelete")
public String replyDelete( @RequestParam("no") String no, @RequestParam("grp") String grp ){
int result = 0;
if (no.equals(grp)) result = boardService.parentReplyDelete(no);
else result = boardService.childReplyDelete(no);
if(result > 0) return "success";
else return "error";
}
}
BoardService
public interface BoardService {
int insertBoard(BoardDTO boardDTO);
Map<String,Object> listBoard(Criteria criteria , BoardDTO boardDTO);
BoardDTO searchBoardOne(BoardDTO boardDTO);
void boardViewUpdate(BoardDTO boardDTO);
int deleteBoard(BoardDTO boardDTO);
int updateBoard(BoardDTO boardDTO);
List<BoardReplyDTO> boardReplyList(BoardDTO boardDTO);
int parentReplyInsert(BoardReplyDTO boardReplyDTO);
int childReplyInsert(BoardReplyDTO boardReplyDTO);
int parentReplyDelete(String no);
int childReplyDelete(String no);
}
BoardServiceImpl
@Service("BoardService")
public class BoardServiceImpl implements BoardService{
@Autowired
private BoardDAO boardDAO;
@Autowired
private BoardReplyDAO boardReplyDAO;
public int insertBoard(BoardDTO boardDTO) { // 자유게시판 글 등록
return boardDAO.insertBoard(boardDTO);
}
public Map<String, Object> listBoard(Criteria criteria, BoardDTO boardDTO) { // 자유게시판 리스트
return boardDAO.listBoard(criteria , boardDTO);
}
public BoardDTO searchBoardOne(BoardDTO boardDTO) { // 선택한 게시글 상세보기
return boardDAO.searchBoardOne(boardDTO);
}
@Override
public void boardViewUpdate(BoardDTO boardDTO) { // 게시글 조회수 +1
boardDAO.boardViewUpdate(boardDTO);
}
@Override
public int deleteBoard(BoardDTO boardDTO) { // 게시글 삭제하기
return boardDAO.deleteBoard(boardDTO);
}
@Override
public int updateBoard(BoardDTO boardDTO) {
return boardDAO.updateBoard(boardDTO);
}
@Override
public List<BoardReplyDTO> boardReplyList(BoardDTO boardDTO) {
return boardReplyDAO.boardReplyList(boardDTO);
}
@Override
public int parentReplyInsert(BoardReplyDTO boardReplyDTO) { return boardReplyDAO.parentReplyInsert(boardReplyDTO); }
@Override
public int childReplyInsert(BoardReplyDTO boardReplyDTO) { return boardReplyDAO.childReplyInsert(boardReplyDTO); }
@Override
public int parentReplyDelete(String no) { return boardReplyDAO.parentReplyDelete(no); }
@Override
public int childReplyDelete(String no) { return boardReplyDAO.childReplyDelete(no); }
}
BoardReplyDAO
@Repository
public class BoardReplyDAO extends SqlSessionDaoSupport {
@Autowired
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
public List<BoardReplyDTO> boardReplyList(BoardDTO boardDTO) {
return getSqlSession().selectList("boardReplyList",boardDTO);
}
public int parentReplyInsert(BoardReplyDTO boardReplyDTO) {
SqlSession session = getSqlSessionFactory().openSession();
int rowCount = session.insert("parentReplyInert", boardReplyDTO);
int no = Integer.parseInt(boardReplyDTO.getNo());
int key = session.update("parentReplyCheck", no);
session.close();
return key;
}
public int childReplyInsert(BoardReplyDTO boardReplyDTO) {
return getSqlSession().insert("childReplyInsert",boardReplyDTO);
}
public int parentReplyDelete(String no) {
return getSqlSession().update("parentReplyDelete",no);
}
public int childReplyDelete(String no) {
return getSqlSession().update("childReplyDelete",no);
}
}
Board-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="boardConfig">
<select id="queryRowCount2" resultType="int" >
SELECT FOUND_ROWS() AS total_cnt
</select>
<select id="listBoard" parameterType="hashmap" resultType="boardConfig">
select
SQL_CALC_FOUND_ROWS
*
from
(
select
@rownum:=@rownum+1 as num
, mc.*
from board mc
,(SELECT @rownum:=0) r
where <![CDATA[ mc.idx > 0 ]]>
<if test="boardDTO.writer != null and boardDTO.writer != '' " >
<![CDATA[ and mc.writer LIKE CONCAT('%', #{boardDTO.writer}, '%') ]]>
</if>
<if test="boardDTO.title != null and boardDTO.title != '' " >
<![CDATA[ and mc.title LIKE CONCAT('%', #{boardDTO.title}, '%') ]]>
</if>
order by mc.idx desc
)ee
order by num asc
LIMIT #{startNum} , #{endNum}
</select>
<select id="searchBoardOne" parameterType="boardConfig" resultType="boardConfig" >
select * from board where idx = #{idx}
</select>
<update id="boardViewUpdate" parameterType="boardConfig" >
update board set view = (view+1) where idx = #{idx}
</update>
<update id="updateBoard" parameterType="boardConfig" >
update board set title = #{title} , content = #{content} , req_dt = #{req_dt} where idx = #{idx}
</update>
<insert id="insertBoard" parameterType="boardConfig" useGeneratedKeys="true" keyProperty="idx" >
insert
into board(writer, writer_id, title, content)
values (#{writer}, #{writer_id}, #{title}, #{content} )
<selectKey resultType="int" keyProperty="idx" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
<delete id="deleteBoard" parameterType="boardConfig" >
DELETE FROM board WHERE idx = #{idx}
</delete>
</mapper>
boardReply-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="boardReplyConfig">
<select id="boardReplyList" parameterType="boardConfig" resultType="boardReplyConfig" >
select r.no, r.bno, r.grp, r.grpl, r.writer, r.content, date_format(wdate,'%Y-%m-%d') wdate, datediff(now(), wdate) wgap
from board_reply r
where r.bno = #{idx}
order by grp asc, grps asc
</select>
<insert id="parentReplyInert" parameterType="boardReplyConfig" useGeneratedKeys="true" keyProperty="no" >
insert into board_reply (bno, grp, grpl, grps, writer, content , wdate)
values ( #{bno} , 0 , 0 , 0 , #{writer} , #{content} , now() )
<selectKey resultType="String" keyProperty="no" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
<update id="parentReplyCheck" parameterType="int" >
update board_reply set grp = #{no} where no = #{no}
</update>
<insert id="childReplyInsert" parameterType="boardReplyConfig" >
<selectKey resultType="int" keyProperty="grps" order="BEFORE">
select max(grps)+1 from board_reply where bno = #{bno} and grp = #{grp}
</selectKey>
insert into board_reply (bno, grp, grpl, grps, writer, content , wdate)
values (#{bno} , #{grp} , 1 , #{grps} , #{writer} , #{content} , now() )
</insert>
<delete id="parentReplyDelete" parameterType="String">
delete from board_reply where grp = #{no}
</delete>
<update id="childReplyDelete" parameterType="String" >
update board_reply set content = '' where no = #{no}
</update>
</mapper>
728x90