crew모임글에 댓글을 작성하는 로직을 추가해 보겠습니다.
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class Comment extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id") //외래키 : DB에 해당 명으로 컬럼설정
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id") //DB에 해당 명으로 컬럼설정
private Post post;
}
Authentication에서 UserName을 가져와 사용했습니다.
게시물에 댓글을 달것이여서 PostId를 PathVariable로 받아 service로 넘겨주었습니다.
//댓글 작성
@PostMapping("/{crewId}/comments")
public Response<CommentResponse> addComment(@RequestBody CommentRequest commentRequest, @PathVariable Long crewId, Authentication authentication) {
return Response.success(commentService.addComment(commentRequest, crewId, authentication.getName()));
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class CommentReplyRequest {
private String comment;
public Comment toEntity(User user, Crew crew, Comment parentComment) {
return Comment.builder()
.comment(this.comment)
.user(user)
.crew(crew)
.parent(parentComment)
.build();
}
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentResponse {
private Long id;
private String comment;
private String userName;
private Long crewId;
private LocalDateTime createdAt;
public static CommentResponse of(Comment comment) {
return CommentResponse.builder()
.id(comment.getId())
.comment(comment.getComment())
.userName(comment.getUser().getUsername())
.crewId(comment.getCrew().getId())
.createdAt(comment.getCreatedAt())
.build();
}
}
public CommentResponse addComment(CommentRequest commentRequest, Long crewId, String userName) {
User user = getUser(userName);
Crew crew = getCrew(crewId);
Comment comment = commentRepository.save(commentRequest.toEntity(user, crew));
return CommentResponse.of(comment);
}
public interface CommentRepository extends JpaRepository<Comment, Long> {
Page<Comment> findByCrewId(Long id, Pageable pageable);
Page<Comment> findByCrewIdAndParentId(Long id, Pageable pageable, Long parentId);
}
List를 조회할떄 Pageing 처리를 해주기 위해 따로 JPA메소드를 설정해 주었습니다.
추가적으로 대댓글의 정보도 볼 수 있도록 쿼리문을 하나 더 추가시켜 주었습니다.
이제 댓글을 작성 할 수 있습니다.
댓글기능을 만들었으니 대댓글도 만들어 보겠습니다.
처음에는 대댓글이라는 엔티티를 하나 더 만들 생각이었습니다.
하지만 댓글과 같은기능의 엔티티를 하나 더만들 필요가 있을까? 라는 생각이 들었고
구글링을 통해 셀프참조라는 방법을 알게되었습니다.
셀프 조인(Self Join)이란 동일 테이블 사이의 조인을 말합니다.
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
@Where(clause = "deleted_at is null")
public class Comment extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "crew_id")
Crew crew;
public void setComment(String comment) {
this.comment = comment;
}
// 아래 추가
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Comment parent;
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Comment> children = new ArrayList<>();
}
셀프 조인을 하기위해서 추가된것은 @ManyToOne
과 @OneToMany
을 통한 연관관계 맵핑밖에 없습니다.
대댓글을 작성할때, 부모인 댓글의 정보를 parent_id에 넣으면 됩니다.
@PostMapping("/{crewId}/comments/{parentCommentId}")
public Response<CommentReplyResponse> addCommentReply(@RequestBody CommentReplyRequest commentReplyRequest, @PathVariable Long crewId, @PathVariable Long parentCommentId, Authentication authentication) {
return Response.success(commentService.addCommentReply(commentReplyRequest, crewId, parentCommentId, authentication.getName()));
}
대댓글을 작성하기 위해서는 Entity에 parent필드에 들어가 부모댓글의 id도 알고있어야 합니다.
따라서 @PathVariable을 통해 Comment의 정보도 받아옵니다.
public CommentReplyResponse addCommentReply(CommentReplyRequest commentReplyRequest, Long crewId, Long parentCommentId, String userName) {
User user = getUser(userName);
Crew crew = getCrew(crewId);
//추가
Comment parentComment = getComment(parentCommentId);
Comment comment = commentRepository.save(commentReplyRequest.toEntity(user, crew, parentComment));
return CommentReplyResponse.of(comment);
}
기존 댓글에서 추가된 로직은 대댓글을 작성할 댓글을 찾는것입니다.
그리고 저장할때 찾은 Comment를 parent로 저장하면서 대댓글임을 표시하면 됩니다.
@GetMapping("/{crewId}/comments/{parentCommentId}/list")
public Response<Page<CommentReplyResponse>> getCommentReplyList(@PathVariable Long crewId, Long parentCommentId, Pageable pageable) {
return Response.success(commentService.getCommentReplyList(pageable, crewId, parentCommentId));
}
대댓글을 조회할 것이기 때문에, crewId와 commentId를 받아옵니다.
public Page<CommentReplyResponse> getCommentReplyList(Pageable pageable, Long crewId, Long parentCommentId) {
Page<Comment> list = commentRepository.findByCrewIdAndParentId(crewId, pageable, parentCommentId);
return list.map(CommentReplyResponse::of);
}
받아온 정보을 통해 정보를 찾고 .map을 통해 Comment를 CommentReplyResponse로 변경하여 리턴 합니다.
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentReplyResponse {
private Long id;
private String comment;
private String userName;
private Long crewId;
private Long parentId;
private LocalDateTime createdAt;
public static CommentReplyResponse of(Comment comment) {
return CommentReplyResponse.builder()
.id(comment.getId())
.comment(comment.getComment())
.userName(comment.getUser().getUsername())
.crewId(comment.getCrew().getId())
.createdAt(comment.getCreatedAt())
.parentId(comment.getParent().getId())
.build();
}
}