From c9bdf8cd30344fffa5071bae2efb8063991f3e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=98=81=ED=83=9C?= <56019823+dudxo@users.noreply.github.com> Date: Sun, 18 Aug 2024 03:34:43 +0900 Subject: [PATCH] =?UTF-8?q?[feat=20#42]=20=EC=8A=A4=ED=81=AC=EB=9E=A9=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] : 스크랩 전제 조회 관려 에러코드 추가 * [feat] : 스크랩 전체 조회 응답 DTO 추가 * [feat] : 스크랩 전체 조회 QueryDSL 추가 * [feat] : 스크랩 전체 조회 API Controller/Service 로직 추가 * [test] : InteractionFixture 정적 생성메서드 추가 * [test] : 스크랩 전체 조회 Controller/Repository 테스트 추가 * [style] : 코드 포맷팅 * [rename] : 마이페이지 게시글 관련 에러코드 통일 및 메시지 내용 변경 * [fix] : 마이페이지 게시글 조회 관련 API Path 일관성을 위한 수정 * [test] : 마이페이지 게시글 조회 관련 API 변경으로 인한 수정 * [fix] : 마이페이지 게시글 조회 시 updatedAt -> createdAt 정렬 순으로 변경 * [fix] : Coalesce를 통한 null 값 처리 --- .../member/controller/MemberController.java | 15 +++- ...AnsweredQuestionPostsByMemberResponse.java | 13 +-- .../response/BookmarksByMemberResponse.java | 35 ++++++++ .../QuestionPostsByMemberResponse.java | 14 +--- .../member/exception/MemberErrorCode.java | 3 +- .../member/repository/MemberCustom.java | 3 + .../member/repository/MemberCustomImpl.java | 52 +++++++++++- .../member/service/MemberService.java | 15 +++- .../common/fixture/InteractionFixture.java | 13 +++ .../controller/MemberControllerTest.java | 41 +++++++++- .../repository/MemberRepositoryTest.java | 79 ++++++++++++++----- 11 files changed, 234 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/dnd/gongmuin/member/dto/response/BookmarksByMemberResponse.java diff --git a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java index 969eef3b..84baffef 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -13,6 +13,7 @@ import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.request.UpdateMemberProfileRequest; import com.dnd.gongmuin.member.dto.response.AnsweredQuestionPostsByMemberResponse; +import com.dnd.gongmuin.member.dto.response.BookmarksByMemberResponse; import com.dnd.gongmuin.member.dto.response.MemberProfileResponse; import com.dnd.gongmuin.member.dto.response.QuestionPostsByMemberResponse; import com.dnd.gongmuin.member.service.MemberService; @@ -64,7 +65,7 @@ public ResponseEntity> getQuestionPo @Operation(summary = "댓글 단 질문 전체 조회 API", description = "댓글 단 질문을 전체 조회한다.") @ApiResponse(useReturnTypeSchema = true) - @GetMapping("/answer-posts") + @GetMapping("/question-posts/answers") public ResponseEntity> getAnsweredQuestionPostsByMember( @AuthenticationPrincipal Member member, Pageable pageable) { @@ -74,4 +75,16 @@ public ResponseEntity> getAn return ResponseEntity.ok(response); } + @Operation(summary = "스크랩 질문 전체 조회 API", description = "스크랩한 질문을 전체 조회한다.") + @ApiResponse(useReturnTypeSchema = true) + @GetMapping("/question-posts/bookmarks") + public ResponseEntity> getBookmarksByMember( + @AuthenticationPrincipal Member member, + Pageable pageable) { + PageResponse response = + memberService.getBookmarksByMember(member, pageable); + + return ResponseEntity.ok(response); + } + } diff --git a/src/main/java/com/dnd/gongmuin/member/dto/response/AnsweredQuestionPostsByMemberResponse.java b/src/main/java/com/dnd/gongmuin/member/dto/response/AnsweredQuestionPostsByMemberResponse.java index 49669d31..70440eb5 100644 --- a/src/main/java/com/dnd/gongmuin/member/dto/response/AnsweredQuestionPostsByMemberResponse.java +++ b/src/main/java/com/dnd/gongmuin/member/dto/response/AnsweredQuestionPostsByMemberResponse.java @@ -1,7 +1,6 @@ package com.dnd.gongmuin.member.dto.response; import com.dnd.gongmuin.answer.domain.Answer; -import com.dnd.gongmuin.post_interaction.domain.InteractionCount; import com.dnd.gongmuin.question_post.domain.QuestionPost; import com.querydsl.core.annotations.QueryProjection; @@ -23,8 +22,8 @@ public record AnsweredQuestionPostsByMemberResponse( @QueryProjection public AnsweredQuestionPostsByMemberResponse( QuestionPost questionPost, - InteractionCount savedCount, - InteractionCount recommendCount, + int savedTotalCount, + int recommendTotalCount, Answer answer) { this( questionPost.getId(), @@ -34,15 +33,11 @@ public AnsweredQuestionPostsByMemberResponse( questionPost.getReward(), questionPost.getUpdatedAt().toString(), questionPost.getIsChosen(), - extractTotalCount(savedCount), - extractTotalCount(recommendCount), + savedTotalCount, + recommendTotalCount, answer.getId(), answer.getContent(), answer.getUpdatedAt().toString() ); } - - private static int extractTotalCount(InteractionCount interactionCount) { - return interactionCount != null ? interactionCount.getCount() : 0; - } } diff --git a/src/main/java/com/dnd/gongmuin/member/dto/response/BookmarksByMemberResponse.java b/src/main/java/com/dnd/gongmuin/member/dto/response/BookmarksByMemberResponse.java new file mode 100644 index 00000000..40070b75 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/member/dto/response/BookmarksByMemberResponse.java @@ -0,0 +1,35 @@ +package com.dnd.gongmuin.member.dto.response; + +import com.dnd.gongmuin.question_post.domain.QuestionPost; +import com.querydsl.core.annotations.QueryProjection; + +public record BookmarksByMemberResponse( + Long questionPostId, + String title, + String content, + String jobGroup, + int reward, + String updatedAt, + boolean isChosen, + int savedTotalCount, + int recommendTotalCount +) { + @QueryProjection + public BookmarksByMemberResponse( + QuestionPost questionPost, + int savedTotalCount, + int recommendTotalCount + ) { + this( + questionPost.getId(), + questionPost.getTitle(), + questionPost.getContent(), + questionPost.getJobGroup().getLabel(), + questionPost.getReward(), + questionPost.getUpdatedAt().toString(), + questionPost.getIsChosen(), + savedTotalCount, + recommendTotalCount + ); + } +} diff --git a/src/main/java/com/dnd/gongmuin/member/dto/response/QuestionPostsByMemberResponse.java b/src/main/java/com/dnd/gongmuin/member/dto/response/QuestionPostsByMemberResponse.java index ea4a0ee1..17c53bff 100644 --- a/src/main/java/com/dnd/gongmuin/member/dto/response/QuestionPostsByMemberResponse.java +++ b/src/main/java/com/dnd/gongmuin/member/dto/response/QuestionPostsByMemberResponse.java @@ -1,6 +1,5 @@ package com.dnd.gongmuin.member.dto.response; -import com.dnd.gongmuin.post_interaction.domain.InteractionCount; import com.dnd.gongmuin.question_post.domain.QuestionPost; import com.querydsl.core.annotations.QueryProjection; @@ -19,8 +18,8 @@ public record QuestionPostsByMemberResponse( @QueryProjection public QuestionPostsByMemberResponse( QuestionPost questionPost, - InteractionCount savedCount, - InteractionCount recommendCount + int savedTotalCount, + int recommendTotalCount ) { this( questionPost.getId(), @@ -30,13 +29,8 @@ public QuestionPostsByMemberResponse( questionPost.getReward(), questionPost.getUpdatedAt().toString(), questionPost.getIsChosen(), - extractTotalCount(savedCount), - extractTotalCount(recommendCount) + savedTotalCount, + recommendTotalCount ); } - - private static int extractTotalCount(InteractionCount interactionCount) { - return interactionCount != null ? interactionCount.getCount() : 0; - } - } diff --git a/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java b/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java index fa1133df..4451bf2e 100644 --- a/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java +++ b/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java @@ -14,8 +14,7 @@ public enum MemberErrorCode implements ErrorCode { LOGOUT_FAILED("로그아웃을 실패했습니다.", "MEMBER_003"), NOT_ENOUGH_CREDIT("보유한 크레딧이 부족합니다.", "MEMBER_004"), UPDATE_PROFILE_FAILED("프로필 수정에 실패했습니다.", "MEMBER_005"), - QUESTION_POSTS_BY_MEMBER_FAILED("작성한 게시글 목록을 찾는 도중 실패했습니다.", "MEMBER_006"), - ANSWERED_QUESTION_POSTS_BY_MEMBER_FAILED("댓글 단 게시글 목록을 찾는 도중 실패했습니다.", "MEMBER_007"); + QUESTION_POSTS_BY_MEMBER_FAILED("마이페이지 게시글 목록을 불러오는데 실패했습니다", "MEMBER_006"); private final String message; private final String code; diff --git a/src/main/java/com/dnd/gongmuin/member/repository/MemberCustom.java b/src/main/java/com/dnd/gongmuin/member/repository/MemberCustom.java index 70a47e30..50343bca 100644 --- a/src/main/java/com/dnd/gongmuin/member/repository/MemberCustom.java +++ b/src/main/java/com/dnd/gongmuin/member/repository/MemberCustom.java @@ -5,10 +5,13 @@ import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.response.AnsweredQuestionPostsByMemberResponse; +import com.dnd.gongmuin.member.dto.response.BookmarksByMemberResponse; import com.dnd.gongmuin.member.dto.response.QuestionPostsByMemberResponse; public interface MemberCustom { Slice getQuestionPostsByMember(Member member, Pageable pageable); Slice getAnsweredQuestionPostsByMember(Member member, Pageable pageable); + + Slice getBookmarksByMember(Member member, Pageable pageable); } diff --git a/src/main/java/com/dnd/gongmuin/member/repository/MemberCustomImpl.java b/src/main/java/com/dnd/gongmuin/member/repository/MemberCustomImpl.java index 020ece4b..e0800a4b 100644 --- a/src/main/java/com/dnd/gongmuin/member/repository/MemberCustomImpl.java +++ b/src/main/java/com/dnd/gongmuin/member/repository/MemberCustomImpl.java @@ -9,10 +9,13 @@ import com.dnd.gongmuin.answer.domain.QAnswer; import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.response.AnsweredQuestionPostsByMemberResponse; +import com.dnd.gongmuin.member.dto.response.BookmarksByMemberResponse; import com.dnd.gongmuin.member.dto.response.QAnsweredQuestionPostsByMemberResponse; +import com.dnd.gongmuin.member.dto.response.QBookmarksByMemberResponse; import com.dnd.gongmuin.member.dto.response.QQuestionPostsByMemberResponse; import com.dnd.gongmuin.member.dto.response.QuestionPostsByMemberResponse; import com.dnd.gongmuin.post_interaction.domain.InteractionType; +import com.dnd.gongmuin.post_interaction.domain.QInteraction; import com.dnd.gongmuin.post_interaction.domain.QInteractionCount; import com.dnd.gongmuin.question_post.domain.QQuestionPost; import com.querydsl.jpa.JPAExpressions; @@ -32,14 +35,18 @@ public Slice getQuestionPostsByMember(Member memb QInteractionCount recommend = new QInteractionCount("RECOMMEND"); List content = queryFactory - .select(new QQuestionPostsByMemberResponse(qp, saved, recommend)) + .select(new QQuestionPostsByMemberResponse( + qp, + saved.count.coalesce(0).as("savedTotalCount"), + recommend.count.coalesce(0).as("recommendTotalCount") + )) .from(qp) .leftJoin(saved) .on(qp.id.eq(saved.questionPostId).and(saved.type.eq(InteractionType.SAVED))) .leftJoin(recommend) .on(qp.id.eq(recommend.questionPostId).and(recommend.type.eq(InteractionType.RECOMMEND))) .where(qp.member.eq(member)) - .orderBy(qp.updatedAt.desc()) + .orderBy(qp.createdAt.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize() + 1L) .fetch(); @@ -60,7 +67,12 @@ public Slice getAnsweredQuestionPostsByMe List content = queryFactory - .select(new QAnsweredQuestionPostsByMemberResponse(qp, saved, recommend, aw1)) + .select(new QAnsweredQuestionPostsByMemberResponse( + qp, + saved.count.coalesce(0).as("savedTotalCount"), + recommend.count.coalesce(0).as("recommendTotalCount"), + aw1 + )) .from(qp) .join(aw1) .on(aw1.id.eq( @@ -81,7 +93,7 @@ public Slice getAnsweredQuestionPostsByMe .on(qp.id.eq(saved.questionPostId).and(saved.type.eq(InteractionType.SAVED))) .leftJoin(recommend) .on(qp.id.eq(recommend.questionPostId).and(recommend.type.eq(InteractionType.RECOMMEND))) - .orderBy(qp.updatedAt.desc()) + .orderBy(qp.createdAt.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize() + 1L) .fetch(); @@ -91,6 +103,38 @@ public Slice getAnsweredQuestionPostsByMe return new SliceImpl<>(content, pageable, hasNext); } + @Override + public Slice getBookmarksByMember(Member member, Pageable pageable) { + QQuestionPost qp = QQuestionPost.questionPost; + QInteraction ir = QInteraction.interaction; + QInteractionCount saved = new QInteractionCount("SAVED"); + QInteractionCount recommend = new QInteractionCount("RECOMMEND"); + + List content = queryFactory + .select(new QBookmarksByMemberResponse( + qp, + saved.count.coalesce(0).as("savedTotalCount"), + recommend.count.coalesce(0).as("recommendTotalCount") + )) + .from(qp) + .join(ir) + .on(qp.id.eq(ir.questionPostId).and(ir.type.eq(InteractionType.SAVED))) + .leftJoin(saved) + .on(qp.id.eq(saved.questionPostId).and(saved.type.eq(InteractionType.SAVED))) + .leftJoin(recommend) + .on(qp.id.eq(recommend.questionPostId).and(recommend.type.eq(InteractionType.RECOMMEND))) + .where(ir.memberId.eq(member.getId())) + .orderBy(qp.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1L) + .fetch(); + + boolean hasNext = hasNext(pageable.getPageSize(), content); + + return new SliceImpl<>(content, pageable, hasNext); + } + + // .on(qp.id.eq(ir.questionPostId).and(ir.type.eq(InteractionType.SAVED))) private boolean hasNext(int pageSize, List content) { if (content.size() <= pageSize) { return false; diff --git a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java index 350cff0f..25157242 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -26,6 +26,7 @@ import com.dnd.gongmuin.member.dto.request.UpdateMemberProfileRequest; import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; import com.dnd.gongmuin.member.dto.response.AnsweredQuestionPostsByMemberResponse; +import com.dnd.gongmuin.member.dto.response.BookmarksByMemberResponse; import com.dnd.gongmuin.member.dto.response.LogoutResponse; import com.dnd.gongmuin.member.dto.response.MemberProfileResponse; import com.dnd.gongmuin.member.dto.response.QuestionPostsByMemberResponse; @@ -216,7 +217,19 @@ public PageResponse getAnsweredQuestionPo return PageMapper.toPageResponse(responsePage); } catch (Exception e) { - throw new NotFoundException(MemberErrorCode.ANSWERED_QUESTION_POSTS_BY_MEMBER_FAILED); + throw new NotFoundException(MemberErrorCode.QUESTION_POSTS_BY_MEMBER_FAILED); + } + } + + public PageResponse getBookmarksByMember( + Member member, Pageable pageable) { + try { + Slice responsePage = + memberRepository.getBookmarksByMember(member, pageable); + + return PageMapper.toPageResponse(responsePage); + } catch (Exception e) { + throw new NotFoundException(MemberErrorCode.QUESTION_POSTS_BY_MEMBER_FAILED); } } } \ No newline at end of file diff --git a/src/test/java/com/dnd/gongmuin/common/fixture/InteractionFixture.java b/src/test/java/com/dnd/gongmuin/common/fixture/InteractionFixture.java index cc606725..64cc8009 100644 --- a/src/test/java/com/dnd/gongmuin/common/fixture/InteractionFixture.java +++ b/src/test/java/com/dnd/gongmuin/common/fixture/InteractionFixture.java @@ -24,4 +24,17 @@ public static Interaction interaction( ReflectionTestUtils.setField(interaction, "id", 1L); return interaction; } + + public static Interaction interaction2( + InteractionType type, + Long memberId, + Long questionPostId + ) { + Interaction interaction = Interaction.of( + type, + memberId, + questionPostId + ); + return interaction; + } } diff --git a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java index 724c9aa3..ed32c50d 100644 --- a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java +++ b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java @@ -18,15 +18,18 @@ import com.dnd.gongmuin.answer.repository.AnswerRepository; import com.dnd.gongmuin.common.fixture.AnswerFixture; import com.dnd.gongmuin.common.fixture.InteractionCountFixture; +import com.dnd.gongmuin.common.fixture.InteractionFixture; import com.dnd.gongmuin.common.fixture.MemberFixture; import com.dnd.gongmuin.common.fixture.QuestionPostFixture; import com.dnd.gongmuin.common.support.ApiTestSupport; import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.request.UpdateMemberProfileRequest; import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.post_interaction.domain.Interaction; import com.dnd.gongmuin.post_interaction.domain.InteractionCount; import com.dnd.gongmuin.post_interaction.domain.InteractionType; import com.dnd.gongmuin.post_interaction.repository.InteractionCountRepository; +import com.dnd.gongmuin.post_interaction.repository.InteractionRepository; import com.dnd.gongmuin.question_post.domain.QuestionPost; import com.dnd.gongmuin.question_post.repository.QuestionPostRepository; @@ -45,6 +48,9 @@ class MemberControllerTest extends ApiTestSupport { @Autowired InteractionCountRepository interactionCountRepository; + @Autowired + InteractionRepository interactionRepository; + @AfterEach void tearDown() { answerRepository.deleteAll(); @@ -152,7 +158,7 @@ void getAnsweredQuestionPostsByMember() throws Exception { answerRepository.save(answer2); // when // then - mockMvc.perform(get("/api/members/answer-posts") + mockMvc.perform(get("/api/members/question-posts/answers") .header(AUTHORIZATION, accessToken) ) .andExpect(status().isOk()) @@ -163,4 +169,37 @@ void getAnsweredQuestionPostsByMember() throws Exception { .andExpect(jsonPath("$.content[1].questionPostId").value(questionPost2.getId())) .andExpect(jsonPath("$.content[1].answerId").value(answer1.getId())); } + + @DisplayName("로그인 된 회원의 스크랩 질문을 전체 조회한다.") + @Test + void getBookmarksByMember() throws Exception { + // given + Member member = MemberFixture.member2(); + Member savedMember = memberRepository.save(member); + + QuestionPost questionPost1 = QuestionPostFixture.questionPost(loginMember, "첫 번째 게시글입니다."); + QuestionPost questionPost2 = QuestionPostFixture.questionPost(savedMember, "두 번째 게시글입니다."); + QuestionPost questionPost3 = QuestionPostFixture.questionPost(loginMember, "세 번째 게시글입니다."); + questionPostRepository.saveAll(List.of(questionPost1, questionPost2, questionPost3)); + + Interaction interaction1 = InteractionFixture.interaction2(InteractionType.SAVED, loginMember.getId(), + questionPost1.getId()); + Interaction interaction3 = InteractionFixture.interaction2(InteractionType.RECOMMEND, loginMember.getId(), + questionPost2.getId()); + Interaction interaction2 = InteractionFixture.interaction2(InteractionType.SAVED, loginMember.getId(), + questionPost3.getId()); + Interaction interaction4 = InteractionFixture.interaction2(InteractionType.RECOMMEND, loginMember.getId(), + questionPost3.getId()); + interactionRepository.saveAll(List.of(interaction1, interaction2, interaction3, interaction4)); + + // when // then + mockMvc.perform(get("/api/members/question-posts/bookmarks") + .header(AUTHORIZATION, accessToken) + ) + .andExpect(status().isOk()) + .andDo(MockMvcResultHandlers.print()) + .andExpect(jsonPath("$.size").value(2)) + .andExpect(jsonPath("$.content[0].questionPostId").value(questionPost3.getId())) + .andExpect(jsonPath("$.content[1].questionPostId").value(questionPost1.getId())); + } } diff --git a/src/test/java/com/dnd/gongmuin/member/repository/MemberRepositoryTest.java b/src/test/java/com/dnd/gongmuin/member/repository/MemberRepositoryTest.java index 59b7344a..b954f2fa 100644 --- a/src/test/java/com/dnd/gongmuin/member/repository/MemberRepositoryTest.java +++ b/src/test/java/com/dnd/gongmuin/member/repository/MemberRepositoryTest.java @@ -19,15 +19,19 @@ import com.dnd.gongmuin.answer.repository.AnswerRepository; import com.dnd.gongmuin.common.fixture.AnswerFixture; import com.dnd.gongmuin.common.fixture.InteractionCountFixture; +import com.dnd.gongmuin.common.fixture.InteractionFixture; import com.dnd.gongmuin.common.fixture.MemberFixture; import com.dnd.gongmuin.common.fixture.QuestionPostFixture; import com.dnd.gongmuin.common.support.DataJpaTestSupport; import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.response.AnsweredQuestionPostsByMemberResponse; +import com.dnd.gongmuin.member.dto.response.BookmarksByMemberResponse; import com.dnd.gongmuin.member.dto.response.QuestionPostsByMemberResponse; +import com.dnd.gongmuin.post_interaction.domain.Interaction; import com.dnd.gongmuin.post_interaction.domain.InteractionCount; import com.dnd.gongmuin.post_interaction.domain.InteractionType; import com.dnd.gongmuin.post_interaction.repository.InteractionCountRepository; +import com.dnd.gongmuin.post_interaction.repository.InteractionRepository; import com.dnd.gongmuin.question_post.domain.QuestionPost; import com.dnd.gongmuin.question_post.repository.QuestionPostRepository; @@ -47,6 +51,9 @@ class MemberRepositoryTest extends DataJpaTestSupport { @Autowired InteractionCountRepository interactionCountRepository; + @Autowired + InteractionRepository interactionRepository; + @AfterEach void tearDown() { answerRepository.deleteAll(); @@ -91,8 +98,8 @@ void getQuestionPostsByMember() { () -> assertThat(postsByMember).hasSize(2), () -> assertThat(postsByMember).extracting(QuestionPostsByMemberResponse::title) .containsExactly( - "두 번째 게시글입니다.", - "첫 번째 게시글입니다." + questionPost2.getTitle(), + questionPost1.getTitle() ), () -> assertThat(postsByMember).extracting(QuestionPostsByMemberResponse::questionPostId) .containsExactly( @@ -139,8 +146,8 @@ void getQuestionPostsByMemberWithInteractionCount() { ), () -> assertThat(postsByMember).extracting(QuestionPostsByMemberResponse::title) .containsExactly( - "두 번째 게시글입니다.", - "첫 번째 게시글입니다." + questionPost2.getTitle(), + questionPost1.getTitle() ), () -> assertThat(postsByMember).extracting(QuestionPostsByMemberResponse::savedTotalCount) .containsExactly( @@ -183,8 +190,8 @@ void getAnsweredQuestionPostsByMember() { () -> assertThat(postsByMember).hasSize(2), () -> assertThat(postsByMember).extracting(AnsweredQuestionPostsByMemberResponse::title) .containsExactly( - "세 번째 게시글입니다.", - "첫 번째 게시글입니다." + questionPost3.getTitle(), + questionPost1.getTitle() ), () -> assertThat(postsByMember).extracting(AnsweredQuestionPostsByMemberResponse::questionPostId) .containsExactly( @@ -237,8 +244,8 @@ void getAnsweredQuestionPostsByMemberWithInteractionCount() { () -> assertThat(postsByMember).hasSize(2), () -> assertThat(postsByMember).extracting(AnsweredQuestionPostsByMemberResponse::title) .containsExactly( - "세 번째 게시글입니다.", - "첫 번째 게시글입니다." + questionPost3.getTitle(), + questionPost1.getTitle() ), () -> assertThat(postsByMember).extracting(AnsweredQuestionPostsByMemberResponse::questionPostId) .containsExactly( @@ -329,19 +336,6 @@ void whenAnsweredQuestionPosts_thenGetQuestionPostsAtRecently() { // then Assertions.assertAll( - () -> postsByMember.forEach(post -> { - System.out.println("QuestionPostId: " + post.questionPostId()); - System.out.println("Title: " + post.title()); - System.out.println("Content: " + post.content()); - System.out.println("JobGroup: " + post.jobGroup()); - System.out.println("Reward: " + post.reward()); - System.out.println("UpdatedAt: " + post.questionPostUpdatedAt()); - System.out.println("IsChosen: " + post.isChosen()); - System.out.println("answerId: " + post.answerId()); - System.out.println("post.answerContent() = " + post.answerContent()); - System.out.println("post.answerUpdatedAt() = " + post.answerUpdatedAt()); - System.out.println("----------"); - }), () -> assertThat(postsByMember).hasSize(1), () -> assertThat(postsByMember).extracting(AnsweredQuestionPostsByMemberResponse::answerId) .containsExactly( @@ -358,6 +352,49 @@ void whenAnsweredQuestionPosts_thenGetQuestionPostsAtRecently() { ); } + @DisplayName("회원의 스크랩 질문 목록을 조회힌다.") + @Test + void getBookmarksByMember() { + // given + Member member1 = MemberFixture.member(); + Member member2 = MemberFixture.member2(); + memberRepository.saveAll(List.of(member1, member2)); + + QuestionPost questionPost1 = QuestionPostFixture.questionPost(member1, "첫 번째 게시글입니다."); + QuestionPost questionPost2 = QuestionPostFixture.questionPost(member2, "두 번째 게시글입니다."); + QuestionPost questionPost3 = QuestionPostFixture.questionPost(member2, "세 번째 게시글입니다."); + questionPostRepository.saveAll(List.of(questionPost1, questionPost2, questionPost3)); + + Interaction interaction1 = InteractionFixture.interaction2(InteractionType.SAVED, member1.getId(), + questionPost1.getId()); + Interaction interaction2 = InteractionFixture.interaction2(InteractionType.SAVED, member1.getId(), + questionPost2.getId()); + Interaction interaction3 = InteractionFixture.interaction2(InteractionType.RECOMMEND, member1.getId(), + questionPost2.getId()); + Interaction interaction4 = InteractionFixture.interaction2(InteractionType.RECOMMEND, member1.getId(), + questionPost3.getId()); + interactionRepository.saveAll(List.of(interaction1, interaction2, interaction3, interaction4)); + + // when + Slice bookmarksByMember = memberRepository.getBookmarksByMember(member1, + pageRequest); + + // then + Assertions.assertAll( + () -> assertThat(bookmarksByMember).hasSize(2), + () -> assertThat(bookmarksByMember).extracting(BookmarksByMemberResponse::questionPostId) + .containsExactly( + questionPost2.getId(), + questionPost1.getId() + ), + () -> assertThat(bookmarksByMember).extracting(BookmarksByMemberResponse::title) + .containsExactly( + questionPost2.getTitle(), + questionPost1.getTitle() + ) + ); + } + private Member createMember(String nickname, String socialName, String socialEmail, String officialEmail) { return Member.builder() .nickname(nickname)