JQuery는 자바스크립트(JavaScript - JS)를 브라우저 호환성 등을

신경 쓰지 않고 쉽게 짧은 코드로 사용할 수 있게 만든 라이브러리(위키-https://ko.wikipedia.org/wiki/JQuery)로,

가장 폭 넓게 사용되는 라이브러리라고 생각한다. 

사용법에 대한 자료가 많으니 인터넷 검색을 통해 익혀 두길 바라고

여기서는 게시판(글읽기-댓글)에 사용된 자바 스크립트를 JQuery로 변환하면서 필요한 내용만 언급하고자 한다.

https://opentutorials.org/course/53 생활코딩

http://jquery.com/

여기서 JQuery를 이용하는 이유는 코드가 간단해지는 것도 있고

동적인 웹 사이트를 만들 때 필요한 라이브러리(Tree, Grid 등)들이 JQuery를 기반으로 제공되기 때문이다.

개인적으로 JS = JQuery 라고 생각한다.


먼저, 컨트롤(Board6Ctr.java), JSP(board6)만 board7으로 복사해서 만들고

서비스(Board6Svcjava)와 sql문이 있는 board6.xml는 새로 작성하지 않는다.

JS를 JQuery로 바꾸는 것은 JSP 파일만 수정하면 된다.

Board7Ctr 컨트롤이 필요한 이유는

별도로 예제를 구성하기 위해서 이기도 하고

다음 예제가 JQuery Ajax를 사용하는 것으로

Ajax는 서버(컨트롤)와 통신하는 것이기 때문에 통신을 위한 수정이 필요하기 때문이다.

따라서, 새로운 컨트롤(Board7Ctr.java), JSP(board7)을 생성한다.

다음으로 JQuery 공식 사이트에서 적절한 버전을 다운로드 받는다.

http://jquery.com/download/

1.x와 2.x 중에 적절한 버전을 선택하여 다운 받는다.

여기서는 Internet Explorer 6, 7, or 8를 지원하지 않는 2.2.3를 다운 받았다.

https://code.jquery.com/jquery-2.2.3.min.js

다운 받은 jquery-2.2.3.min.js 파일을 webapp 폴더에 js 폴더를 만들어 보관하였다.

폴더의 위치는 원하는 위치로 하면 되는데

개인적으로 webapp/js 폴더에 모아서 사용한다.

그런 후 Spring 환경파일(webapp/WEB-INF/dispatcher-servlet.xml)에

<mvc:resources mapping="/js/**" location="/js/" />

를 추가해 주어야 한다.

그렇지 않으면 파일을 찾을 수 없다는 메시지를 받게 된다.

대부분 CSS와 이미지도 이런 식으로 모아서 사용하니 다른 자료들을 더 확인해 두길 바란다.

http://www.mkyong.com/spring-mvc/spring-mvc-how-to-include-js-or-css-files-in-a-jsp-page/

http://fromleaf.tistory.com/180


마지막으로 JQuery를 사용할 jsp파일 (BoardRead.jsp)의 head 테그에

JQuery 스크립트를 가져오는 다음 코드를 추가해 준다.

<script src="js/jquery-2.2.3.min.js"></script>



'Java > 게시판 7: JQuery' 카테고리의 다른 글

2. 변환: Javascript -> JQuery  (1) 2016.06.04
3. Ajax - 댓글 삭제  (2) 2016.06.04
4.Ajax – 댓글 작성  (0) 2016.06.04

댓글을 저장하는 fn_formSubmit() 함수를 변환한다.

현재 게시물에 대한 댓글을 작성하는 폼(form1)의 구성원인

작성자(rewriter)와 글내용(rememo)의 입력 여부를 확인하여

입력하지 않은 경우 메시지를 사용자에게 보여 주는 기능이다.

form1.rewriter.value로 작성했는데 document.form1을 form1으로지정했기 때문에

실제로는 document.form1.rewriter.value라고 사용하는 것이다.

JavaScript

function fn_formSubmit(){
    var form1 = document.form1;
   
    if (form1.rewriter.value=="") {
        alert("작성자를 입력해주세요.");
        form1.rewriter.focus();
        return;
    }
    if (form1.rememo.value=="") {
        alert("글 내용을 입력해주세요.");
        form1.rememo.focus();
        return;
    }
    form1.submit();   
}

JQuery

function fn_formSubmit(){
    if ( $.trim($("#rewriter1").val()) == "") {
        alert("작성자를 입력해주세요.");
        $("#rewriter1").focus();
        return;
    }
    if ($.trim($("#rememo1").val()) == "") {
        alert("글 내용을 입력해주세요.");
        $("#rememo1").focus();
        return;
    }
    $("#form1").submit();   
}

이 긴 문장을 $("#rewriter1")로 간단하게 사용하게 된다.

$는 JQuery 사용을 의미하고, #은 HTML 태그 속성 중 ID를 의미한다.

즉 $("#rewriter1")은 id가 rewriter1(작성자)인 HTML 태그(입력상자)를 반환하게 되고,

태그의 값(value)을 val()함수로 가져와 비었는지 여부를 확인한다.

이러한 처리를 위해서는

다음 코드처럼 입력과 관련된 모든 HTML 태그에 이름(name)과 같은 id를 부여해 줘야 한다.

그렇지 않으면 $("input[name= rewriter]")과 같이 사용하는데

간단하게 사용하기 위해 id를 사용하는 것이 좋다.

<form id="form1" name="form1" action="board7ReplySave" method="post">
    <input type="hidden" id="brdno1" name="brdno" value="<c:out value="${boardInfo.brdno}"/>">
    <input type="hidden" id="reno1" name="reno">
    작성자: <input type="text" id="rewriter1" name="rewriter" size="20" maxlength="20"> <br/>
    <textarea id="rememo1" name="rememo" rows="3" cols="60" maxlength="500" placeholder="댓글을 달아주세요."></textarea>
    <a href="#" onclick="fn_formSubmit()">저장</a>
</form>

BoardRead.jsp

JS도 document.getElementById를 이용하여 id로 식별하여 사용하지만

입력상자(Textbox)와 관련된 처리를 할 때는 개인적으로 간편하게 사용하기 위해

JS는 태그의 이름(ex:form1.rewriter.value)으로 식별하고 JQuery를 사용할 때는 id로 식별하여 사용한다.


form2, form3도 모두 이와 같이 id와 name에 값을 부여해서

JS(JQuery)에서는 id로 인식하여 처리하고,

서버로 값을 전달할 때는 name으로 인식한다.

<div id="replyDiv" style="width: 99%; display:none">
    <form id="form2" name="form2" action="board7ReplySave" method="post">
        <input type="hidden" id="brdno2" name="brdno" value="<c:out value="${boardInfo.brdno}"/>">
        <input type="hidden" id="reno2" name="reno">
        <textarea id="rememo2" name="rememo" rows="3" cols="60" maxlength="500"></textarea>
        <a href="#" onclick="fn_replyUpdateSave()">저장</a>
        <a href="#" onclick="fn_replyUpdateCancel()">취소</a>
    </form>
</div>

<div id="replyDialog" style="width: 99%; display:none">
    <form id="form3" name="form3" action="board7ReplySave" method="post">
        <input type="hidden" id="brdno3" name="brdno" value="<c:out value="${boardInfo.brdno}"/>">
        <input type="hidden" id="reno3" name="reno">
        <input type="hidden" id="reparent3" name="reparent">
        작성자: <input type="text" id="rewriter3" name="rewriter" size="20" maxlength="20"> <br/>
        <textarea id="rememo3" name="rememo" rows="3" cols="60" maxlength="500"></textarea>
        <a href="#" onclick="fn_replyReplySave()">저장</a>
        <a href="#" onclick="fn_replyReplyCancel()">취소</a>
    </form>
</div>

BoardRead.jsp

form2, form3의 입력 상자들에 ID를 바꿀 때 주의 할 것이 있다.

이름과 동일한 id를 주면 id는 문서 전체(document)에서 고유하게 식별되는 것이고

폼(form)이 있는 경우 이름은 폼 내부에서 고유하게 식별된다.

따라서 같은 이름이 있어도 폼이 다르면 다르게 인식되지만

ID는 같게 인식되어 이름과 동일하게 주면 여러 개의 입력상자가 선택되게 되어 오류를 발생시킨다.

따라서 폼이 form2면 id도 brdno2처럼 부여 하여 사용한다.

이름은 그대로 brdno로 지정하여 서버로 전송 되게 한다.

$.trim() 함수는 문자열의 좌우 공백을 제거해 주는 JQuery 함수로 공백만 넣고 입력하는 글을 막기 위해 사용하였다.


댓글을 삭제하는 fn_replyDelete() 함수에서는 HTML 태그에 속성(attribute)을 지정하는 방법을 알 수 있다.

JS에서는 action 속성에 값을 지정해서 넣어주고 (form.action="board6ReplyDelete")

JQuery에서는 속성 함수 (attr) 를 호출해서 지정한다 ($("#form2").attr("action", "board7ReplyDelete")).


fn_replyUpdate() 함수에서는 다음과 같이 이름으로 식별하고(form), id로 식별하는 (getElementById) 방식을 같이 사용했다.

    var form = document.form2;

    var reply = document.getElementById("reply"+reno);

    var replyDiv = document.getElementById("replyDiv");

div 태그는 id로 인식해서 사용하는데,

두 방식 모두 필요한 태그를 찾아서 변수에 담아서 사용한다.

JQuery에서도 동일하게 사용하는 게 좋지만 복잡하게 사용하는 것이 아니라서

변수에 담아서 사용하지 않고 직접 호출해서 사용하지만

reply = $("#reply"+reno)의 경우에는 매번 ID를 조합해서 찾는 것이 무리라고 여겨서 변수에 담아서 사용했다.

div 태그를 보이고(replyDiv.style.display = "") 숨기는 (replyDiv.style.display = "none") 방법도

개발자에게 직관적인 show, hide 함수로 치리된다.

입력 박스(Text)는 값(value)을 가지기 때문에 JQuery의 val함수를 사용하고

div 태그는 innerText나 innerHTML 속성으로 값을 다루고, JQuery는 text와 html 함수로 호출해서 사용한다.


이하의 함수들도 이와 같은 규칙에 의해 변환되었기 때문에 더 이상의 설명이 필요 없을 것 같아서 생략한다.

여기서는 JQuery의 장점을 제대로 표현하지 못했고 단지 JS와 비교하며 초보 수준에서 다루었다.




'Java > 게시판 7: JQuery' 카테고리의 다른 글

1. JQuery 사용 기초  (0) 2016.06.04
3. Ajax - 댓글 삭제  (2) 2016.06.04
4.Ajax – 댓글 작성  (0) 2016.06.04

Ajax (Asynchronous JavaScript and XML)는

웹 브라우저와 웹 서버가 통신을 하여 서버에서 반환된 결과를

전체 웹 페이지의 로딩 없이 서비스를 사용할 수 있게 한다.

즉, 현재 구현한 댓글은 사용자가 하나의 댓글을 달면

댓글 내용이 웹 서버로 전송되고(submit)

웹 서버가 데이터베이스에 데이터를 저장한 후,

모든 댓글을 리스트로 가져와서 웹 페이지를 구성하여 클라이언트(웹 브라우저)에게 전달한다.

정리하면 하나의 댓글을 저장하고 현재 페이지를 다시 읽어와서 보여 주는 것이다.

이 경우 네트워크로 주고 받는 데이터 양도 많고 (속도 저하 문제)

웹 브라우저가 다시 HTML을 해석해서 보여주는(CPU, 메모리 사용) 낭비를 하게 된다.

이러한 낭비를 줄이고자

수정된 내용만 서버로 보내고,

수정된 내용만 클라이언트로 보내서 반영하는 방법을 사용하게 된다.

데이터 베이스에 댓글이 잘 저장되었으면

추가된 댓글만 화면에 적절하게 보여 주면 되고

이때 사용하는 기술을 Ajax라고 한다.

자세한 내용은 다음 생활 코딩에서 확인하여 익혀두길 바란다.

https://opentutorials.org/course/1375/6843
https://opentutorials.org/course/1375/6851


앞서 처리한 Ajax를 사용하지 않은 JQuery처리와

지금부터 처리하는 Ajax를 사용한 JQuery처리를 구별하기 위해

Ajax를 사용하지 않은 JQuery는 BoardRead,jsp

Ajax를 사용한 JQuery는 BoardReadAjax,jsp파일에 따로 구현했다.

글읽기 컨트롤에서 다음과 같이 호출파일 명을 바꾸어주면 확인할 수 있다. 

    @RequestMapping(value = "/board7Read")
    public String board7Read(HttpServletRequest request, ModelMap modelMap) {
       
        String brdno = request.getParameter("brdno");
       
        boardSvc.updateBoard6Read(brdno);
        BoardVO boardInfo = boardSvc.selectBoardOne(brdno);
        List<?> listview = boardSvc.selectBoard6FileList(brdno);
        List<?> replylist = boardSvc.selectBoard6ReplyList(brdno);
       
        modelMap.addAttribute("boardInfo", boardInfo);
        modelMap.addAttribute("listview", listview);
        modelMap.addAttribute("replylist", replylist);
       
        return "board7/BoardRead";
        //return "board7/BoardReadAjax";

    }

Board7Ctr.java

먼저 가장 쉬운 예로 댓글 삭제를 바꾸어 본다.

기존 코드는 form 태그(form2)의 action에 삭제 컨트롤 주소를 넣고 (board7ReplyDelete),

삭제할 댓글번호를 파라메터로 설정해서 전송(submit)하면 된다.

function fn_replyDelete(reno){
    if (!confirm("삭제하시겠습니까?")) {
        return;
    }
    $("#form2").attr("action", "board7ReplyDelete");
    $("#reno2").val(reno);
    $("#form2").submit();   
}

BoardRead.jsp

JQuery Ajax로 삭제하려면 다소 복잡한 처리가 필요하다.

다음 코드와 같이 데이터 전송을 위해 필요한 정보(파라미터)는

키와 값으로 구성된 JSON 방식으로 설정한다.

function fn_replyDelete(reno){
    if (!confirm("삭제하시겠습니까?")) {
        return;
    }
    $.ajax({
        url: "board7ReplyDeleteAjax",
        type:"post",
        data: {"brdno": $("#brdno").val(), "reno": reno},
        success: function(result){
            if (result=="OK") {
                $("#replyItem"+reno).remove();
                alert("삭제되었습니다.");
            } else{
                alert("댓글이 있어서 삭제할 수 있습니다.");
            }
        }
    })
}

BoardReadAjax.jsp

각 Ajax의 파라미터 키에 대한 설명은 다음 표와 같다.


설명

 url

 컨트롤 주소

 type

 폼 데이터 전송 타입(get/post)

 data

 전송하려는 데이터를 다시 JSON으로 지정

 success

 서버에서 처리가 완료된 후 반환된 값을 처리할 콜백 함수

전송할 데이터(data)는

현재 읽고 있는 게시 글번호(brdno)와 삭제하고자 하는 댓글 번호(reno)를

JSON 형식({})으로 만들어서,

삭제한 컨트롤 주소(url)로 board7ReplyDeleteAjax를 지정하여 실행한다. 


board7ReplyDeleteAjax는 기존의 board7ReplyDelete와 같은 코드를 가지는 컨트롤인데

지정된 댓글을 삭제한 후

board7ReplyDelete는 글읽기로 가서(redirect) 댓글 리스트를 다시 화면에 출력하게 구현하였고

board7ReplyDeleteAjax는 삭제 후 성공 여부를 반환한다(response.getWriter()).

    @RequestMapping(value = "/board7ReplyDelete")
    public String board7ReplyDelete(BoardReplyVO boardReplyInfo) {
       
        if (!boardSvc.deleteBoard6Reply(boardReplyInfo.getReno()) ) {
            return "board7/BoardFailure";
        }
        return "redirect:/board7Read?brdno=" + boardReplyInfo.getBrdno();
    }
   
    @RequestMapping(value = "/board7ReplyDeleteAjax")
    public void board7ReplyDeleteAjax(HttpServletResponse response, BoardReplyVO boardReplyInfo) {
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json;charset=UTF-8");
       
        try {
            if (!boardSvc.deleteBoard6Reply(boardReplyInfo.getReno()) ) {
                response.getWriter().print(mapper.writeValueAsString("Fail"));
            } else {
                response.getWriter().print(mapper.writeValueAsString("OK"));
            }
        } catch (IOException ex) {
            System.out.println("오류: 댓글 삭제에 문제가 발생했습니다.");
        }
    }

Board7Ctr.java

컨트롤에서 반환하는 값도 JSON방식으로 반환되는 것으로

위 코드는 자주 사용되는 형식이니 잘 기억해 두어야 한다.

이 코드는 성공(OK), 실패(Fail)이라는 문자열을 반환한다.


마지막으로 success 키에 적용된 콜백 함수는

Ajax로 전송되어 처리가 완료되면 실행되는 함수로

서버의 컨트롤에서 반환된 성공(OK), 실패(Fail)를 함수의 파라미터 변수인 result로 받아서 처리한다.

result값이 OK이면 성공메시지를, 아니면 실패 메시지를 사용자에게 보여준다(alert).

추가적으로 성공일 때는

현재 삭제한 댓글에 해당하는 DIV를 찾아서($("#replyItem"+reno)) 삭제함으로써(remove)

사용자에게 삭제가 제대로 된 것으로 보여지게 만들어야 한다.

즉, Ajax는 기존 방식과 다른 것이

전체 데이터를 처음부터 다시 읽어와서 화면에 출력하는 것이 아니라

해당 데이터만 서버에서 삭제하고,

사용자 화면에서도 지움으로써

네트워크로 주고받는 데이터를 최소화하고 서버와 클라이언트의 처리를 줄여주는 것이다.

글 수정(fn_replyUpdateSave)도 비슷한 코드로 구성되어 있으니

복습차원에서 확인하면 이해에 도움이 될 것이다.


정리하면

Ajax를 이용한 댓글 삭제는 서버에 삭제할 정보(JSON)를 전송하고

서버에서 삭제가 잘 진행된 뒤 (DELETE)

반환된 값이 OK이면 해당 댓글 DIV를 삭제(remove)하면 된다.






'Java > 게시판 7: JQuery' 카테고리의 다른 글

1. JQuery 사용 기초  (0) 2016.06.04
2. 변환: Javascript -> JQuery  (1) 2016.06.04
4.Ajax – 댓글 작성  (0) 2016.06.04

본 예제에는 두 가지의 댓글 작성법이 있는 데,

JS(Ajax)에 대한 보다 깊은 이해를 위해 다른 방식으로 구현했다.

즉, 기존(board6)에는 게시물에 작성하는 댓글과 댓글에 대한 댓글을 구분하여 처리하였다.

두 방식 모두 처리하는 코드는 유사해서 하나로 할 수도 있지만

이해를 위해 다르게 구현했다.

차이는 저장한 데이터를 서버에 저장한 후 사용자에게 보여주는 방식에 있다.


먼저 게시물에 대한 댓글은 작성한 댓글을 서버에 저장하고,

반환 받은 값(JSON: 댓글번호 + 서버로 전송한 값)을 적절하게 구성해서

화면 출력에 필요한 모든 구성(HTML 태그)들을 모두 JS (JQuery)로 동적으로 생성한다.

이 방법은 꼭 필요한 데이터만 주고 받지만 제법 수준 있는 JS 실력을 필요로 하고

작성된 코드도 복잡하다.


또 다른 방법은 서버에서 저장 후 반환될 때 HTML로 잘 정리된 값을 만들고

클라이언트에서는 그냥 화면에 출력만 해주면 되는 방식이다.

기존 코드
function fn_formSubmit(){
    if ( $.trim($("#rewriter1").val()) == "") {
        alert("작성자를 입력해주세요.");
        $("#rewriter1").focus();
        return;
    }
    if ($.trim($("#rememo1").val()) == "") {
        alert("글 내용을 입력해주세요.");
        $("#rememo1").focus();
        return;
    }
    $("#form1").submit();   
}

Ajax
function fn_formSubmit(){
    if ( $.trim($("#rewriter1").val()) == "") {
        alert("작성자를 입력해주세요.");
        $("#rewriter1").focus();
        return;
    }
    if ($.trim($("#rememo1").val()) == "") {
        alert("글 내용을 입력해주세요.");
        $("#rememo1").focus();
        return;
    }
    $.ajax({
        url: "board7ReplySaveAjax",
        type:"post",
        data: {"brdno": $("#brdno1").val(), "rewriter": $("#rewriter1").val(), "rememo": $("#rememo1").val()},
        success: function(result){
            if (result!=="") {
                var div = $("<div>");
                div.attr("id", "replyItem" + result);
                div.appendTo($("#replyList"));
                div.css({border: "1px solid gray", width: "600px", "padding": "5px", "margin-top": "5px", "margin-left": "0px", display:"inline-block"});
                div.text($("#rewriter1").val() + " 방금" );
               
                $("<a>",{
                    text: "삭제",
                    href: "#",
                    click: function (){fn_replyDelete(result)}
                }).appendTo(div);
               
                $("<a>").attr("href", "#").text("수정").click(function (){fn_replyUpdate(result)}).appendTo(div);
               
                var href = $("<a>");
                href.attr("href", "#");
                href.text("댓글");
                href.click(function (){fn_replyReply(result)});
                href.appendTo(div);
                var reply=$("<div>").appendTo(div);
                reply.attr("id", "reply" + result);
                reply.html($("#rememo1").val());
                $("#rewriter1").val("");
                $("#rememo1").val("");
                alert("저장되었습니다.");
            } else{
                alert("서버에 오류가 있어서 저장되지 않았습니다.");
            }
        }
    })   
}

BoardReadAjax.jsp

이상의 코드에 보이는 것처럼

기존에는 폼 태그에 대하여 submit만 진행하면 됐다.

Ajax로 변환한 코드는 코드 양에서부터 부담스럽고 복잡해 보인다.

즉, submit을 할 때는 웹 브라우저가 알아서 데이터를 정리해서 보내는데

Ajax는 삭제에서 다루었지만 전송할 값들을 개발자가 모두 지정해야 하는 불편함이 있다.

(이 이유를 포함해서 이런 저런 이유로

Ajax 사용은 꼭 필요한 곳에만 적용해야 유지보수를 쉽게 할 수 있다.

어떤 웹 사이트는 Ajax로 도배가 되어 있는데 사용하기도, 개발/유지 보수하기도 힘들뿐이라고 생각한다.)


댓글 작성시 필요한 게시글 번호(brdno), 작성자(rewriter1), 댓글내용(rememo1)을 data로 구성해 준다.

댓글 번호(reno)는 서버에 저장 후 반환 되는 값이니 전달하지 않는다.

서버 주소(url)은 board7ReplySaveAjax

삭제와 같이 기존 컨트롤 이름(board7ReplySave)과 유사하게 작성하였다

    @RequestMapping(value = "/board7ReplySave")
    public String board7ReplySave(HttpServletRequest request, BoardReplyVO boardReplyInfo) {
       
        boardSvc.insertBoardReply(boardReplyInfo);

        return "redirect:/board7Read?brdno=" + boardReplyInfo.getBrdno();
    }
   
    @RequestMapping(value = "/board7ReplySaveAjax")
    public void board7ReplySaveAjax(HttpServletResponse response, BoardReplyVO boardReplyInfo) {
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json;charset=UTF-8");
       
        boardSvc.insertBoardReply(boardReplyInfo);
       
        try {
            response.getWriter().print( mapper.writeValueAsString(boardReplyInfo.getReno()));
        } catch (IOException ex) {
            System.out.println("오류: 댓글 저장에 문제가 발생했습니다.");
        }
    }

Board7Ctr.java

이상의 코드에서 확인되지만 삭제와 유사하다.

차이점은 반환 값이 저장하면서 부여된 댓글 번호(reno)라는 것이다.

클라이언트에서 전송 받은 클래스(boardReplyInfo)를 같이 넘겨도 되지만

클라이언트에서 전송한 값은 클라이언트에 있고 최소값만 네트워크로 이동하고자

클라이언트에 없는 댓글 번호만 반환했다.


success에서 반환 값을 result로 받아서

값이 없으면 오류 메시지를,

값이 있으면 사용자에게 보여줄 화면 구성을 하게 된다.

여기서는 사용자에게 보여줄 화면 구성 코드에 대한 상세한 설명은 하지 않을 것이다.

이러한 개발 방법이 있다는 것만 기억하고

가급적 다음 방법으로 개발하는 것이 좋기 때문이다.


success에 작성된 코드는

다음 코드와 같이 댓글 리스트를 생성하는 코드에서

리스트 중 하나의 댓글에 대한 코드를(forEach를 제외한 코드) 생성 하는 JS 코드이다.

<div id="replyList">
    <c:forEach var="replylist" items="${replylist}" varStatus="status">
        <div id="replyItem<c:out value="${replylist.reno}"/>"
             style="border: 1px solid gray; width: 600px; padding: 5px; margin-top: 5px; margin-left: <c:out value="${20*replylist.redepth}"/>px; display: inline-block">    
            <c:out value="${replylist.rewriter}"/> <c:out value="${replylist.redate}"/>
            <a href="#" onclick="fn_replyDelete('<c:out value="${replylist.reno}"/>')">삭제</a>
            <a href="#" onclick="fn_replyUpdate('<c:out value="${replylist.reno}"/>')">수정</a>
            <a href="#" onclick="fn_replyReply('<c:out value="${replylist.reno}"/>')">댓글</a>
            <br/>
            <div id="reply<c:out value="${replylist.reno}"/>"><c:out value="${replylist.rememo}"/></div>
        </div><br/>
    </c:forEach>
</div>

BoardReadAjax.jsp


즉 HTML을 JS를 이용하여 동적으로 생성하는 것이라 이상과 같이 복잡한 것이다.

개인적이 발전을 위해서는 이상의 두 코드(JS/HTML)을 비교해서 익혀두면 좋을 것이다.


이러한 복잡한 코드를 서버에서 HTML로 처리해서 반환 받으면 쉽게 처리 할 수 있다.

먼저, 다음과 같이 board7ReplySaveAjax4Reply 컨트롤을 만든다.

    @RequestMapping(value = "/board7ReplySaveAjax4Reply")
    public String board7ReplySaveAjax4Reply(BoardReplyVO boardReplyInfo, ModelMap modelMap) {
       
        boardSvc.insertBoardReply(boardReplyInfo);

        modelMap.addAttribute("replyInfo", boardReplyInfo);
       
        return "board7/BoardReadAjax4Reply";
    }

기존 코드와 다른 점은 modelMap으로 반환 값을 주고 JSP 파일을 지정한다는 것이다.

기존에는 저장 후

  • 게시글 전체를 다시 읽어오거나

return "redirect:/board7Read?brdno=" + boardReplyInfo.getBrdno();

  • Ajax로 키 값만 전송해서 클라이언트에서 해당 글만 생성

response.getWriter().print( mapper.writeValueAsString(boardReplyInfo.getReno()));

했는데,

이번 예제는 Ajax를 기존 MVC 예제처럼 댓글에 대한 정보를

JSP(BoardReadAjax4Reply.jsp)로 넘겨서 처리한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>
<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>

<div id="replyItem<c:out value="${replyInfo.reno}"/>"
     style="border: 1px solid gray; width: 600px; padding: 5px; margin-top: 5px; margin-left: <c:out value="${20*replyInfo.redepth}"/>px; display: inline-block">   
    <c:out value="${replyInfo.rewriter}"/> <c:out value="${replyInfo.redate}"/>
    <a href="#" onclick="fn_replyDelete('<c:out value="${replyInfo.reno}"/>')">삭제</a>
    <a href="#" onclick="fn_replyUpdate('<c:out value="${replyInfo.reno}"/>')">수정</a>
    <a href="#" onclick="fn_replyReply('<c:out value="${replyInfo.reno}"/>')">댓글</a>
    <br/>
    <div id="reply<c:out value="${replyInfo.reno}"/>"><c:out value="${replyInfo.rememo}"/></div>
</div><br/>

BoardReadAjax4Reply.jsp

BoardReadAjax4Reply.jsp 파일의 내용은 앞서 댓글 리스트에서 사용된 HTML 코드를 그대로 사용한 것이다.

댓글 리스트에서는 forEach문을 사용하여 List를 처리했고

여기서는 하나의 댓글이므로 replyInfo(BoardReplyVO)로 하나의 클래스가 사용되었다.

function fn_replyReplySave(){
    if ( $.trim($("#rewriter3").val()) == "") {
        alert("작성자를 입력해주세요.");
        $("#rewriter3").focus();
        return;
    }
    if ($.trim($("#rememo3").val()) == "") {
        alert("글 내용을 입력해주세요.");
        $("#rememo3").focus();
        return;
    }
   
    var formData = $("#form3").serialize();
    $.ajax({
        url: "board7ReplySaveAjax4Reply",
        type:"post",
        data : formData,
        success: function(result){
            if (result!=="") {
                var parent = $("#reparent3").val();
                $("#replyItem"+parent).after(result);
                $("#replyDialog").hide();
                alert("저장되었습니다.");
            } else{
                alert("서버에 오류가 있어서 저장되지 않았습니다.");
            }
        }
    })       
}


서버에서 작성된 HTML이 result 변수에 담겨서 success의 콜백 함수를 호출한다.

클라이언트에서는 부모 댓글을 찾아서($("#replyItem"+parent)) 부모댓글 다음(after)에

result 값을 넣어주면 자동으로(? – JS/JQuery의 장점) 신규 댓글이 생성되어 보여지게 된다.

부모 댓글을 찾기 위해 댓글 항목을 출력할 때,

하나의 댓글을 나타내는 DIV 에 "replyitem"와 댓글 번호를 이용한 고유 이름(ID)을 부여 했다.


서버로 데이터를 전송하기 위해 JSON 데이터를 생성할 때도 하나씩 키와 값을 지정하지 않고

JQuery에서 제공하는 serialize를 이용했다.

serialize는 Form ID만 지정해주면 자동으로 JSON data가 생성된다.


정리하면

JQuery Ajax를 사용할 때

댓글 삭제나 수정처럼 JSON으로 데이터를 전송하고 받아서 처리하기도 하고

댓글 저장(들)처럼 HTML로 받아서 처리할 때가 있다.

잘 선택해서 사용하면 빠르고 안정적인 사이트를 구축할 수 있을 것이다.

'Java > 게시판 7: JQuery' 카테고리의 다른 글

1. JQuery 사용 기초  (0) 2016.06.04
2. 변환: Javascript -> JQuery  (1) 2016.06.04
3. Ajax - 댓글 삭제  (2) 2016.06.04

댓글에 대한 댓글을 작성하는 것을 무한 댓글, 계층형 댓글, 대댓글 이라고 한다.

이 무한 댓글을 개발하기 위해서는 3가지 필드가 필요하다.


먼저, 부모를 표시하는 필드(reparent),

현재 댓글이 게시물을 기준으로 어느 정도 떨어져 있는지,

즉 사람으로 치면 이순신 장순의 몇 대손인지를 나타내는 깊이(redepth),

게시물을 기준으로 몇 번째 글인지를 나타내는 순서(reorder) 필드가 필요하다.

부모 필드(reparent)는 당연이 필요한 것으로 여겨지지만

없어도 기능적인 문제는 발생하지 않는다.

부모 댓글을 삭제 할 때 자식 댓글을 삭제하지 않으면

엉뚱한 댓글에 붙어서 나오게 된다.

자식까지 삭제하기 위해 부모 댓글 필드가 필요한 것이다.

깊이(redepth)는 트리 구조로 보여주기 위해 필요한 필드이다.

부모 댓글 보다 조금 더 들여쓰기 하여 트리처럼 보여 주게 된다.

가장 이해가 어렵고 중요한 필드는 댓글들의 순서를 의미하는 순서(reorder) 필드이다.


하나의 게시물에 2개의 댓글이 달렸다고 하면

댓글 번호(reno)와 순서(reorder)는 1, 2번 일 것이다.

이 상태에서 1번 댓글에 댓글을 달면 새로운 댓글의 댓글 번호는 3이 되지만 순서는 2가 되어야 한다.

그리고, 댓글 번호 2의 순서는 3이 되어야 한다.

그래야지만 순서대로 화면에 출력되어 보여지게 된다.


더 예를 들면,

댓글 번호 1번에 다시 댓글을 달면

새 댓글은 댓글 번호 4가 되고 순서는 2가 된다.

댓글 번호 3가 되고 순서는 3가 되고

댓글 번호 2가 되고 순서는 4가 된다.


즉, 중간에 값이 들어오기 때문에 순서가 하나씩 증가하게 되는 것이다.

새로운 댓글은

기존 댓글 번호의 최대값에 1 더한 값을 가지지만

순서는 부모 댓글의 순서 값에 1 더한 값을 가지고,

부모 댓글의 순서 값보다 큰 값들은 모두 1씩 더한 값을 가지게 된다.



개념을 이해했으면

다음 SQL문을 실행하고 BoardReplyVO에 변수 reparent(부모), redepth(깊이), reorder(순서)를 추가한다.

public class BoardReplyVO {
    private String brdno;
    private String reno;
    private String rewriter;
    private String redeleteflag;
    private String rememo;
    private String redate;
    private String reparent;
    private String redepth;
    private Integer reorder;

   
    ~~ 생략 ~~

    public String getReparent() {
        return reparent;
    }
   
    public void setReparent(String reparent) {
        this.reparent = reparent;
    }

    public String getRedepth() {
        return redepth;
    }

    public void setRedepth(String redepth) {
        this.redepth = redepth;
    }

    public Integer getReorder() {
        return reorder;
    }

    public void setReorder(Integer reorder) {
        this.reorder = reorder;
    }
   
}

BoardReplyVO.java


다음 SQL문을 직접 실행해서 필드를 추가해 주고 (ALTER ),

기존 댓글의 데이터에 값을 넣어준다(UPDATE).

ALTER TABLE TBL_BOARDREPLY ADD(
      REPARENT INT(11),
      REDEPTH INT,
      REORDER INT
);

UPDATE TBL_BOARDREPLY SET REPARENT=RENO, REDEPTH=0, REORDER=RENO;



'Java > 게시판 6: 무한댓글' 카테고리의 다른 글

2. 무한 댓글 리스트  (11) 2016.05.15
3. 무한 댓글 쓰기 – control  (3) 2016.05.15
4. 무한 댓글 쓰기 – JSP  (0) 2016.05.15
5. 무한 댓글 삭제  (1) 2016.05.15
6. 무한 댓글 삭제 II  (0) 2016.05.15
댓글의 댓글은 계층형 구조를 가지고 저장되고

화면에 출력될 때도 계층형(트리 구조)으로 출력된다.

이 계층형 구조는 부모 글과 자식 글을 구분하기 위한 것으로

자식 글을 부모 글 보다 뒤로 들여쓰기 해서 다음 그림과 같이 트리(?)처럼 표현 할 수 있다.


여기서는 하나의 댓글을 둘러싼 박스(DIV)를 뒤로 물러나게 만들었다.

댓글 박스를 만드는 DIV의 좌표 값을(margin-left )값을 20px씩 더 주면 들여쓰기 형태로 구현된다.

즉, 댓글의 깊이(redepth)값에 20씩 곱하여 계산한다.

기존의 코드에 다음 코드를 추가하여 간단하게 처리하였다.

<c:forEach var="replylist" items="${replylist}" varStatus="status">
    <div style="border: 1px solid gray; width: 600px; padding: 5px; margin-top: 5px;
          margin-left: <c:out value="${20*replylist.redepth}"/>px; display: inline-block">   
        <c:out value="${replylist.rewriter}"/> <c:out value="${replylist.redate}"/>
        <a href="#" onclick="fn_replyDelete('<c:out value="${replylist.reno}"/>')">삭제</a>
        <a href="#" onclick="fn_replyUpdate('<c:out value="${replylist.reno}"/>')">수정</a>
        <a href="#" onclick="fn_replyReply('<c:out value="${replylist.reno}"/>')">댓글</a>
        <br/>
        <div id="reply<c:out value="${replylist.reno}"/>"><c:out value="${replylist.rememo}"/></div>
    </div><br/>
</c:forEach>





'Java > 게시판 6: 무한댓글' 카테고리의 다른 글

1. 무한 댓글 시작하기  (14) 2016.05.15
3. 무한 댓글 쓰기 – control  (3) 2016.05.15
4. 무한 댓글 쓰기 – JSP  (0) 2016.05.15
5. 무한 댓글 삭제  (1) 2016.05.15
6. 무한 댓글 삭제 II  (0) 2016.05.15

댓글을 저장하기 위해서는 2가지를 고려해야 한다.

먼저, 게시물에 대하여 댓글을 달은 경우와 댓글에 댓글을 달은 경우이다.

게시물에 대하여 댓글을 달은 경우는 부모가 없는 경우로 깊이(redepth)가 0이된다.

개발자에 따라 차이가 있지만 부모가 없다는 걸 의미하기 위해 깊이를 0으로 주고

그 외에 reparent 값을 0으로 주거나 reparent 값을 reno로 준다.

여기에서는 reparent 값을 reno로 주었다.

순서(reorder)값은 게시물 내에서 최대값을(selectBoard6ReplyMaxOrder)을 가지게 된다.


댓글에 댓글을 달은 경우는

부모 댓글의 정보(selectBoard6ReplyParent)를 가지고 와서

깊이(redepth)는 부모 댓글에 1을 더한 값을 가지고

순서(reorder)도 부모 순서에 1 더한 값을 가진다.

부모(reparent)는 부모 댓글의 댓글 번호(reno)를 지정한다.

여기서는 깊이는 Java에서 1 더했고, 순서는 SQL에서 1을 더했다.

어느 쪽에서 처리해도 되지만 개인적으로 SQL에서 처리하는 것을 선호한다.

    public void insertBoardReply(BoardReplyVO param) {
        if (param.getReno() == null || "".equals(param.getReno())) {
            if (param.getReparent() != null) {
                BoardReplyVO replyInfo = sqlSession.selectOne("selectBoard6ReplyParent", param.getReparent());
                param.setRedepth(replyInfo.getRedepth());
                param.setReorder(replyInfo.getReorder() + 1);
                sqlSession.selectOne("updateBoard6ReplyOrder", replyInfo);
            } else {
                Integer reorder = sqlSession.selectOne("selectBoard6ReplyMaxOrder", param.getBrdno());
                param.setReorder(reorder);
            }
           
            sqlSession.insert("insertBoard6Reply", param);
        } else {
            sqlSession.insert("updateBoard6Reply", param);
        }
    }

Board6Svc.java

   <select id="selectBoard6ReplyParent" resultType="gu.board6.BoardReplyVO" parameterType="String">
        SELECT BRDNO, REDEPTH+1 REDEPTH, REORDER
          FROM TBL_BOARDREPLY
         WHERE RENO=#{reparent}
    </select>
    <select id="selectBoard6ReplyMaxOrder" resultType="Integer" parameterType="String">
        SELECT IFNULL(MAX(REORDER),0)+1
          FROM TBL_BOARDREPLY
         WHERE BRDNO=#{brdno}
    </select>
   <update id="updateBoard6ReplyOrder" parameterType="gu.board6.BoardReplyVO">
        UPDATE TBL_BOARDREPLY
           SET REORDER = REORDER + 1
         WHERE BRDNO=#{brdno} AND REORDER>#{reorder}      
    </update>

board6.xml


마지막으로 저장하기 전에

updateBoard6ReplyOrder를 실행해서 부모 댓글의 순서(reorder)보다 큰 값들을 1씩 증가 시켜야 한다.

이유는 기본 개념에서 설명했다.


저장(INSERT)은 다음과 같이 바꾸어야 한다.

    <insert id="insertBoard6Reply" parameterType="gu.board6.BoardReplyVO" >
        <selectKey resultType="String" keyProperty="reno" order="BEFORE">
            SELECT IFNULL(MAX(RENO),0)+1 FROM TBL_BOARDREPLY
        </selectKey>
   
        INSERT INTO TBL_BOARDREPLY(BRDNO, RENO, REWRITER, REDELETEFLAG, REMEMO, REDATE, REORDER, REPARENT, REDEPTH)
        VALUES (#{brdno}, #{reno}, #{rewriter}, 'N', #{rememo}, NOW(), #{reorder},
                   <choose>
                       <when test="reparent==null">#{reno}, 0</when>
                       <otherwise>#{reparent}, #{redepth}</otherwise>
                   </choose>
               )
    </insert>

board6.xml

순서는 자바에서 처리해서 넘어오기 때문에 그대로 저장한다.

다만, 부모 댓글과 깊이는

게시물에 대한 댓글일 경우(부모가 없는 경우, reparent==null)에는 자기 번호와 0을 주고

댓글의 댓글일 경우는 Java에서 처리된 값을 가지게 된다.

이러한 처리도 Java에서 처리하면 되지만 여기서는 MyBatis에서 처리했다.

개발자의 선택사항이고 이 경우에는 개인적으로는 자바에서 처리하는 걸 선호한다.


수정은 기존 코드 그대로 지정된 댓글번호에 대하여 UPDATE문을 실행한다.


'Java > 게시판 6: 무한댓글' 카테고리의 다른 글

1. 무한 댓글 시작하기  (14) 2016.05.15
2. 무한 댓글 리스트  (11) 2016.05.15
4. 무한 댓글 쓰기 – JSP  (0) 2016.05.15
5. 무한 댓글 삭제  (1) 2016.05.15
6. 무한 댓글 삭제 II  (0) 2016.05.15

댓글의 댓글을 쓰는 화면을 위해서는

게시물에 다는 첫 댓글 작성과 같이 별도의 화면과 4개의 자바스크립트 함수가 필요하다.

기존의 첫 댓글화면과 유사한 구조이지만 다른 이름을 부여 해서

다음과 같이 별도의 화면(replyDialog와 form3)을 만들어야 한다.

폼의 컨트롤은 댓글이 달리는 게시물번호(brdno), 부모글번호(reparent), 수정을 위해 자기 댓글번호(reno)가 숨져진 필드로 저장되고,

사용자가 입력하는 댓글 내용(rememo)과 작성자(rewriter)가 필요하다.

이 컨트롤들의 이름은 게시물에 대한 댓글이나 수정시 사용하는 컨트롤과 이름이 동일하다.

이름이 동일해도 폼 이름이 form3으로 다르다.

<div id="replyDialog" style="width: 99%; display:none">
    <form name="form3" action="board6ReplySave" method="post">
        <input type="hidden" name="brdno" value="<c:out value="${boardInfo.brdno}"/>">
        <input type="hidden" name="reno">
        <input type="hidden" name="reparent">
        작성자: <input type="text" name="rewriter" size="20" maxlength="20"> <br/>
        <textarea name="rememo" rows="3" cols="60" maxlength="500"></textarea>
        <a href="#" onclick="fn_replyReplySave()">저장</a>
        <a href="#" onclick="fn_replyReplyCancel()">취소</a>
    </form>
</div>

BoardRead.jsp

hideDiv 함수는 수정하거나 댓글 작성 화면을 가진 DIV 들을 숨기는 함수로

반복되어 사용하는 것이라 함수로 처리했다.

지정된 DIV 테그의 ID값으로 컨트롤(HTML Element)을 찾아서 화면에서 안보이게(display = "none") 처리한다.

그리고 부모를 document.body로 바꾸어 준다.

현재 있는 위치에는 기존 댓글이 있기 때문에 기존 댓글 수정시 문제를 일으킬 수 있어 다른 곳으로 옮겨 주는 것이다.


나머지 3개의 함수는 댓글 수정에서 사용한 것과 유사하게 작성되었는데

부모 댓글 번호를 가지는 것과

수정에서는 댓글 내용이 있는 위치에 댓글 내용을 지우고 수정 폼이 놓이게 되는데

댓글의 댓글에서는 댓글 내용 뒤에 추가(appendChild)해준다.


function hideDiv(id){
    var div = document.getElementById(id);
    div.style.display = "none";
    document.body.appendChild(div);
}

function fn_replyReply(reno){
    var form = document.form3;
    var reply = document.getElementById("reply"+reno);
    var replyDia = document.getElementById("replyDialog");
    replyDia.style.display = "";
   
    if (updateReno) {
        fn_replyUpdateCancel();
    }
   
    form.rememo.value = "";
    form.reparent.value=reno;
    reply.appendChild(replyDia);
    form.rewriter.focus();
}
function fn_replyReplyCancel(){
    hideDiv("replyDialog");
}

function fn_replyReplySave(){
    var form = document.form3;
   
    if (form.rewriter.value=="") {
        alert("작성자를 입력해주세요.");
        form.rewriter.focus();
        return;
    }
    if (form.rememo.value=="") {
        alert("글 내용을 입력해주세요.");
        form.rememo.focus();
        return;
    }
   
    form.action="board6ReplySave";
    form.submit();   
}

BoardRead.jsp

계층형 댓글의 수정은 일반 댓글과 같이 댓글 번호(reno)를 이용하기 때문에 다른 처리를 하지 않아도 된다.




'Java > 게시판 6: 무한댓글' 카테고리의 다른 글

1. 무한 댓글 시작하기  (14) 2016.05.15
2. 무한 댓글 리스트  (11) 2016.05.15
3. 무한 댓글 쓰기 – control  (3) 2016.05.15
5. 무한 댓글 삭제  (1) 2016.05.15
6. 무한 댓글 삭제 II  (0) 2016.05.15

댓글 삭제는 기존과 동일하게 넘겨 받은 댓글 번호(reno)를 삭제 처리해 주면 된다.

    public boolean deleteBoard6Reply(String param) {
        Integer cnt = sqlSession.selectOne("selectBoard6ReplyChild", param);
       
        if ( cnt > 0) {
            return false;
        }
       
        sqlSession.update("updateBoard6ReplyOrder4Delete", param);
       
        sqlSession.delete("deleteBoard6Reply", param);
       
        return true;
    }

Board6Svc.java

다만, 삭제하기 전에 삭제하려는 대상의 댓글보다 순번이 큰 댓글들의 순서(reorder) 값은 1씩 빼서 순서 값을 맞춰 주는 것이 좋다.

하지 않아도 되지만 값이 들쑥날쑥 한 것이 보기에 좋지 않아서 처리한다.

    <update id="updateBoard6ReplyOrder4Delete" parameterType="gu.board6.BoardReplyVO">
        UPDATE TBL_BOARDREPLY TBR1
         INNER JOIN TBL_BOARDREPLY TBR2 ON TBR2.BRDNO=TBR1.BRDNO AND TBR1.REORDER >TBR2.REORDER AND TBR1.REDELETEFLAG='N'
           SET TBR1.REORDER = TBR1.REORDER - 1
         WHERE TBR2.RENO=#{reno}
    </update>

board6.xml


마지막으로 다음의 SQL 문은 자식 댓글 수를 계산하는 것으로,

자식 댓글이 있으면(cnt > 0) 삭제 하지 않도록 한다.

자식 댓글이 있는데 삭제 하면 화면 리스트에서 트리처럼 보이도록 한 것이 틀어지고

글 내용도 이해 할 수 없게 된다.

    <select id="selectBoard6ReplyChild" resultType="Integer" parameterType="String">
        SELECT COUNT(*)
          FROM TBL_BOARDREPLY
         WHERE REPARENT=#{reparent} AND RENO!=#{reparent} AND REDELETEFLAG='N'
    </select>

board6.xml


자식 댓글이 있으면 서비스에서 false 값을 반환 받게 되고

컨트롤에서는 BoardFailure.jsp 를 호출해서 오류 메시지를 출력해 준다.

    public String board6ReplyDelete(HttpServletRequest request, BoardReplyVO boardReplyInfo) {
       
        if (!boardSvc.deleteBoard6Reply(boardReplyInfo.getReno()) ) {
            return "board6/BoardFailure";
        }

        return "redirect:/board6Read?brdno=" + boardReplyInfo.getBrdno();
    }

Board6Ctr.java

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>
<script>
        alert("다른 사람의 댓글이 있어서 삭제할 수 없습니다.");
        history.back(-1);
</script>

BoardFailure.jsp



'Java > 게시판 6: 무한댓글' 카테고리의 다른 글

1. 무한 댓글 시작하기  (14) 2016.05.15
2. 무한 댓글 리스트  (11) 2016.05.15
3. 무한 댓글 쓰기 – control  (3) 2016.05.15
4. 무한 댓글 쓰기 – JSP  (0) 2016.05.15
6. 무한 댓글 삭제 II  (0) 2016.05.15

여기서는 다루지 않지만

부모를 지우면 모든 자식을 지우는 것도 방법이 될 수 있다.

익혀두면 도움이 되는 기법이라 적어본다.

부모를 지우면 모든 자식을 지우는 방법은

부모 필드(reparent)를 따라서 나를 부모로 하는 모든 자식을 찾으면 된다.

오라클에서는 connect BY로 쉽게 구현 할 수 있는데

mysql에서는 제공되지 않는 기능으로

대부분 만들어 사용하고 있다.


다음 블로그에서 제공하는 함수를 가져와 게시판 예제에 맞추어 수정했다.

이 함수의 원리는 간단하다.

지정된 댓글을 부모로 하는 자식이 없을 때까지 반복해서 찾는(SELECT) 것이다.

블로그 내용을 잘 읽어보길 바란다.

http://blog.naver.com/PostView.nhn?blogId=endstar7&logNo=100124603859&categoryNo=32&viewDate=&currentPage=1&listtype=0
https://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/

위 블로그의 내용과 다른 점은 테이블명(tbl_boardreply)과

게시물 번호(brdno) 값을 받아서 조건으로 사용한 것이다.

블로그 함수는 테이블의 전체 데이터를 대상으로 하고,

본 예제는 하나의 게시물에 대하여 처리하기 때문이다.


DELIMITER $$

CREATE FUNCTION  board_connect_by_parent(_brdno int) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
    DECLARE _id INT;
    DECLARE _parent INT;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;

    SET _parent = @id;
    SET _id = -1;

    IF @id IS NULL THEN
        RETURN NULL;
    END IF;

    LOOP
        SELECT MIN(reno)
          INTO @id
          FROM tbl_boardreply
         WHERE reparent = _parent and brdno=_brdno and reno != reparent
           AND reno > _id;

        IF @id IS NOT NULL OR _parent = @start_with THEN
            SET @level = @level + 1;
            RETURN @id;
        END IF;

        SET @level := @level - 1;

        SELECT reno, reparent
          INTO _id, _parent
          FROM tbl_boardreply
         WHERE reno = _parent and brdno=_brdno and reno != reparent;
    END LOOP;

END

$$
DELIMITER ;


이렇게 작성된 함수를 다음과 같이 실행하면 된다.

다음 예는 모든 자식 리스트를 출력하는 것이고 SELECT 문 대신에 DELETE(UPDATE)문을 사용하면 된다.

SELECT *
 FROM TBL_BOARDREPLY
 WHERE RENO IN (
          SELECT ID
           FROM (
                     SELECT BOARD_CONNECT_BY_PARENT(18) AS ID, @LEVEL AS LEVEL
                      FROM (
                                SELECT @START_WITH := 6,
                                         @ID := @START_WITH,
                                         @LEVEL := 0
                     ) VARS, TBL_BOARDREPLY
                      WHERE @ID IS NOT NULL
          ) DS
)

지정된 부모의 모든 자식을 찾거나

지정된 자식의 모든 부모를 찾는 것은 많이 사용되는 기술이니

게시판 예제에는 적용하지 않았지만 잘 익혀두길 바란다.



'Java > 게시판 6: 무한댓글' 카테고리의 다른 글

1. 무한 댓글 시작하기  (14) 2016.05.15
2. 무한 댓글 리스트  (11) 2016.05.15
3. 무한 댓글 쓰기 – control  (3) 2016.05.15
4. 무한 댓글 쓰기 – JSP  (0) 2016.05.15
5. 무한 댓글 삭제  (1) 2016.05.15

+ Recent posts