본문 바로가기
프로젝트/Recipository

[Dev] 22.12.23. 게시글 삭제

by 규글 2022. 12. 23.

작업

content.html / content.js

    <a th:href="@{/user/content/delete/{contentId}(contentId = ${recipe.contentId})}"
       id="contentDeleteBtn" class="btn btn-light float-end">삭제</a>

(content.html)       
--------------------------------------------------------------------------------------
(content.js)

    // 게시글 삭제 클릭 시 동작
    document.querySelector("#contentDeleteBtn").addEventListener("click", function(e){
        e.preventDefault();

        var wantToDelete = confirm("게시글을 삭제하겠습니까?");
        if(wantToDelete){
            var url = this.getAttribute("href");

            var token = document.querySelector("meta[name=_csrf]").content;
            var header = document.querySelector("meta[name=_csrf_header]").content;

            var promise = fetch(url, {
                method: "DELETE",
                headers: {
                    "header": header,
                    "X-Requested-With": "XMLHttpRequest",
                    "X-CSRF-Token": token
                }
            });

            promise.then(function(response){
                return response.json();
            }).then(function(data){
                if(data.beDeleted){
                    alert("게시글을 삭제했습니다.");
                    location.href = "/";
                } else {
                    alert("게시글을 삭제할 수 없습니다. 문제가 반복된다면 문의 바랍니다.");
                }
            });
        }
    });

 Client에서는 삭제 버튼을 가져다두었는데, 수정 버튼과 마찬가지로 이 친구는 게시글 작성자와 Authentication의 name이 같은 경우 위치할 수 있다. 해당 버튼을 누르면 게시글을 삭제할 것인지 확인하고, 삭제를 위한 request를 server로 보내고 성공 여부를 response를 받아 다르게 동작하도록 했다.

 

RecipeController.java

    // 게시글을 삭제하는 controller method
    @DeleteMapping("/user/content/delete/{contentId}")
    public ResponseEntity<Object> delete(@PathVariable Long contentId){

        Map<String, Object> map = new HashMap<>();
        map.put("beDeleted", recipeService.delete(contentId));

        return ResponseEntity.ok().body(map);
    }

 게시글을 삭제하는 controller의 method이다. 전달받는 url에서 path variable에 해당하는 data를 지울 수 있도록 service logic에 그 값을 전달하도록 했다.

 

RecipeServiceImpl.java

    // 게시글을 삭제하는 service logic
    @Override
    public boolean delete(Long contentId) {
        try{
            recipeRepository.deleteById(contentId);

            return true;
        } catch (Exception e) {
            return false;
        }
    }

 게시글을 삭제하는 service logic이다. 간단히 해당 Id에 해당하는 Recipe를 지우도록 했다.

 

 

 실제로 동작하는 query문의 순서이다. 우선 recipe table에서 data를 select하고, Recipe Entity와 연관 관계가 있는 comment, link table에서도 data를 select 한다.

 

 그리고 이어서 comment table과 link table에서 data에 대해 delete하고, recipe table에서 data를 delete 하는 것으로 마무리 된다. 이 모든 과정은 Recipe와 Link, Recipe와 Comment 사이의 연관 관계와 그 옵션 덕분에 가능한 것이다. 다음 이미지를 보자.

 

 왼쪽 이미지는 Recipe, 오른쪽 이미지에서 위쪽은 Link이고 아래쪽은 Comment 이다. 중요 point는 Recipe Entity에 작성된 cascade 속성이다. Cascade Type ALL 에도 포함되어 있는 REMOVE 옵션은 해당 Recipe의 data가 지워지면, 그와 연관된 Entity에 해당하는 data도 지워주는 역할을 한다.

 즉, Cascade Type ALL은 같은 날 같은 시간에 함께 태어나고 함께 죽자는 것을 의미하고, Cascade Type REMOVE는 같은 날 같은 시간에 태어나지는 않았어도 함께 죽자는 것을 의미한다.

 

 이것은 table에서 기존 data를 삭제하면서 연관된 table의 data를 쉽게 함께 삭제할 수 있다는 점에서 큰 차이가 있다. 이전의 국비 과정에서의 프로젝트에서는 하나의 매장 정보를 지울 때 그와 관련된 메뉴, 리뷰, 주문정보, 자리정보를 모두 지웠어야 했고, 그것들을 모두 query문을 동작하는 method를 하나하나 작성해주었던 기억이 난다. 하지만 이번 작업에서는 DB와 data를 주고 받는 Entity를 만들고, 그들 사이에 연관 관계를 맺도록 하여 과거의 번거로움을 피할 수 있었다.

 

 

 게시글을 삭제하는 것은 이렇게 간단한 일이다. 다음은 예고 했듯이 개인 정보를 조회할 수 있는 마이 페이지에 대한 작업을 해볼 것인데, 그 사이에 잠깐 뭔가를 좀 고민해본 후에 시작하겠다.

댓글