From 93fd2a4c2849083a3de63f60f7e7deb3ebb8a398 Mon Sep 17 00:00:00 2001 From: nYeonG4001 <2371324@hansung.ac.kr> Date: Sun, 12 Apr 2026 20:20:50 +0900 Subject: [PATCH] =?UTF-8?q?DP-309:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20recentAnswers=20=EB=8F=99?= =?UTF-8?q?=EC=9D=BC=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 한 게시글에 답변을 여러 개 달았을 때 recentAnswers에 동일 postId가 답변 수만큼 중복 포함되던 버그 수정. Collectors.toMap으로 postId 기준 중복 제거 (가장 최신 답변 유지). 중복 제거 검증 테스트 추가. --- .../user/dto/PublicUserProfileResponse.java | 17 ++++++--- .../domain/user/service/UserServiceTest.java | 35 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/devpick/domain/user/dto/PublicUserProfileResponse.java b/src/main/java/com/devpick/domain/user/dto/PublicUserProfileResponse.java index 193752c1..69ce6bd6 100644 --- a/src/main/java/com/devpick/domain/user/dto/PublicUserProfileResponse.java +++ b/src/main/java/com/devpick/domain/user/dto/PublicUserProfileResponse.java @@ -9,8 +9,10 @@ import java.time.Instant; import java.time.ZoneOffset; +import java.util.LinkedHashMap; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; public record PublicUserProfileResponse( UUID userId, @@ -50,11 +52,16 @@ public static PublicUserProfileResponse of( p.getCreatedAt() != null ? p.getCreatedAt().toInstant(ZoneOffset.UTC) : null)) .toList(), answers.stream() - .map(a -> new RecentAnswer( - a.getId(), - a.getPost().getId(), - a.getPost().getTitle(), - a.getCreatedAt() != null ? a.getCreatedAt().toInstant(ZoneOffset.UTC) : null)) + .collect(Collectors.toMap( + a -> a.getPost().getId(), + a -> new RecentAnswer( + a.getId(), + a.getPost().getId(), + a.getPost().getTitle(), + a.getCreatedAt() != null ? a.getCreatedAt().toInstant(ZoneOffset.UTC) : null), + (existing, replacement) -> existing, // 가장 최신 답변 유지 (쿼리가 createdAt DESC 순) + LinkedHashMap::new)) + .values().stream() .toList() ); } diff --git a/src/test/java/com/devpick/domain/user/service/UserServiceTest.java b/src/test/java/com/devpick/domain/user/service/UserServiceTest.java index 7e21f3d3..6817bfc5 100644 --- a/src/test/java/com/devpick/domain/user/service/UserServiceTest.java +++ b/src/test/java/com/devpick/domain/user/service/UserServiceTest.java @@ -145,6 +145,41 @@ void getPublicProfile_withCreatedAt_convertsToInstant() { assertThat(response.recentAnswers().get(0).createdAt()).isNotNull(); } + @Test + @DisplayName("getPublicProfile — 동일 게시글에 답변 여러 개일 때 recentAnswers에 게시글 1개만 포함") + void getPublicProfile_multipleAnswersOnSamePost_deduplicatesRecentAnswers() { + Post post = Post.builder().user(user).title("Spring 질문").content("내용").level(Level.JUNIOR).build(); + + Answer answer1 = Mockito.mock(Answer.class); + Answer answer2 = Mockito.mock(Answer.class); + UUID postId = UUID.randomUUID(); + UUID answer1Id = UUID.randomUUID(); + UUID answer2Id = UUID.randomUUID(); + + Post mockPost = Mockito.mock(Post.class); + Mockito.when(mockPost.getId()).thenReturn(postId); + Mockito.when(mockPost.getTitle()).thenReturn("Spring 질문"); + + Mockito.when(answer1.getId()).thenReturn(answer1Id); + Mockito.when(answer1.getPost()).thenReturn(mockPost); + Mockito.when(answer1.getCreatedAt()).thenReturn(null); + + Mockito.when(answer2.getId()).thenReturn(answer2Id); + Mockito.when(answer2.getPost()).thenReturn(mockPost); + Mockito.when(answer2.getCreatedAt()).thenReturn(null); + + given(userRepository.findByIdAndIsActiveTrue(userId)).willReturn(Optional.of(user)); + given(userBadgeRepository.findByUser_IdOrderByAcquiredAtDesc(userId)).willReturn(List.of()); + given(postRepository.findByUser_IdOrderByCreatedAtDesc(userId)).willReturn(List.of(post)); + given(answerRepository.findByUserIdWithPost(userId)).willReturn(List.of(answer1, answer2)); + + PublicUserProfileResponse response = userService.getPublicProfile(userId); + + assertThat(response.recentAnswers()).hasSize(1); + assertThat(response.recentAnswers().get(0).postId()).isEqualTo(postId); + assertThat(response.recentAnswers().get(0).answerId()).isEqualTo(answer1Id); // 첫 번째(최신) 답변 유지 + } + @Test @DisplayName("getPublicProfile — 비활성 사용자 USER_NOT_FOUND 예외") void getPublicProfile_inactiveUser_throwsUserNotFound() {