[개발일지] 취미 커뮤니티 (4) 댓글 등록, 삭제, 수정

zwon·2023년 10월 15일
0

개발일지

목록 보기
16/23

User

@Entity @Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Table(name = "users")
public class User extends BaseTimeEntity {
  @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "user_id")
  private Long id;
  private String name; // 본명
  private String nickname; // 별명
  private String password;
  private String email;

  @Builder
  public User(String name, String nickname, String password, String email) {
    this.name = name;
    this.nickname = nickname;
    this.password = password;
    this.email = email;
  }

  public void update(UserUpdateRequestDto updateDto) {
    this.nickname = updateDto.getNickname();
    this.password = updateDto.getPassword();
  }
}

Reply

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Reply extends BaseTimeEntity {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Lob
  private String comment;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "user_id")
  private User user;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "board_id")
  private Board board;

  public void update(ReplyUpdateRequestDto updateRequestDto) {
    this.comment = updateRequestDto.getComment();
  }

  public void setUser(User user){
    this.user = user;
  }
  // 연관관계 편의 메서드
  public void setBoard(Board board){
    if(this.board != null) {
      this.board.getReplies().remove(this);
    }
    this.board = board;
    this.board.getReplies().add(this);
  }
}

Board

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Board extends BaseTimeEntity {

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

  private String title;
  @Lob
  private String content;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "user_id")
  private User user;

  @OneToMany(mappedBy = "board", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) // 글이 삭제되면 댓글 모두 삭제
  @OrderBy("createdDate")
  private List<Reply> replies = new ArrayList<>();


  public void setUser(User user){ // 연관관계 편의 메서드
    this.user = user;
  }

  public void update(BoardUpdateRequestDto request) {
    this.title = request.getTitle();
    this.content = request.getContent();
  }
}
  • 게시물 삭제 시 댓글도 함께 삭데되도록 cascade 설정함.
  • 게시물 가져올 땐 댓글도 필수이니 EAGER로 설정했음.. 하지만 나중에 문제가 될지 몰랐지..

ReplyRepository

public interface ReplyRepository extends JpaRepository<Reply, Long> {
  Page<Reply> findAll(Pageable pageable);
}

ReplyService

@Service
@RequiredArgsConstructor
public class ReplyService {
  private final ReplyRepository replyRepository;
  private final BoardRepository boardRepository;
  private final EntityManager em;

  // 댓글 등록
  public void save(Long boardId, User user, ReplySaveRequestDto replySaveRequestDto){
    Board board = boardRepository.findById(boardId)
        .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 포스트입니다.")); // 이러면 코드가 너무 반복되지 않나..음..방법을 찾아보자..
    Reply reply = replySaveRequestDto.toEntity();
    reply.setBoard(board);
    reply.setUser(user);
    replyRepository.save(reply);
  }
  // 댓글 조회 (전체) , 댓글 단건 조회는 딱히,, "내가 쓴 댓글 보기"에서 사용할 메서드
  // 마이페이지 구현할 때 구현할 것
  // 댓글 삭제
  public Long delete(Long id){
    // 여기서 postID를 반환하게 ..
    Long boardId = em.createQuery("select r.board.id from Reply r where r.id=:id ", Long.class)
        .setParameter("id", id)
        .getSingleResult();
    replyRepository.deleteById(id);
    return boardId;
  }
  // 댓글 수정
  @Transactional
  public Long update(Long id, ReplyUpdateRequestDto updateRequestDto){
    Long boardId = em.createQuery("select r.board.id from Reply r where r.id=:id ", Long.class)
        .setParameter("id", id)
        .getSingleResult();
    Reply reply = replyRepository.findById(id).orElseThrow(IllegalArgumentException::new);
    reply.update(updateRequestDto);
    return boardId;
  }
}

ReplyViewController

@Controller
@RequiredArgsConstructor
public class ReplyViewController {
  private final ReplyService replyService;
  private final BoardService boardService;

  // 댓글 등록
  @PostMapping("/boards/reply/{boardId}")
  public String save(@PathVariable Long boardId, @ModelAttribute ReplySaveRequestDto replySaveRequestDto,
                     HttpSession session) {
    User loginUser = (User) session.getAttribute("loginUser");
    replyService.save(boardId, loginUser, replySaveRequestDto);
    return "redirect:/boards/"+boardId;
  }
  // 댓글 삭제
  @DeleteMapping("/boards/reply/{replyId}")
  public String delete(@PathVariable Long replyId) {
    // 게시판의 아이디가 아니라 댓글의 아이디 여야함.
    // board로 어떻게 보내지,, -> replyId를 사용해서 쿼리문 조인해서 보드 아이디 가져오게함. 지금은 생각나는 방법이 이거밖에 없음..
    Long boardId = replyService.delete(replyId);
    return "redirect:/boards/"+boardId;
  }

  // 댓글 수정
  @PostMapping("/boards/edit/reply/{replyId}")
  public String update(@PathVariable Long replyId, @ModelAttribute ReplyUpdateRequestDto updateRequestDto){
    Long boardId = replyService.update(replyId, updateRequestDto);
    return "redirect:/boards/"+boardId;
  }
  // 댓글 조회 - "내가 쓴 댓글 보기"를 클릭한 경우

}

Thymeleaf - detailBoard.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>상세 조회 화면</title>
  <link rel="stylesheet" th:href="@{css/board/detailBoard}">
</head>
<body>
  <div class="container">
    <div class="board-body">
      작성자 : <span th:text="${board.nickname}"></span>
      <h4 class="title" th:text="${board.title}"></h4>
      <p class="content" th:text="${board.content}"></p> <!-- 나중에 텍스트....처리하는 css할려고함.-->
      <!--태그-->
      <div class="tags" th:each="tag : ${board.getTags()}">
        <span th:text="${tag.tagName}"></span>
      </div>
    </div>
    <!--버튼 영역-->
    <div>
      <form th:action="@{/boards/{boardId}(boardId=${board.id})}" th:method="delete">
        <button type="button"
                th:onclick="|location.href='@{/boards}'|">취소</button>
        <button type="button"
                th:if="${board.nickname != null && board.nickname == loginUser.nickname}"
                th:onclick="|location.href='@{/boards/{boardId}/edit(boardId=${board.id})}'|">수정</button>
        <button type="submit"
                th:if="${board.nickname != null && board.nickname == loginUser.nickname}"
                th:onclick="|location.href='@{/boards/{boardId}(boardId=${board.id})}'|">삭제</button>
      </form>
    </div>
    <hr>
    <!-- 댓글 영역 -->
    <div class="reply-container">
      <!-- 댓글 입력 폼 -->
      <form th:action="@{/boards/reply/{boardId}(boardId=${board.id})}" th:object="${replySaveRequestDto}" method="post">
        <textarea rows="5" cols="50" th:field="*{comment}"></textarea>
        <button type="submit">등록</button>
      </form>

      <div class="reply" th:each="reply :${board.getReplies()} ">
        <!-- 댓글 수정 폼 -->
        <form class="edit-form" style="display: none"
              th:action="@{/boards/edit/reply/{replyId}(replyId=${reply.id})}"
              th:object="${replyUpdateRequestDto}" method="post">
          <textarea name="editedContent" rows="5" cols="50"
                    th:field="*{comment}" placeholder="수정할 댓글을 입력해주세요"></textarea>
          <button type="submit">저장</button>
        </form>

        <p th:text="${reply.comment}"></p>
        <p th:text="${reply.nickname}"></p>
        <p th:text="${reply.lastModifiedDate}"></p>
        <form th:action="@{/boards/reply/{replyId}(replyId=${reply.id})}" th:method="delete">
          <button
              th:if="${reply.nickname != null && reply.nickname == loginUser.nickname}"
              type="submit">삭제
          </button>
          <button
              th:if="${reply.nickname != null && reply.nickname == loginUser.nickname}"
              th:onclick="'toggleEditForm(this)'"
              type="button">수정
          </button>
        </form>
        <hr>
      </div>
    </div><!-- reply-container -->

  </div><!--container -->
  <script th:inline="javascript">
      function toggleEditForm(button) {
          var replyContainer = button.closest('.reply');
          var editForm = replyContainer.querySelector('.edit-form');
    
          if (editForm.style.display == 'none') {
              editForm.style.display = 'block';
          } else {
              editForm.style.display = 'none';
          }
      }
  </script>
</body>
</html>

동작 화면

대댓글 기능도 추가할기 말지 고민이다.

profile
Backend 관련 지식을 정리하는 Back과사전

0개의 댓글