프로그래밍/- 남굼성의 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