diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/repository/QuestionLikeRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/question/repository/QuestionLikeRepository.java index 9fb38f3..2d3cb3a 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/repository/QuestionLikeRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/repository/QuestionLikeRepository.java @@ -4,7 +4,10 @@ import com.example.Piroin.project.domain.question.entity.QuestionLike; import com.example.Piroin.project.domain.user.entity.User; 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; import java.util.Optional; public interface QuestionLikeRepository extends JpaRepository { @@ -19,4 +22,12 @@ public interface QuestionLikeRepository extends JpaRepository findLikedQuestionIdsByQuestionIdsAndUser( + @Param("questionIds") List questionIds, + @Param("user") User user + ); } \ No newline at end of file diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index 1f0fa78..af58000 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java @@ -27,7 +27,9 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Set; import java.util.Map; +import java.util.HashSet; import java.util.stream.Collectors; @Service @@ -589,7 +591,7 @@ private QuestionResDTO.UnderstandingCheckResponse toUnderstandingCheckResponse( private QuestionResDTO.QuestionGroupsResponse getQuestionGroups(StudySession session, User loginUser) { List questions = questionRepository.findBySessionAndDeletedAtIsNull(session); - QuestionSummaryContext summaryContext = getQuestionSummaryContext(questions); + QuestionSummaryContext summaryContext = getQuestionSummaryContext(questions, loginUser); List popularQuestions = questions.stream() .filter(q -> !q.getIsResolved() && q.getLikeCount() >= POPULAR_LIKE_THRESHOLD) @@ -616,7 +618,7 @@ private QuestionResDTO.QuestionSummaryResponse toQuestionSummaryResponse ( User loginUser ) { Long questionId = question.getId(); - boolean isLiked = questionLikeRepository.existsByQuestionAndUser(question, loginUser); + boolean isLiked = summaryContext.likedQuestionIds().contains(questionId); boolean isMine = question.getUser().getId().equals(loginUser.getId()); return new QuestionResDTO.QuestionSummaryResponse( questionId, question.getContent(), question.getImageUrl(), @@ -632,9 +634,9 @@ private QuestionResDTO.QuestionSummaryResponse toQuestionSummaryResponse ( ); } - private QuestionSummaryContext getQuestionSummaryContext(List questions) { + private QuestionSummaryContext getQuestionSummaryContext(List questions, User loginUser) { if (questions.isEmpty()) { - return new QuestionSummaryContext(Map.of(), Map.of()); + return new QuestionSummaryContext(Map.of(), Map.of(), Set.of()); } List questionIds = questions.stream() @@ -651,14 +653,17 @@ private QuestionSummaryContext getQuestionSummaryContext(List question questionCommentRepository.findPreviewCommentsByQuestionIds(questionIds) .forEach(row -> { Question question = questionsById.get(row.getQuestionId()); - if (question == null) { - return; - } + if (question == null) return; previewComments.computeIfAbsent(row.getQuestionId(), key -> new ArrayList<>()) .add(toPreviewCommentResponse(question, row)); }); - return new QuestionSummaryContext(commentCounts, previewComments); + // 좋아요 여부를 질문마다 조회하는 대신 한 번에 배치 조회한다. + Set likedQuestionIds = new HashSet<>( + questionLikeRepository.findLikedQuestionIdsByQuestionIdsAndUser(questionIds, loginUser) + ); + + return new QuestionSummaryContext(commentCounts, previewComments, likedQuestionIds); } private QuestionResDTO.PreviewCommentResponse toPreviewCommentResponse( @@ -692,16 +697,26 @@ private String getPreviewDisplayName(Question question, QuestionCommentRepositor private void publishCommentCreatedEventAfterCommit(Question question) { Long sessionId = question.getSession().getId(); Long questionId = question.getId(); - QuestionSummaryContext summaryContext = getQuestionSummaryContext(List.of(question)); - // 프론트가 전체 목록을 다시 조회하지 않고 해당 질문만 갱신할 수 있는 최소 데이터만 보낸다. + // 이벤트 발행용이라 좋아요 여부가 필요 없으므로 댓글 수/미리보기만 직접 조회 + List questionIds = List.of(questionId); + + Map commentCounts = new HashMap<>(); + questionCommentRepository.countByQuestionIds(questionIds) + .forEach(row -> commentCounts.put(row.getQuestionId(), Math.toIntExact(row.getCommentCount()))); + + Map> previewComments = new HashMap<>(); + questionCommentRepository.findPreviewCommentsByQuestionIds(questionIds) + .forEach(row -> previewComments.computeIfAbsent(row.getQuestionId(), key -> new ArrayList<>()) + .add(toPreviewCommentResponse(question, row))); + QuestionResDTO.CommentCreatedEvent event = new QuestionResDTO.CommentCreatedEvent( "COMMENT_CREATED", sessionId, questionId, question.getIsResolved(), - summaryContext.commentCounts().getOrDefault(questionId, 0), - summaryContext.previewComments().getOrDefault(questionId, List.of()) + commentCounts.getOrDefault(questionId, 0), + previewComments.getOrDefault(questionId, List.of()) ); publishAfterCommit(() -> questionEventService.publishCommentCreated(sessionId, event)); @@ -774,7 +789,8 @@ public void afterCommit() { private record QuestionSummaryContext( Map commentCounts, - Map> previewComments + Map> previewComments, + Set likedQuestionIds ) { } }