댓글과 대댓글에 둘다 더보기 기능이 있을 때 두 부분 모두 페이징 처리를 해야했던 부분이 어려웠어서 저의 코드를 공유해보려고 합니다.
아직 미숙한 부분이 많아서 부끄럽지만 그동안 구글링의 도움을 많이 받은만큼 저도 누군가에게 도움이 되길 바라며 이 포스팅을 작성합니다.
Comment 엔티티
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Comment extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String content;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="users_id", nullable = false)
private Users users;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="post_id")
private Post post;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "comment", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Likes> likes;
@Column
private String redHeart = "false";
private Boolean isDeleted;
@Column
private Long rootId;
@Column
private String rootName;
@Column
private String parentName;
public Comment(CommentRequestDto requestDto, Post post, Users users, String redHeart) {
this.content = requestDto.getContents();
this.rootId = requestDto.getRootId();
this.rootName = requestDto.getRootName();
this.post = post;
this.users = users;
this.redHeart = "false";
}
public void update(String content) {
this.content = content;
}
//== 삭제 ==//
public void delete() {
this.isDeleted = true;
}
public boolean validateMember(Users users) {
return !this.users.equals(users);
}
public void setRedHeart(Users users, Long commentId){
this.redHeart = "true";
}
public void cancelRedHeart(Users users, Long commentId){
this.redHeart = "false";
}
public void setParentName(CommentResponseDto.CommentResponse comment){
this.parentName = comment.getUserName();
}
}
CommentRepository
import com.example.pinterestclone.domain.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query(" SELECT c FROM Comment c WHERE c.post.id = :postId and c.rootName = :rootName ")
Page<Comment> findAllByPostIdAndRootName( @Param("postId") Long postId,@Param("rootName") String rootName, Pageable pageable);
@Query(" SELECT c FROM Comment c WHERE c.post.id = :postId and c.rootName = :rootName ")
List<Comment> findAllByPostIdAndRootNameAndOrderByCreatedAt( @Param("postId") Long postId,@Param("rootName") String rootName);
@Query("SELECT c FROM Comment c WHERE c.post.id = :postId and c.rootName = :keyword ")
Page<Comment> findAllReCommentByPostIdAndRootNameLikeComment(@Param("postId") Long postId, @Param("keyword") String keyword, Pageable pageable);
int countByPostId(Long RootId);
@Query("SELECT c FROM Comment c WHERE c.id = :id ")
Comment findByCommentId(@Param("id") Long id);
서비스는 댓글을 불러오는 부분만 작성하겠습니다.
CommentService
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
@Getter
public class CommentSerivce {
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final LikesRepository likesRepository;
private final PostService postService;
private final TokenProvider tokenProvider;
(create, update, delete 생략)
//부모댓글만 가져오기
public List<CommentResponseDto.CommentResponse> getParentComment (Long postId, String rootName, int size, int page){
CommentResponseDto commentResponseDto = new CommentResponseDto();
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").ascending());
Page<Comment> parentCommentPage = commentRepository.findAllByPostIdAndRootName(postId, rootName, pageable);
List<Comment> parentComment = parentCommentPage.getContent();
int a = 0;
for(int i = 0; i < parentComment.size(); i++){
commentResponseDto.addComment(parentComment.get(i));
}
int totalCounts = commentRepository.countByPostId(postId);
int totalPages = parentCommentPage.getTotalPages();
int totalCountsInThisPage = parentCommentPage.getNumberOfElements();
int currentPage = page;
Boolean isLastPage = parentCommentPage.isLast();
Integer nextPage;
if (isLastPage){
nextPage = null;
}else{
nextPage = currentPage + 1;
}
commentResponseDto.addPagination(size, totalCountsInThisPage, totalPages, currentPage, nextPage, isLastPage);
return commentResponseDto.getComments();
}
// 자식 댓글만 가져오기
public List<ReCommentResponseDto.ReComment> getReCommentList (Long postId, int size, int page, Long commentId) {
ReCommentResponseDto reCommentResponseDto = new ReCommentResponseDto();
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").ascending());
Page<Comment> reCommentList = commentRepository.findAllReCommentByPostIdAndRootNameLikeComment(postId, "comment", pageable);
List<Comment> parentComment = commentRepository.findAllByPostIdAndRootNameAndOrderByCreatedAt(postId, "post");
CommentResponseDto commentResponseDto = new CommentResponseDto();
int a = 0;
int b = size * (page + 1);
for (Comment eachParent : parentComment) {
commentResponseDto.addComment(eachParent);
}
List<CommentResponseDto.CommentResponse> parentComments = commentResponseDto.getComments();
for (Comment eachComment : reCommentList) {
Long itsParentId = eachComment.getRootId();
if(itsParentId == commentId){
for( CommentResponseDto.CommentResponse parents : parentComments){
if (parents.getCommentId().equals(itsParentId)) {
int index = commentResponseDto.getComments().indexOf(parents);
eachComment.setParentName(parents);
reCommentResponseDto.addComment(index, eachComment);
}
} a +=1;
if(a==b) break;
}
}
return reCommentResponseDto.getComments();
}
// 자식댓글 가져올 때 부모댓글도 함께 가져오기
public CommentResponseDto getReComment (Long postId, String rootName, int size, int page, Long commentId) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").ascending());
List<Comment> reCommentList = commentRepository.findAllReCommentByPostIdAndRootNameLikeComment(postId, "comment");
List<Comment> parentComment = commentRepository.findAllByPostIdAndRootNameAndOrderByCreatedAt(postId, "post");
CommentResponseDto commentResponseDto = new CommentResponseDto();
int a = 0;
int b = size * (page + 1);
//부모댓글 불러오기
for (Comment eachParent : parentComment) {
commentResponseDto.addComment(eachParent);
}
List<CommentResponseDto.CommentResponse> parentComments = commentResponseDto.getComments();
// 대댓글이 현재 포스트에 있는 경우에만 반복문 실행
for (Comment eachComment : reCommentList) {
Long itsParentId = eachComment.getRootId();
if(itsParentId == commentId){
//부모댓글과 대댓글 매칭 시켜주기
for( CommentResponseDto.CommentResponse parents : parentComments){
if (parents.getCommentId().equals(itsParentId)) {
int index = commentResponseDto.getComments().indexOf(parents);
commentResponseDto.addReCommentImpl(index, eachComment);
}
} a +=1;
if(a==b) break;
}
}
return commentResponseDto;
}
CommentResponseDto는 두개로 나누었습니다.
하나로 처리하려고 하니까 부모댓글과 자식댓글의 값이 다르게 들어가서 어쩔 수 없이 나누게 되었습니다.
package com.example.pinterestclone.controller.response;
import com.example.pinterestclone.domain.Comment;
import com.example.pinterestclone.domain.Timestamped;
import com.example.pinterestclone.domain.Users;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Setter
@Getter
@NoArgsConstructor
public class CommentResponseDto extends Timestamped {
private List<CommentResponse> comments = new ArrayList<>();
public void addComment(Comment comment){
this.comments.add(new CommentResponse(comment));
}
public void addReCommentImpl(int index, Comment comment){
if(comment == null) {
System.out.println("comment is null");
}
this.comments.get(index).addReComment(new ReComment(comment));
}
private Pagination pagination;
public void addPagination(int size, int totalCounts, int totalPages,
int currentPage, Integer nextPage, Boolean isLastPage) {
this.pagination = new Pagination(size, totalCounts, totalPages,
currentPage, nextPage, isLastPage);
}
// 생성시간 순으로 오름차순 정렬
public int compareTo(CommentResponseDto o) {
return o.getCreatedAt().compareTo(o.getCreatedAt());
}
@Getter
public class CommentResponse implements Comparator<CommentResponse> {
private Pagination pagination;
private Long commentId;
private Long postId;
private Long userId;
private String userName;
private String uniqueName;
private String profileImage;
private String content;
private String redHeart;
private Integer likes;
// private Long nestedCommentsCount;
// 대댓글 리스트에 넣어주기
private List<ReComment> reComment = new ArrayList<>();
private Long reCommentsCount = 0L;
public LocalDateTime createdAt;
public LocalDateTime modifiedAt;
// comment와 user 정보 가져오기
public CommentResponse(Comment comment) {
this.commentId = comment.getId();
this.postId = comment.getPost().getId();
this.userId = comment.getUsers().getId();
this.userName = comment.getUsers().getUserId();
this.uniqueName = comment.getUsers().getUniqueName();
this.profileImage = comment.getUsers().getProfileImage();
// userOrigin.getIntroduce());
this.likes = comment.getLikes().size();
this.redHeart = comment.getRedHeart();
this.content = comment.getContent();
this.createdAt = comment.getCreatedAt();
this.modifiedAt = comment.getModifiedAt();
//this.isMine = isMine;
}
public void addReComment(ReComment reComment) {
this.reComment.add(reComment);
this.reCommentsCount += 1;
}
@Override
public int compare(CommentResponse o1, CommentResponse o2) {
Long first = Long.parseLong(String.valueOf(o1.createdAt));
Long second = Long.parseLong(String.valueOf(o2.createdAt));
if(first > second){
return 1;
}
else if(first < second){
return -1;
}
return 0;
}
}
@Getter
@Setter
public class ReComment {
private CommentResponseDto.Pagination pagination;
private Long commentId;
private Long parentId;
private Long postId;
private String parentName;
@JsonIgnore
private Users userOrigin;
private Long userId;
private String userName;
private String uniqueName;
private String content;
private String profileImage;
private Integer likes;
private String redHeart = "false";
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
private Boolean folded;
public String checkNull(String redHeart, String replace) {
replace = "false";
return (redHeart == null || redHeart.equals("")) ? replace : redHeart;
}
public ReComment(Comment comment) {
this.commentId = comment.getId();
this.parentId = comment.getRootId();
this.postId = comment.getPost().getId();
this.parentName = comment.getParentName();
this.userId = comment.getUsers().getId();
this.userName = comment.getUsers().getUserId();
this.uniqueName = comment.getUsers().getUniqueName();
this.profileImage = comment.getUsers().getProfileImage();
// userOrigin.getIntroduce());
this.likes = comment.getLikes().size();
this.redHeart = "false";
this.content = comment.getContent();
this.createdAt = comment.getCreatedAt();
this.modifiedAt = comment.getModifiedAt();
this.parentId = comment.getRootId();
//this.isMine = isMine;
}
}
@Getter
@AllArgsConstructor
public class Pagination {
private int per;
private int totalCounts;
private int totalPages;
private int currentPage;
private Integer nextPage;
private Boolean isLastPage;
}
}
ReCommentResponseDto
@Setter
@Getter
@NoArgsConstructor
public class ReCommentResponseDto {
private List<ReComment> comments = new ArrayList<>();
public void addComment( int index, Comment comment){
this.comments.add(new ReCommentResponseDto.ReComment(comment));
}
public void addPagination(int size, int totalCounts, int totalPages,
int currentPage, Integer nextPage, Boolean isLastPage) {
this.pagination = new Pagination(size, totalCounts, totalPages,
currentPage, nextPage, isLastPage);
}
private Pagination pagination;
public int compareTo(CommentResponseDto o) {
return o.getCreatedAt().compareTo(o.getCreatedAt());
}
@Getter
@Setter
public class ReComment implements Comparator<ReComment> {
private Long commentId;
private Long parentId;
private Long postId;
private String parentName;
private Long userId;
private String userName;
private String uniqueName;
private String content;
private String profileImage;
private int likes;
private String redHeart;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
private Pagination pagination;
public ReComment(Comment comment) {
this.commentId = comment.getId();
this.parentId = comment.getRootId();
this.postId = comment.getPost().getId();
this.parentName = comment.getParentName();
this.userId = comment.getUsers().getId();
this.userName = comment.getUsers().getUserId();
this.uniqueName = comment.getUsers().getUniqueName();
this.profileImage = comment.getUsers().getProfileImage();
// userOrigin.getIntroduce());
this.likes = comment.getLikes().size();
this.redHeart = comment.getRedHeart();
this.content = comment.getContent();
this.createdAt = comment.getCreatedAt();
this.modifiedAt = comment.getModifiedAt();
this.parentId = comment.getRootId();
//this.isMine = isMine;
}
@Override
public int compare(ReComment o1, ReComment o2) {
Long first = Long.parseLong(String.valueOf(o1.createdAt));
Long second = Long.parseLong(String.valueOf(o2.createdAt));
if(first > second){
return 1;
}
else if(first < second){
return -1;
}
return 0;
}
}
@Getter
@AllArgsConstructor
public class Pagination {
private int per;
private int totalCounts;
private int totalPages;
private int currentPage;
private Integer nextPage;
private Boolean isLastPage;
}
}
CommentRequestDto
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CommentRequestDto {
private String contents;
//댓글이면 postId, 대댓글이면 댓글id
private Long rootId;
private String rootName; //comment/post
}
CommentController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/api")
@RestController
public class CommentController {
private final CommentSerivce commentSerivce;
@GetMapping(value = "/comments/{rootId}")
// @Pagable을 통해 보여줄 페이지 위치(0이 시작), 한 페이지에 댓글 개수 2, 정렬 기준(createdAt), 정렬 기준의 순서(오름차순)을 정의
public ResponseDto<?> getAllComments(@PathVariable Long rootId, String keyword,
int size, int page, Long commentId) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").ascending());
if (keyword == null) {
return ResponseDto.success(commentSerivce.getParentComment(rootId, "post", size, page));
}else {
List<ReCommentResponseDto.ReComment> result = commentSerivce.getReCommentList(rootId, size, page, commentId);
return ResponseDto.success(result);
}
}
@GetMapping(value = "/comments/find/{commentId}")
public ResponseDto<?> getOneComment(@PathVariable Long commentId) {
return ResponseDto.success(commentSerivce.getOneComment(commentId));
}
// 좋아요 처리
@DeleteMapping("/comments/likes/{commentId}")
public ResponseDto<?> cancelLike(@LoginUsers Users loginUsers,
@PathVariable Long commentId) {
if (loginUsers != null) {
commentSerivce.cancelLike(loginUsers, commentId);
}
return ResponseDto.success(true);
}
@PostMapping("/comments/likes/{commentId}")
public ResponseDto<?> addLike(@LoginUsers Users loginUsers,
@PathVariable Long commentId) {
boolean result = false;
if (Objects.nonNull(loginUsers))
result = commentSerivce.addLike(loginUsers, commentId);
return result ?
ResponseDto.success(true) : ResponseDto.fail("false", "좋아요를 이미 하셨습니다.");
}
}
다른분 코드도 많이 참고했지만 제가 작성한 부분도 많아서 미흡한 부분이 있을거라고 생각합니다.
지적해주시면 감사드리겠습니다.