Skip to content

Commit 92e375c

Browse files
committed
feat: 포스트 댓글 조회 기능 구현
1 parent cd5626b commit 92e375c

File tree

6 files changed

+162
-10
lines changed

6 files changed

+162
-10
lines changed

src/main/generated/com/daramg/server/user/domain/QUser.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public class QUser extends EntityPathBase<User> {
3131
//inherited
3232
public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt;
3333

34+
public final DateTimePath<java.time.LocalDateTime> deletedAt = createDateTime("deletedAt", java.time.LocalDateTime.class);
35+
3436
public final StringPath email = createString("email");
3537

3638
public final NumberPath<Integer> followerCount = createNumber("followerCount", Integer.class);
@@ -51,6 +53,8 @@ public class QUser extends EntityPathBase<User> {
5153
//inherited
5254
public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt;
5355

56+
public final EnumPath<UserStatus> userStatus = createEnum("userStatus", UserStatus.class);
57+
5458
public QUser(String variable) {
5559
super(User.class, forVariable(variable));
5660
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.daramg.server.comment.dto;
2+
3+
import com.daramg.server.comment.domain.Comment;
4+
5+
import java.time.LocalDateTime;
6+
import java.util.List;
7+
8+
public record CommentResponseDto(
9+
Long id,
10+
String content,
11+
boolean isDeleted,
12+
int likeCount,
13+
int childCommentCount,
14+
LocalDateTime createdAt,
15+
String writerNickname,
16+
String writerProfileImage,
17+
Boolean isLiked,
18+
List<ChildCommentResponseDto> childComments
19+
) {
20+
21+
public static CommentResponseDto from(Comment comment, Boolean isLiked, List<ChildCommentResponseDto> childComments) {
22+
int childCommentCount = childComments != null ? childComments.size() : 0;
23+
24+
return new CommentResponseDto(
25+
comment.getId(),
26+
comment.getContent(),
27+
comment.isDeleted(),
28+
comment.getLikeCount(),
29+
childCommentCount,
30+
comment.getCreatedAt(),
31+
comment.getUser().getNickname(),
32+
comment.getUser().getProfileImage(),
33+
isLiked,
34+
childComments
35+
);
36+
}
37+
38+
public record ChildCommentResponseDto(
39+
Long id,
40+
String content,
41+
boolean isDeleted,
42+
int likeCount,
43+
LocalDateTime createdAt,
44+
String writerNickname,
45+
String writerProfileImage,
46+
Boolean isLiked
47+
) {
48+
49+
public static ChildCommentResponseDto from(Comment comment, Boolean isLiked) {
50+
return new ChildCommentResponseDto(
51+
comment.getId(),
52+
comment.getContent(),
53+
comment.isDeleted(),
54+
comment.getLikeCount(),
55+
comment.getCreatedAt(),
56+
comment.getUser().getNickname(),
57+
comment.getUser().getProfileImage(),
58+
isLiked
59+
);
60+
}
61+
}
62+
}
63+

src/main/java/com/daramg/server/comment/repository/CommentLikeRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
import com.daramg.server.comment.domain.CommentLike;
44
import org.springframework.data.jpa.repository.JpaRepository;
55

6+
import java.util.List;
7+
68
public interface CommentLikeRepository extends JpaRepository<CommentLike, Long> {
79
boolean existsByCommentIdAndUserId(Long commentId, Long userId);
810
void deleteByCommentIdAndUserId(Long commentId, Long userId);
911
void deleteAllByCommentId(Long commentId);
12+
List<CommentLike> findByCommentIdInAndUserId(Iterable<Long> commentIds, Long userId);
1013
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
package com.daramg.server.comment.repository;
22

33
import com.daramg.server.comment.domain.Comment;
4+
import org.springframework.data.jpa.repository.EntityGraph;
45
import org.springframework.data.jpa.repository.JpaRepository;
56

7+
import java.util.List;
8+
69
public interface CommentRepository extends JpaRepository<Comment, Long> {
10+
11+
@EntityGraph(attributePaths = {"user", "childComments", "childComments.user"})
12+
List<Comment> findByPostIdAndIsBlockedFalseOrderByCreatedAtAsc(Long postId);
713
}

src/main/java/com/daramg/server/post/application/PostQueryService.java

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.daramg.server.post.application;
22

3+
import com.daramg.server.comment.domain.Comment;
4+
import com.daramg.server.comment.domain.CommentLike;
5+
import com.daramg.server.comment.dto.CommentResponseDto;
6+
import com.daramg.server.comment.repository.CommentLikeRepository;
7+
import com.daramg.server.comment.repository.CommentRepository;
38
import com.daramg.server.common.application.EntityUtils;
9+
import com.daramg.server.common.domain.BaseEntity;
410
import com.daramg.server.common.dto.PageRequestDto;
511
import com.daramg.server.common.dto.PageResponseDto;
612
import com.daramg.server.common.util.PagingUtils;
@@ -22,8 +28,8 @@
2228
import org.springframework.stereotype.Service;
2329
import org.springframework.transaction.annotation.Transactional;
2430

25-
import java.util.List;
26-
import java.util.Set;
31+
import java.util.*;
32+
import java.util.stream.Collectors;
2733

2834
@Service
2935
@RequiredArgsConstructor
@@ -36,6 +42,8 @@ public class PostQueryService {
3642
private final PagingUtils pagingUtils;
3743
private final EntityUtils entityUtils;
3844
private final ComposerLikeRepository composerLikeRepository;
45+
private final CommentRepository commentRepository;
46+
private final CommentLikeRepository commentLikeRepository;
3947

4048
public PageResponseDto<PostResponseDto> getAllPublishedFreePosts(PageRequestDto pageRequest, User user){
4149
List<FreePost> posts = postQueryRepository.getAllFreePostsWithPaging(pageRequest);
@@ -113,7 +121,11 @@ public PostDetailResponse getPostById(Long postId, User user) {
113121
Post post = entityUtils.getEntity(postId, Post.class);
114122
Boolean isLiked = user != null ? postLikeRepository.existsByPostIdAndUserId(postId, user.getId()) : null;
115123
Boolean isScrapped = user != null ? postScrapRepository.existsByPostIdAndUserId(postId, user.getId()) : null;
116-
return PostDetailResponse.from(post, isLiked, isScrapped);
124+
List<Comment> comments = commentRepository.findByPostIdAndIsBlockedFalseOrderByCreatedAtAsc(postId);
125+
126+
List<CommentResponseDto> commentDtos = mapCommentsWithChildren(comments, user);
127+
128+
return PostDetailResponse.from(post, isLiked, isScrapped, commentDtos);
117129
}
118130

119131
public ComposerWithPostsResponseDto getComposerWithPosts(Long composerId, PageRequestDto pageRequest, User user) {
@@ -164,17 +176,72 @@ private PostResponseDto toPostResponseDto(Post post, User user, Set<Long> likedP
164176

165177
private Set<Long> getLikedPostIds(List<Post> posts, User user) {
166178
if (user == null || posts.isEmpty()) {
167-
return java.util.Collections.emptySet();
179+
return Collections.emptySet();
168180
}
169181
List<Long> postIds = posts.stream().map(Post::getId).toList();
170182
return postLikeRepository.findPostIdsByPostIdsAndUserId(postIds, user.getId());
171183
}
172184

173185
private Set<Long> getScrappedPostIds(List<Post> posts, User user) {
174186
if (user == null || posts.isEmpty()) {
175-
return java.util.Collections.emptySet();
187+
return Collections.emptySet();
176188
}
177189
List<Long> postIds = posts.stream().map(Post::getId).toList();
178190
return postScrapRepository.findPostIdsByPostIdsAndUserId(postIds, user.getId());
179191
}
192+
193+
private List<CommentResponseDto> mapCommentsWithChildren(List<Comment> allComments, User user) {
194+
if (allComments.isEmpty()) {
195+
return List.of();
196+
}
197+
198+
// 부모/자식 댓글 분리 (작성시간 오름차순 정렬)
199+
List<Comment> parentComments = allComments.stream()
200+
.filter(comment -> comment.getParentComment() == null)
201+
.sorted(Comparator.comparing((Comment c) -> c.getCreatedAt()).thenComparingLong(BaseEntity::getId))
202+
.toList();
203+
204+
Map<Long, List<Comment>> childrenByParentId = allComments.stream()
205+
.filter(comment -> comment.getParentComment() != null)
206+
.collect(Collectors.groupingBy(comment -> comment.getParentComment().getId()));
207+
208+
// 현재 로그인 유저가 좋아요한 댓글 ID 모음
209+
Set<Long> likedCommentIds = getLikedCommentIds(allComments, user);
210+
211+
return parentComments.stream()
212+
.map(parent -> {
213+
List<Comment> children = childrenByParentId.getOrDefault(parent.getId(), List.of())
214+
.stream()
215+
.sorted(Comparator.comparing((Comment c) -> c.getCreatedAt()).thenComparingLong(BaseEntity::getId))
216+
.toList();
217+
218+
List<CommentResponseDto.ChildCommentResponseDto> childDtos = children.stream()
219+
.map(child -> CommentResponseDto.ChildCommentResponseDto.from(
220+
child,
221+
user != null && likedCommentIds.contains(child.getId())
222+
))
223+
.toList();
224+
225+
Boolean isParentLiked = user != null ? likedCommentIds.contains(parent.getId()) : null;
226+
227+
return CommentResponseDto.from(parent, isParentLiked, childDtos);
228+
})
229+
.toList();
230+
}
231+
232+
private Set<Long> getLikedCommentIds(List<Comment> comments, User user) {
233+
if (user == null || comments.isEmpty()) {
234+
return Collections.emptySet();
235+
}
236+
237+
List<Long> commentIds = comments.stream()
238+
.map(Comment::getId)
239+
.toList();
240+
241+
List<CommentLike> likes = commentLikeRepository.findByCommentIdInAndUserId(commentIds, user.getId());
242+
243+
return likes.stream()
244+
.map(like -> like.getComment().getId())
245+
.collect(Collectors.toSet());
246+
}
180247
}

src/main/java/com/daramg/server/post/dto/PostDetailResponse.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.daramg.server.common.exception.BusinessException;
44
import com.daramg.server.composer.domain.Composer;
5+
import com.daramg.server.comment.dto.CommentResponseDto;
56
import com.daramg.server.post.domain.CurationPost;
67
import com.daramg.server.post.domain.FreePost;
78
import com.daramg.server.post.domain.Post;
@@ -15,6 +16,8 @@
1516

1617
public record PostDetailResponse(
1718
Long id,
19+
String writerNickname,
20+
String writerProfileImage,
1821
String title,
1922
String content,
2023
List<String> images,
@@ -26,18 +29,22 @@ public record PostDetailResponse(
2629
boolean isBlocked,
2730
LocalDateTime createdAt,
2831
LocalDateTime updatedAt,
29-
String writerNickname,
3032
PostType type,
3133
ComposerInfo primaryComposer,
3234
List<ComposerInfo> additionalComposers,
3335
Boolean isLiked,
34-
Boolean isScrapped
36+
Boolean isScrapped,
37+
List<CommentResponseDto> comments
3538
) {
3639
public static PostDetailResponse from(Post post) {
37-
return from(post, null, null);
40+
return from(post, null, null, List.of());
3841
}
3942

4043
public static PostDetailResponse from(Post post, Boolean isLiked, Boolean isScrapped) {
44+
return from(post, isLiked, isScrapped, List.of());
45+
}
46+
47+
public static PostDetailResponse from(Post post, Boolean isLiked, Boolean isScrapped, List<CommentResponseDto> comments) {
4148
PostType type = getPostType(post);
4249
ComposerInfo primaryComposer = null;
4350
List<ComposerInfo> additionalComposers = null;
@@ -59,6 +66,8 @@ public static PostDetailResponse from(Post post, Boolean isLiked, Boolean isScra
5966

6067
return new PostDetailResponse(
6168
post.getId(),
69+
post.getUser().getNickname(),
70+
post.getUser().getProfileImage(),
6271
post.getTitle(),
6372
post.getContent(),
6473
post.getImages(),
@@ -70,12 +79,12 @@ public static PostDetailResponse from(Post post, Boolean isLiked, Boolean isScra
7079
post.isBlocked(),
7180
post.getCreatedAt(),
7281
post.getUpdatedAt(),
73-
post.getUser().getNickname(),
7482
type,
7583
primaryComposer,
7684
additionalComposers,
7785
isLiked,
78-
isScrapped
86+
isScrapped,
87+
comments
7988
);
8089
}
8190

0 commit comments

Comments
 (0)