본문 바로가기
자바의 봄(Spring)/프로젝트

SpringBoot-Movie-Thymeleaf-Project - 10 Thymeleaf에서 javascript 함수 사용하기

by 종안이 2024. 4. 14.

삭제 버튼을 만들어야 돼서 , 버튼 클릭 시 자바스크립트 함수가 동작하도록 만들어야 됐는데 ,

 

안돼서 머리에 김이 모락모락 나기 시작했는데 , 작동하는 실마리가 잡혀서 작성한다. 

 

<div >
    <button th:onclick="replyDelete('[[${reply.id}]]')">using no variable</button>
</div>

 

댓글 삭제 버튼을 만들어야 되는데 해당 번호를 가진 댓글이 삭제 버튼이 눌렸을때 삭제가 되어야 한다. 

 

그런데 계속 Uncaught ReferenceError: replyDelete is not defined
    at HTMLButtonElement.onclick 이런 에러 메시지가 떴다. 

 

하단에는 아래와 같은 모양이었다.  현재는 정상 작동하는 상태인데 

알고보니 자바스크립트를 작성하는 부분에서 } 괄호 문제였는지 

전부 다 지우고 다시 작성하니 정상 작동하고 있다. 

 

<script type="text/javascript">
    $(document).ready(function(){
        $("#backButton").on("click", function () {
            location.replace("/v1/board");
        });
    });

        function replyDelete(replyId){

            alert(replyId);
            // url = "[[@{/v1/delete-reply}]]";
            // userReplyId = replyId;
            //
            // requestParam = {id : userReplyId}
            // $.get(url, function (response) {
            //     console.log(response);
            // });
        }

</script>

 

 

그러면 이제 버튼을 클릭 했을때 링크를 타고 요청이 날아가도록 만들면 된다. 

 

그러나 버튼을 클릭하면 아래와 같은 에러가 발생하는데 여기서도 상당히 오래 시간을 많이 잡아먹었다. 

 

 

 

그래서 버튼을 클릭할 때마다 아래와 같은 값이 나오며 오류가 발생해 정상적으로 작동하지 않았다. 

주소가 정상인데 왜 올라가지 않았지하며 Controller 단으로 올라가보니 뭔가 이상한 것을 조금 눈치챌 수 있었다. 

 

알고보니  아래와 같이 작성해서 파라미터가 안먹혔던 것이었다. 

 

 

 

현재는 @PathVariable을 추가해줘서 먹히는 상태이다. 

 

@RestController
public class ReplyRestController {


    @Autowired
    private replyServiceImpl replyService;

    @GetMapping("/v1/delete-reply/{id}")
    public ResponseEntity<?> replyDelete(@PathVariable(name = "id") Integer id) {

        replyService.deleteReply(id);
        return ResponseEntity.ok().build();
    }
}

 

 

그럼 이제 모양새가 이상한 버튼을 바꿔준다. 

 

이제 삭제 버튼 클릭 시 정상적으로 작동하는 것을 알 수 있다.

그런데 이것도 역시 삭제를 눌렀을때 너무 동의 없이 삭제해주므로 모달창을 추가해서 동의를 얻고 삭제를 해주자 . 

 

 

이것들은 form 상태에 있을때 자바스크립트의 전파를 막는 것이다. 

e.preventDefault()
e.stopPropagation()

 

자바스크립트 함수로 href가 실행돼서 delete가 되는 것을 막으려고 했는데 계속 하다가 안돼서 찾아보니 ,

해당 내용이 form 안에 있어야 하며 e.preventDefault()는 form 요청이 날아갈때 페이지 새로고침 등을 막아주는 것이었다.


<script type="text/javascript">
    $(document).ready(function () {
        const collection = document.getElementById("delete-button");
        console.log(collection);
        collection.addEventListener("click",function (e){
            alert("경고");
        })

    });


    function replyDelete(replyId) {

        url = "[[@{/v1/delete-reply}]]";
        userReplyId = replyId;
        requestParam = {id: userReplyId};
        $.get(url + '/' + userReplyId, function (response) {
            alert("해당하는 댓글이 삭제되었습니다: " + userReplyId);
            location.reload();
        }).done();
    }


</script>

 

아래와 작성하고 삭제 버튼을 눌렀다 . 예상되는 결과는 모달창이 떠야한다. 

 

 

그런데 중요한 문제가 발생했다. 콘솔 창에 찍어보면 26번째 댓글에는 

클릭 시 값이 나오는데 두번째 댓글 버튼을 눌렀을때는 이벤트 리스너가 먹지 않는 상황이 발생했다. 

 

찾아본 결과  아래 코드는 반복되는 반복문에서 한 줄의 ID 값 밖에 가져오지 못하는 것이었다.

const collection = document.getElementById("delete-button");

다시 말해서 현재 반복문을 도는데

여러 개의 삭제 버튼 중에서 하나만 가져오게 된 것이었다. 

 

그래서 찾아보니  아래와 같은 내용이 있었다. 

 

그렇다면 해당 내용으로 코드를 수정해보고 돌려보니

모든 댓글 삭제 버튼이 클릭 시 이벤트가 작동하는 것을 알 수 있었다.

 

해당 부분의 코드는 아래와 같다. 


<script type="text/javascript">
    $(document).ready(function () {
        const collection = document.querySelectorAll("#delete-button");
        collection.forEach(value => {
            value.addEventListener("click", function (e) {
                console.log(value)
                alert("경고")
            });
        });
        console.log(collection);

    });


    function replyDelete(replyId) {

        url = "[[@{/v1/delete-reply}]]";
        userReplyId = replyId;
        requestParam = {id: userReplyId};
        $.get(url + '/' + userReplyId, function (response) {
            alert("해당하는 댓글이 삭제되었습니다: " + userReplyId);
            location.reload();
        }).done();
    }


</script>

 

그러면 이제 모달창을 생성해서 날려줘야 한다. 그러면 코드를 아래와 같이 작성하고 요청을 날려줬다. 


<script type="text/javascript">
    document.addEventListener("DOMContentLoaded", function () {
        const collection = document.querySelectorAll("#delete-button");
        collection.forEach(value => {
            value.addEventListener("click", function (e) {
                console.log(value);
                var myModal = new bootstrap.Modal(document.getElementById('myModalShow'));
                myModal.show();
            });
        });

    });


    function replyDelete(replyId) {

        url = "[[@{/v1/delete-reply}]]";
        userReplyId = replyId;
        requestParam = {id: userReplyId};
        $.get(url + '/' + userReplyId, function (response) {
            alert("해당하는 댓글이 삭제되었습니다: " + userReplyId);
            location.reload();
        }).done();
    }

 

이렇게 아무것도 없는 모달창이 생성된 것을 알 수 있다. 그렇다면 모달 부분에 제목과 내용을 넣어주면 된다. 

document.getElementById("show-modal-title").innerHTML = "경고!";
document.getElementById("show-modal-body").innerHTML = "해당하는 댓글을 삭제하시겠습니까?";

이렇게 작성하게 되면 show-modal-title , show-modal-body 의 아이디를 갖고 있는 부분에 해당 내용이 들어가게 된다. 

확인해보자 

 

 

해당 부분의 내용이 정상적으로 잘 나오는 것을 알 수 있다. 

 

그렇다면 이제 OK 버튼을 눌렀을때 댓글이 삭제가 되어야 한다. 그렇다면 OK 버튼을 눌렀을때 본래 실행되었던 

삭제 함수가 실행되면 된다.  

 

 

 

그런데 진행하다보니까 처음에 썼던 onclick replyDelete 함수를 어떻게 작성했는지 까먹어버렸다. 

다시 진행해도 되는데 면접 떄문에 시간이 없어서 이만 생략하도록 하겠다. 

 

기능은 완성되도록 했고 코드는 아래와 같다. 

나중에 시간이 되면 다시 돌아와서 어떻게 작동하게 됐는지 쓰도록 하겠다. 

 

<!DOCTYPE html>

<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{Part/header :: header}">
</head>
<nav th:replace="~{Part/navbar :: navigation}"></nav>
<body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<div class="container mt-5">

    <h5 class="my-3 border-bottom pb-2">[[${board.title}]]</h5>
    <div class="mb-3">
        <div>[[${board.content}]]</div>
    </div>
</div>

<div class="container mt-5">


    <div class="card" style="margin: 0 auto; width:900px; ">
        <div class="card-header">댓글 리스트</div>
        <ul id="comment--box" class="list-group">
            <th:block th:each="reply : ${replyList}">
                <li th:upReplyId="'reply-' + ${reply.id}" class="list-group-item d-flex justify-content-between">
                    <div class="d-flex">
                        <th:block th:if="${reply.creationTime}">
                        </th:block>

                        <div>[[${reply.comments}]]</div>
                        <div style="text-align: right"> - [[${reply.user.username}]]</div>
                        <div>- 작성날짜 : [[${reply.creationTime}]]</div>
                        &nbsp

                        <!--                        <th:block th:if="${#authentication.principal} == ${reply.getUser()}">-->
                        <input type="hidden" th:replyId="${reply.id}">
                        <input type="hidden" th:boardId="${board.getId()}">
                        <div>
                            <button id="delete-button" class="btn btn-danger"
                                    th:href="@{'/v1/delete-reply/'+${boardId}+ '/' + ${reply.id}}"> 삭제
                            </button>
                        </div>
                        <!--                        </th:block>-->
                    </div>
                </li>
            </th:block>
            <form th:action="@{'/v1/create-reply/' + ${boardId} + '/reply'}" method="post" th:object="${reply}">

                <div class="text-center m-2">
                    하단에 댓글을 입력하세요
                </div>
                <div>
                    <input type="text" th:field="*{comments}" class="form-control m-3" id="reply-content"
                           style="display: flex; align-items: center;">

                </div>
                <input type="hidden" th:value="${board.id}" id="boardId">

                <button type="submit" class="btn btn-primary m-3" value="저장">저장</button>
                <button type="button" id="backButton" class="btn btn-danger m-3" style="align-items: flex-end"
                        value="저장">뒤로가기
                </button>
            </form>
        </ul>
    </div>
</div>
<div class="modal" tabindex="-1" id="myModalShow">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="show-modal-title"></h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body" id="show-modal-body">
                <p></p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                <a href="" class="btn btn-primary" id="delete-ok">OK</a>
            </div>
        </div>
    </div>
</div>

<script type="text/javascript">
    document.addEventListener("DOMContentLoaded", function () {
        const collection = document.querySelectorAll("#delete-button");
        collection.forEach(value => {
            value.addEventListener("click", function (e) {
                console.log(value);
                document.getElementById("show-modal-title").innerHTML = "경고!";
                document.getElementById("show-modal-body").innerHTML = "해당하는 댓글을 삭제하시겠습니까?";
                var myModal = new bootstrap.Modal(document.getElementById('myModalShow'));
                const attribute = value.getAttribute("href");
                console.log(attribute);
                myModal.show();
                const href = document.getElementById("delete-ok").setAttribute("href",attribute);
                console.log(href);
            });
        });

    });


    function replyDelete(replyId) {

        url = "[[@{/v1/delete-reply}]]";
        userReplyId = replyId;
        requestParam = {id: userReplyId};
        $.get(url + '/' + userReplyId, function (response) {
            alert("해당하는 댓글이 삭제되었습니다: " + userReplyId);
            location.reload();
        }).done();
    }


</script>
</body>

</html>

댓글