FreeHand

[Spring Boot] 게시판 프로젝트 - 05. 댓글 기능 본문

Web/Spring

[Spring Boot] 게시판 프로젝트 - 05. 댓글 기능

Jinn 2023. 10. 4. 00:11

이전 글

 

[Spring Boot] 게시판 프로젝트 - 04. 회원정보 수정

이전 글 [Spring Boot] 게시판 프로젝트 - 03. 게시판 글 CRUD이전 글 { this.save(); }); }, save: function() { let d" data-og-host="pressky99.tistory.com" data-og-source-url="https://pressky99.tistory.com/39" data-og-url="https://pressky99.ti

pressky99.tistory.com

목차

1. 댓글 작성
2. 댓글 삭제

 

1. 댓글 작성

글 상세 페이지 댓글

 

[detail.jsp]

  ... 생략 ...

  <div class="card">
    <form>
      <input type="hidden" id="board-id" value="${board.id}"/>
      <div class="card-body">
        <textarea id="comment-content" class="form-control" rows="1"></textarea>
      </div>
      <div class="card-footer">
        <button type="button" id="btn-comment-save" class="btn btn-primary">등록</button>
      </div>
    </form>
  </div>
  <br/>
  <div class="card">
    <div class="card-header">댓글</div>
    <ul id="comment--box" class="list-group">
      <c:forEach var="comment" items="${board.comments}">
        <li id="comment--${comment.id}" class="list-group-item d-flex justify-content-between">
          <div>${comment.content}</div>
          <div class="d-flex">
            <div class="font-italic">작성자: ${comment.user.username} &nbsp;</div>
            <c:if test="${comment.user.id == principal.user.id}">
              <button class="badge">삭제</button>
            </c:if>
          </div>
        </li>
      </c:forEach>
    </ul>
  </div>

</div>
<!-- Form end -->

<script src="/js/board.js"></script>
[board.js]
let index = {
    init: function() {
        // 생략
        $("#btn-comment-save").on("click", () => {
            this.commentSave();
        });
    }
    commentSave: function() {
        let data = {
            board: {
                id: $("#board-id").val()
            },
            content: $("#comment-content").val()
        }

        $.ajax({
            type: "POST",
            url: "/api/board/comment",
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).done(function() {
            alert("댓글 작성 완료");
            location.reload(true);
        }).fail(function(error) {
            alert(JSON.stringify(error));
        });
    },
}

index.init();

글 상세 페이지에서 댓글 폼에 댓글을 입력하여 ajax 요청을 보내면 댓글이 등록된다.

@AllArgsConstructor
@NoArgsConstructor
@Getter @Setter @ToString
@Builder
@Entity
@Table(name = "comments")
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 200)
    private String content;

    @ManyToOne
    @JoinColumn(name = "boardId")
    private Board board;

    @ManyToOne
    @JoinColumn(name = "userId")
    private User user;

    @CreationTimestamp
    private Timestamp createdAt;
}

Comment와 Board 그리고 Comment와 User의 연관관계에서 Comment가 연관관계의 주인이기때문에 Comment를 save할 때 Board와 User를 초기화 해서 save해야 한다.

이렇게 해야 해당 댓글의 작성자와 어떤 글에 달린 댓글인지를 알수있다.

 

@RequiredArgsConstructor
@RestController
public class BoardApiController {

    private final BoardService boardService;

    // 생략

    @PostMapping("/api/board/comment")
    public ResponseEntity<Integer> saveComment(@RequestBody Comment comment,
                                               @AuthenticationPrincipal PrincipalDetail principal) {
        boardService.saveComment(comment, principal.getUser());
        return new ResponseEntity<>(1, HttpStatus.CREATED);
    }
}
@RequiredArgsConstructor
@Service
public class BoardService {

    private final BoardRepository boardRepository;
    private final CommentRepository commentRepository;

    // 생략

    @Transactional
    public void saveComment(Comment comment, User user) {
        Board board = boardRepository.findById(comment.getBoard().getId())
                .orElseThrow(() -> new NoSuchElementException("댓글 작성 실패: 해당 글을 찾을 수 없음."));

        comment.setBoard(board);
        comment.setUser(user);

        commentRepository.save(comment);
    }

}

요청이 들어온 board의 id로 board를 초기화하고 로그인된 사용자로 작성자(User)를 초기화 해서 저장한다.

 

작성된 댓글은 해당 글의 상세 페이지에서 볼 수 있다.

@AllArgsConstructor
@NoArgsConstructor
@Getter @Setter
@Builder
@Entity
@Table(name = "boards")
public class Board {

    // 생략

    @OrderBy("createdAt")
    @OneToMany(mappedBy = "board", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
    private List<Comment> comments = new ArrayList<>();
}

글 상세 페이지를 불러올때 댓글 목록이 바로 보이므로 댓글 리스트를 바로 조회하기 위해 FetcyType은 EAGER로 작성했다.

 

Board가 Comment 리스트를 갖고 있기 때문에 댓글을 조회하는 메서드는 필요없다. jsp에서 모델로 받은 board에서 comments를 바로 보여주면 댓글 목록이 보인다.

 

그리고 글을 삭제할때 댓글도 같이 삭제되도록 CascadeType은 REMOVE로 설정했다. 이 설정이 되어있지 않으면 글을 삭제할 때 외래키 cascade 때문에 에러가 발생한다.


2. 댓글 삭제

<c:if test="${comment.user.id == principal.user.id}">
    <button onClick="index.commentDelete(${comment.id})" class="badge">삭제</button>
</c:if>
commentDelete: function(commentId) {
    $.ajax({
        type: "DELETE",
        url: "/api/board/comment/" + commentId,
    }).done(function() {
        alert("댓글 삭제 완료");
        location.reload(true);
    }).fail(function(error) {
        alert(JSON.stringify(error));
    });
}