seong

JPA - 댓글 작성 및 삭제 기능 본문

JPA

JPA - 댓글 작성 및 삭제 기능

hyeonseong 2022. 10. 27. 16:53

만들기전 필요 부분 생각

- Comment 테이블 설계 

하나의 게시글에 여러가지 댓글 Board와 Comment는 1:n 관계이므로 Comment에 FK

한명의 유저는 여러개의 댓글 작성 가능 User와 Comment는 1:n 관계 이므로 Comment에 FK 

= 그럼 총 FK는 2개가 된다.  

 

- 댓글은 게시글 상세보기에 포함된다.

BoardDetailRespDto에 댓글 Dto 추가 필요함.


작성

1. Comment.java

 @ManyToOne - FK
하나의 게시글엔 여러개의 댓글, 여러명의 유저가 쓸 수 있다.
테이블 설계 할때 1:n의 관계가 나오면 항상 n 쪽에 FK가 걸린다. 

@Getter
@NoArgsConstructor
@Entity
public class Comment {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String content;

  // Many(Comment) to One(User) - 한명의 유저가 여러개의 댓글 작성
  @ManyToOne(fetch = FetchType.LAZY)
  private User user;

  // Many(Comment) to One(Board) - 하나의 댓글에 여러개의 댓글 작성 
  @ManyToOne(fetch = FetchType.LAZY)
  private Board board;
}

2. Board.java

댓글은 보통 게시글을 볼때 같이 보인다. -> Board에 추가.

하나의 게시글에 여러개의 댓글이 있다. 

그래서 List가 필요하다. 하지만 단순 List로 주면 테이블이 만들어질 때 List라는 타입은 없어서 에러가발생한다.

mappedBy = "테이블 변수명"을 하면 테이블 만들때 컬럼이 자동으로 생성 되지 않고

조회전용으로 만들어준다.

3. CommentRepository.java

package site.metacoding.white.domain;

import java.util.Optional;

import javax.persistence.EntityManager;

import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그확인을 위한 어노테이션
@RequiredArgsConstructor
@Repository
public class CommentRepository {
  // 댓글 등록, 댓글 삭제, 댓글 수정 (추후 구현)

  private final EntityManager em;

  public Comment save(Comment comment) {
    em.persist(comment);
    return comment;
  }

  public void deleteById(Long id) {
    em.createQuery("delete from Comment c where c.id = :id")
        .setParameter("id", id)
        .executeUpdate();
  }

  // Optinal - 예외처리
  public Optional<Comment> findById(Long id) {
    try {
      Optional<Comment> commentOP = Optional
          .of(em.createQuery("select b from Comment c where c.id = :id", Comment.class)
              .setParameter("id", id)
              .getSingleResult());
      return commentOP;
    } catch (Exception e) {
      return Optional.empty();
    }
  }
}

4. CommentService.java

@Slf4j
@RequiredArgsConstructor
@Service
public class CommentService {
  private final CommentRepository commentRepository;
  private final BoardRepository boardRepository;

  @Transactional
  public CommentSaveRespDto save(CommentSaveReqDto commentSaveReqDto) {
    // 1. Board가 있는지 확인
    Optional<Board> boardOP = boardRepository.findById(commentSaveReqDto.getBoardId());
    if (boardOP.isPresent()) {
      // 2. Commtent객체 만들기
      Comment comment = commentSaveReqDto.toEntity(boardOP.get());
      Comment commentPS = commentRepository.save(comment);
      CommentSaveRespDto commentSaveRespDto = new CommentSaveRespDto(commentPS);
      return commentSaveRespDto;
    } else {
      throw new RuntimeException("댓글 작성할 게시글이 없음.");
    }

  }

  @Transactional
  public void deleteById(Long id) {
    Optional<Comment> commentOP = commentRepository.findById(id);
    if (commentOP.isPresent()) {
      commentRepository.deleteById(id);
    } else {
      throw new RuntimeException("해당" + id + "로 삭제할 수 없습니다.");
    }

  }

}

5. CommentController.java

@RequiredArgsConstructor
@RestController
public class CommentApiController {

  private final CommentService commentService;
  private final HttpSession session;

  @PostMapping("/comment")
  public ResponseDto<?> save(@RequestBody CommentSaveReqDto commentSaveReqDto) {
    SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");
    if (sessionUser == null) {
      throw new RuntimeException("로그인을 진행 해주세요");
    }
    commentSaveReqDto.setSessionUser(sessionUser);
    return new ResponseDto<>(1, "성공", commentService.save(commentSaveReqDto));
  }

  @DeleteMapping("comment/{id}")
  public ResponseDto<?> deleteById(@PathVariable Long id) {
    SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");
    if (sessionUser == null) {
      throw new RuntimeException("로그인을 진행 해주세요");
    }
    commentService.deleteById(id);
    return new ResponseDto<>(1, "성공", null);
  }
}

6. RequestDto, ResponseDto를 만들어야한다..

- CommentReqDto

public class CommentReqDto {

  @Getter
  @Setter
  public static class CommentSaveReqDto {
    private String content;
    private SessionUser sessionUser;// 서비스 로직
    private Long boardId;

	//요청의 Dto는 메서드로 처리 
    public Comment toEntity(Board board) { // 현재 Comment의 ReqDto에는 BoardId만 있다. 그래서 Board객체가 필요할 경우 매개변수로 작성
      Comment comment = Comment.builder()
          .content(content)
          .board(board)// 어떤 게시글
          .user(sessionUser.toEntity())
          .build();
      return comment;
    }
  }
}

 

- CommentResponseDto

public class CommentRespDto {

  @Getter
  @Setter
  public static class CommentSaveRespDto {
    private Long id;
    private String content;

    private UserDto user;
    private BoardDto board;

    @Getter
    @Setter
    public static class UserDto {
      private Long id;
      private String username;

      public UserDto(User user) {// LAZY 로딩 시점
        this.id = user.getId();
        this.username = user.getUsername();
      }
    }

    @Getter
    @Setter
    public static class BoardDto {
      private Long id;

      public BoardDto(Board board) {// LAZY 로딩 시점
        this.id = board.getId();
      }
    }
	
    //응답의 Dto는 생성자로 처리 
    public CommentSaveRespDto(Comment comment) {
      this.id = comment.getId();
      this.content = comment.getContent();
      this.board = new BoardDto(comment.getBoard());
      this.user = new UserDto(comment.getUser());
    }
  }
}

7. BoardDetailRespDto 에 CommentDto추가

List<Comment>를 Dto로 변환할때 두가지 방법이 있다.

1. for문으로 모두 변환

//for문 사용 방법
      for (Comment c : board.getComments()) {
        this.comments.add(new CommentDto(c));
      }

 

2. stream방식 

board.getComments() - board객체에 Comments를 가져옴

.stream() - 객체의 컬렉션을 없애둠, (여기선 List이므로 List<CommentDto> -> CommentDto로 변환됨.)

.map((comment) -> new CommentDto(comment)) - comment를 CommentDto로 옮긴다.

.collect(Collectors.toList()); - 다시 List로 만들어준다.

this.comment = board.getComments().stream().map((comment) -> new CommentDto(comment))
.collect(Collectors.toList());

 

BoardDetailRespDto.java

  @Setter
  @Getter
  public static class BoardDetailRespDto {
    private Long id;
    private String title;
    private String content;
    private BoardUserDto user;
    private List<CommentDto> comments = new ArrayList<>();//Comment데이터

    @Setter
    @Getter
    public static class BoardUserDto {
      private Long id;
      private String username;

      public BoardUserDto(User user) {
        this.id = user.getId(); 
        this.username = user.getUsername(); 
      }
    }

    @Setter
    @Getter
    public static class CommentDto {
      private Long id;
      private String content;
      private CommentUserDto user;

      @Setter
      @Getter
      public static class CommentUserDto {
        private Long id;
        private String username;

        public CommentUserDto(User user) {
          this.id = user.getId();
          this.username = user.getUsername();
        }
      }

      public CommentDto(Comment comment) {
        this.id = comment.getId();
        this.content = comment.getContent();
        this.user = new CommentUserDto(comment.getUser());
      }

    }

    public BoardDetailRespDto(Board board) {
      this.id = board.getId();
      this.title = board.getTitle();
      this.content = board.getContent();
      this.user = new BoardUserDto(board.getUser());
      // List<CommentDto> 여기에 List<Comment>를 옮긴다.
      // 방법1
      // this.comment = board.getComments().stream().map((comment) -> new
      // CommentDto(comment))
      // .collect(Collectors.toList());

      // 방법2
      for (Comment c : board.getComments()) {
        this.comments.add(new CommentDto(c));
      }
    }

  }