diff --git a/clokey-api/src/main/java/org/clokey/domain/like/controller/LikeController.java b/clokey-api/src/main/java/org/clokey/domain/like/controller/LikeController.java index f69ae9c6..0e003572 100644 --- a/clokey-api/src/main/java/org/clokey/domain/like/controller/LikeController.java +++ b/clokey-api/src/main/java/org/clokey/domain/like/controller/LikeController.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.clokey.code.GlobalBaseSuccessCode; import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; import org.clokey.domain.like.service.LikeService; import org.clokey.global.annotation.PageSize; import org.clokey.response.BaseResponse; @@ -26,8 +27,27 @@ public class LikeController { private final LikeService likeService; + @GetMapping("/users") + @Operation( + operationId = "Like_getLikedMembers", + summary = "좋아요한 유저 조회", + description = "내 기록을 좋아요한 유저를 조회합니다") + public BaseResponse> getLikedMembers( + @Parameter(description = "기록 ID") @RequestParam Long historyId, + @Parameter(description = "이전 페이지의 좋아요 ID (첫 요청 시 생략)") @RequestParam(required = false) + Long lastLikeId, + @Parameter(description = "페이지당 조회할 개수") @RequestParam @PageSize Integer size) { + SliceResponse response = + likeService.getLikedMembers(historyId, lastLikeId, size); + + return BaseResponse.onSuccess(GlobalBaseSuccessCode.OK, response); + } + @GetMapping("/histories") - @Operation(summary = "좋아요한 기록 조회", description = "사용자가 좋아요한 기록을 조회합니다.") + @Operation( + operationId = "Like_getLikedHistories", + summary = "좋아요한 기록 조회", + description = "사용자가 좋아요한 기록을 조회합니다.") public BaseResponse> getLikedHistories( @Parameter(description = "이전 페이지의 좋아요 ID (첫 요청 시 생략)") diff --git a/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedHistoriesResponse.java b/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedHistoriesResponse.java index b567d8b0..224cfc26 100644 --- a/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedHistoriesResponse.java +++ b/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedHistoriesResponse.java @@ -9,11 +9,40 @@ public record LikedHistoriesResponse( @Schema(description = "마지막 페이지 여부", example = "false") boolean isLast) { @Schema(description = "히스토리 미리보기 DTO") - public record LikedHistoryPreview( - @Schema(description = "히스토리 ID", example = "30") Long id, - @Schema( - description = "히스토리 대표 이미지 URL", - example = - "https://clokeybucket.s3.ap-northeast-2.amazonaws.com/example.jpg") - String imageUrl) {} + public static class LikedHistoryPreview { + @Schema(description = "히스토리 ID", example = "30") + private final Long id; + + @Schema( + description = "히스토리 대표 이미지 URL", + example = "https://clokeybucket.s3.ap-northeast-2.amazonaws.com/example.jpg") + private String imageUrl; + + @Schema(description = "다음 페이지 조회를 위한 커서 ID (MemberLike ID)", example = "100") + private final Long lastLikeId; + + public LikedHistoryPreview(Long id, Long lastLikeId) { + this.id = id; + this.lastLikeId = lastLikeId; + this.imageUrl = null; + } + + public LikedHistoryPreview(Long id, String imageUrl) { + this.id = id; + this.imageUrl = imageUrl; + this.lastLikeId = null; + } + + public Long getId() { + return id; + } + + public String getImageUrl() { + return imageUrl; + } + + public Long getLastLikeId() { + return lastLikeId; + } + } } diff --git a/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedMembersResponse.java b/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedMembersResponse.java new file mode 100644 index 00000000..bfa8cd46 --- /dev/null +++ b/clokey-api/src/main/java/org/clokey/domain/like/dto/response/LikedMembersResponse.java @@ -0,0 +1,53 @@ +package org.clokey.domain.like.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Getter; + +@Schema(description = "좋아요 유저 조회 결과") +public record LikedMembersResponse( + @Schema(description = "유저 미리보기 목록") List memberPreviews, + @Schema(description = "마지막 페이지 여부", example = "false") boolean isLast) { + + @Schema(description = "유저 미리보기 DTO") + @Getter + public static class LikedMemberPreview { + @Schema(description = "유저 ID", example = "30") + private final Long id; + + @Schema(description = "클로키 ID", example = "@Clokey_USER1") + private final String codiveId; + + @Schema(description = "프로필 이미지 URL") + private final String imageUrl; + + @Schema(description = "닉네임") + private final String nickname; + + @Schema(description = "팔로우 여부") + private final boolean followStatus; + + @Schema(description = "다음 페이지 조회를 위한 커서 ID (MemberLike ID)", example = "100") + private final Long lastLikeId; + + public LikedMemberPreview( + Long id, String codiveId, String imageUrl, String nickname, Long lastLikeId) { + this.id = id; + this.codiveId = codiveId; + this.imageUrl = imageUrl; + this.nickname = nickname; + this.lastLikeId = lastLikeId; + this.followStatus = false; + } + + public LikedMemberPreview( + Long id, String codiveId, String imageUrl, String nickname, boolean followStatus) { + this.id = id; + this.codiveId = codiveId; + this.imageUrl = imageUrl; + this.nickname = nickname; + this.followStatus = followStatus; + this.lastLikeId = null; + } + } +} diff --git a/clokey-api/src/main/java/org/clokey/domain/like/repository/MemberLikeRepositoryCustom.java b/clokey-api/src/main/java/org/clokey/domain/like/repository/MemberLikeRepositoryCustom.java new file mode 100644 index 00000000..c28a5fdb --- /dev/null +++ b/clokey-api/src/main/java/org/clokey/domain/like/repository/MemberLikeRepositoryCustom.java @@ -0,0 +1,14 @@ +package org.clokey.domain.like.repository; + +import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; +import org.springframework.data.domain.Slice; + +public interface MemberLikeRepositoryCustom { + + Slice findLikedHistoriesSliceByMemberId( + Long memberId, Long lastLikeId, Integer size); + + Slice findLikedMembersSliceByHistoryId( + Long historyId, Long lastLikeId, Integer size); +} diff --git a/clokey-api/src/main/java/org/clokey/domain/like/repository/MemberLikeRepositoryImpl.java b/clokey-api/src/main/java/org/clokey/domain/like/repository/MemberLikeRepositoryImpl.java new file mode 100644 index 00000000..8ddebd46 --- /dev/null +++ b/clokey-api/src/main/java/org/clokey/domain/like/repository/MemberLikeRepositoryImpl.java @@ -0,0 +1,92 @@ +package org.clokey.domain.like.repository; + +import static org.clokey.like.entity.QMemberLike.memberLike; +import static org.clokey.member.entity.QMember.member; + +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; +import org.clokey.global.paging.SortDirection; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class MemberLikeRepositoryImpl implements MemberLikeRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + private final SortDirection DEFAULT_SORT = SortDirection.DESC; + + @Override + public Slice findLikedHistoriesSliceByMemberId( + Long memberId, Long lastLikeId, Integer size) { + + List results = + queryFactory + .select( + Projections.constructor( + LikedHistoriesResponse.LikedHistoryPreview.class, + memberLike.history.id, + memberLike.id)) + .from(memberLike) + .where( + memberLike.member.id.eq(memberId), + lastLikeIdCondition(lastLikeId, DEFAULT_SORT)) + .limit(size + 1) + .orderBy(memberLike.id.desc()) + .fetch(); + + return checkLastPage(size, results); + } + + @Override + public Slice findLikedMembersSliceByHistoryId( + Long historyId, Long lastLikeId, Integer size) { + + List results = + queryFactory + .select( + Projections.constructor( + LikedMembersResponse.LikedMemberPreview.class, + member.id, + member.clokeyId, + member.profileImageUrl, + member.nickname, + memberLike.id)) + .from(memberLike) + .join(memberLike.member, member) + .where( + memberLike.history.id.eq(historyId), + lastLikeIdCondition(lastLikeId, DEFAULT_SORT)) + .limit(size + 1) + .orderBy(memberLike.id.desc()) + .fetch(); + + return checkLastPage(size, results); + } + + private BooleanExpression lastLikeIdCondition(Long likeId, SortDirection direction) { + if (likeId == null) { + return null; + } + return memberLike.id.lt(likeId); + } + + private Slice checkLastPage(int pageSize, List results) { + boolean hasNext = false; + + if (results.size() > pageSize) { + hasNext = true; + results.remove(pageSize); + } + + return new SliceImpl<>(results, PageRequest.of(0, pageSize), hasNext); + } +} diff --git a/clokey-api/src/main/java/org/clokey/domain/like/service/LikeService.java b/clokey-api/src/main/java/org/clokey/domain/like/service/LikeService.java index 2e2ebea4..f0133d6c 100644 --- a/clokey-api/src/main/java/org/clokey/domain/like/service/LikeService.java +++ b/clokey-api/src/main/java/org/clokey/domain/like/service/LikeService.java @@ -1,9 +1,13 @@ package org.clokey.domain.like.service; import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; import org.clokey.response.SliceResponse; public interface LikeService { + SliceResponse getLikedMembers( + Long historyId, Long lastLikedId, Integer size); + SliceResponse getLikedHistories( Long lastLikedId, Integer size); diff --git a/clokey-api/src/main/java/org/clokey/domain/like/service/LikeServiceImpl.java b/clokey-api/src/main/java/org/clokey/domain/like/service/LikeServiceImpl.java index d74868ad..5dc160aa 100644 --- a/clokey-api/src/main/java/org/clokey/domain/like/service/LikeServiceImpl.java +++ b/clokey-api/src/main/java/org/clokey/domain/like/service/LikeServiceImpl.java @@ -1,24 +1,28 @@ package org.clokey.domain.like.service; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.clokey.domain.history.exception.HistoryErrorCode; import org.clokey.domain.history.repository.HistoryImageRepository; import org.clokey.domain.history.repository.HistoryRepository; import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; import org.clokey.domain.like.repository.MemberLikeRepository; +import org.clokey.domain.like.repository.MemberLikeRepositoryCustom; import org.clokey.domain.member.repository.BlockRepository; +import org.clokey.domain.member.repository.FollowRepository; import org.clokey.exception.BaseCustomException; import org.clokey.global.util.MemberUtil; import org.clokey.history.entity.History; import org.clokey.like.entity.MemberLike; import org.clokey.member.entity.Member; import org.clokey.response.SliceResponse; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,6 +36,8 @@ public class LikeServiceImpl implements LikeService { private final HistoryImageRepository historyImageRepository; private final HistoryRepository historyRepository; private final BlockRepository blockRepository; + private final FollowRepository followRepository; + private final MemberLikeRepositoryCustom memberLikeRepositoryCustom; @Override public SliceResponse getLikedHistories( @@ -39,37 +45,30 @@ public SliceResponse getLikedHistori Member currentMember = memberUtil.getCurrentMember(); - // limit + 1 조회 - Pageable pageable = PageRequest.of(0, size + 1); + Slice likedHistoriesSlice = + memberLikeRepositoryCustom.findLikedHistoriesSliceByMemberId( + currentMember.getId(), lastLikeId, size); - List likes = - memberLikeRepository.findLikedHistoriesByMemberId( - currentMember.getId(), lastLikeId, pageable); - - boolean isLast = likes.size() <= size; - - if (!isLast) { - likes = likes.subList(0, size); - } - - if (likes.isEmpty()) { + if (likedHistoriesSlice.isEmpty()) { return new SliceResponse<>(List.of(), true); } - List historyIds = likes.stream().map(like -> like.getHistory().getId()).toList(); + List historyIds = + likedHistoriesSlice.getContent().stream() + .map(LikedHistoriesResponse.LikedHistoryPreview::getId) + .toList(); Map imageMap = findFirstImagesByHistoryIds(historyIds); List previews = - likes.stream() + likedHistoriesSlice.getContent().stream() .map( - like -> + preview -> new LikedHistoriesResponse.LikedHistoryPreview( - like.getHistory().getId(), - imageMap.get(like.getHistory().getId()))) + preview.getId(), imageMap.get(preview.getId()))) .toList(); - return new SliceResponse<>(previews, isLast); + return new SliceResponse<>(previews, likedHistoriesSlice.isLast()); } private Map findFirstImagesByHistoryIds(List historyIds) { @@ -115,4 +114,42 @@ private boolean isBlockedByOrBlocking(Long fromId, Long toId) { fromId, toId, toId, fromId); } + + @Override + public SliceResponse getLikedMembers( + Long historyId, Long lastLikeId, Integer size) { + + Member currentMember = memberUtil.getCurrentMember(); + + Slice likedMembersSlice = + memberLikeRepositoryCustom.findLikedMembersSliceByHistoryId( + historyId, lastLikeId, size); + + if (likedMembersSlice.isEmpty()) { + return new SliceResponse<>(List.of(), true); + } + + List memberIds = + likedMembersSlice.getContent().stream() + .map(LikedMembersResponse.LikedMemberPreview::getId) + .toList(); + + Set followedIdSet = + new HashSet<>( + followRepository.findFollowedMemberIds(currentMember.getId(), memberIds)); + + List previews = + likedMembersSlice.getContent().stream() + .map( + preview -> + new LikedMembersResponse.LikedMemberPreview( + preview.getId(), + preview.getCodiveId(), + preview.getImageUrl(), + preview.getNickname(), + followedIdSet.contains(preview.getId()))) + .toList(); + + return new SliceResponse<>(previews, likedMembersSlice.isLast()); + } } diff --git a/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepository.java b/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepository.java index 5ed45ba8..5f7858fc 100644 --- a/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepository.java +++ b/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepository.java @@ -1,12 +1,23 @@ package org.clokey.domain.member.repository; +import java.util.List; import java.util.Optional; import org.clokey.member.entity.Follow; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface FollowRepository extends JpaRepository, FollowRepositoryCustom { boolean existsByFollowFrom_IdAndFollowTo_Id(Long fromMemberId, Long toMemberId); + @Query( + """ + SELECT f.followTo.id + FROM Follow f + WHERE f.followFrom.id = :fromMemberId + AND f.followTo.id IN :toMemberIds +""") + List findFollowedMemberIds(Long fromMemberId, List toMemberIds); + Optional findByFollowFrom_IdAndFollowTo_Id(Long fromMemberId, Long toMemberId); } diff --git a/clokey-api/src/test/java/org/clokey/domain/like/controller/LikeControllerTest.java b/clokey-api/src/test/java/org/clokey/domain/like/controller/LikeControllerTest.java index 82ebe94e..fb7500d0 100644 --- a/clokey-api/src/test/java/org/clokey/domain/like/controller/LikeControllerTest.java +++ b/clokey-api/src/test/java/org/clokey/domain/like/controller/LikeControllerTest.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; import org.clokey.domain.like.service.LikeService; import org.clokey.response.SliceResponse; import org.junit.jupiter.api.Nested; @@ -130,4 +131,94 @@ class 좋아요한_기록_조회_시 { .andExpect(jsonPath("$.result.isLast").value(false)); } } + + @Nested + class 좋아요한_유저_조회_시 { + @Test + void 유효한_요청이면_좋아요한_유저를_반환한다() throws Exception { + // given + List previews = + List.of( + new LikedMembersResponse.LikedMemberPreview( + 1L, "codive1", "https://img.com/img1.jpg", "nickname1", true), + new LikedMembersResponse.LikedMemberPreview( + 2L, "codive2", "https://img.com/img2.jpg", "nickname2", false)); + + SliceResponse sliceResponse = + new SliceResponse<>(previews, true); + + given(likeService.getLikedMembers(any(), any(), anyInt())).willReturn(sliceResponse); + + ResultActions perform = + mockMvc.perform( + get("/likes/users") + .param("historyId", "1") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON)); + + // then + perform.andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value("COMMON200")) + .andExpect(jsonPath("$.message").value("성공입니다.")) + .andExpect(jsonPath("$.result.content[0].id").value(1L)) + .andExpect(jsonPath("$.result.content[0].codiveId").value("codive1")) + .andExpect( + jsonPath("$.result.content[0].imageUrl") + .value("https://img.com/img1.jpg")) + .andExpect(jsonPath("$.result.content[0].nickname").value("nickname1")) + .andExpect(jsonPath("$.result.content[0].followStatus").value(true)) + .andExpect(jsonPath("$.result.content[1].id").value(2L)) + .andExpect(jsonPath("$.result.content[1].codiveId").value("codive2")) + .andExpect( + jsonPath("$.result.content[1].imageUrl") + .value("https://img.com/img2.jpg")) + .andExpect(jsonPath("$.result.content[1].nickname").value("nickname2")) + .andExpect(jsonPath("$.result.content[1].followStatus").value(false)) + .andExpect(jsonPath("$.result.isLast").value(true)); + } + + @Test + void 마지막_페이지가_아닌_경우_isLast를_false로_응답한다() throws Exception { + // given + List previews = + List.of( + new LikedMembersResponse.LikedMemberPreview( + 1L, "codive1", "https://img.com/img1.jpg", "nickname1", true), + new LikedMembersResponse.LikedMemberPreview( + 2L, "codive2", "https://img.com/img2.jpg", "nickname2", false)); + + SliceResponse sliceResponse = + new SliceResponse<>(previews, false); + + given(likeService.getLikedMembers(any(), any(), anyInt())).willReturn(sliceResponse); + + // when + ResultActions perform = + mockMvc.perform( + get("/likes/users") + .param("historyId", "1") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON)); + + // then + perform.andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value("COMMON200")) + .andExpect(jsonPath("$.message").value("성공입니다.")) + .andExpect(jsonPath("$.result.content[0].id").value(1L)) + .andExpect(jsonPath("$.result.content[0].codiveId").value("codive1")) + .andExpect( + jsonPath("$.result.content[0].imageUrl") + .value("https://img.com/img1.jpg")) + .andExpect(jsonPath("$.result.content[0].nickname").value("nickname1")) + .andExpect(jsonPath("$.result.content[0].followStatus").value(true)) + .andExpect(jsonPath("$.result.content[1].id").value(2L)) + .andExpect(jsonPath("$.result.content[1].codiveId").value("codive2")) + .andExpect( + jsonPath("$.result.content[1].imageUrl") + .value("https://img.com/img2.jpg")) + .andExpect(jsonPath("$.result.content[1].nickname").value("nickname2")) + .andExpect(jsonPath("$.result.content[1].followStatus").value(false)) + .andExpect(jsonPath("$.result.isLast").value(false)); + } + } } diff --git a/clokey-api/src/test/java/org/clokey/domain/like/service/LikeServiceTest.java b/clokey-api/src/test/java/org/clokey/domain/like/service/LikeServiceTest.java index 4cad615f..e83ddd56 100644 --- a/clokey-api/src/test/java/org/clokey/domain/like/service/LikeServiceTest.java +++ b/clokey-api/src/test/java/org/clokey/domain/like/service/LikeServiceTest.java @@ -11,6 +11,7 @@ import org.clokey.domain.history.repository.HistoryRepository; import org.clokey.domain.history.repository.SituationRepository; import org.clokey.domain.like.dto.response.LikedHistoriesResponse; +import org.clokey.domain.like.dto.response.LikedMembersResponse; import org.clokey.domain.like.repository.MemberLikeRepository; import org.clokey.domain.member.repository.BlockRepository; import org.clokey.domain.member.repository.FollowRepository; @@ -21,6 +22,7 @@ import org.clokey.history.entity.Situation; import org.clokey.like.entity.MemberLike; import org.clokey.member.entity.Block; +import org.clokey.member.entity.Follow; import org.clokey.member.entity.Member; import org.clokey.member.entity.OauthInfo; import org.clokey.member.enums.OauthProvider; @@ -190,6 +192,89 @@ void setUp() { // when SliceResponse response = likeService.getLikedHistories(null, 10); + // then + assertThat(response.content()).isEmpty(); + assertThat(response.isLast()).isTrue(); + } + } + + @Nested + class 좋아요한_유저를_조회할_때 { + + @BeforeEach + void setUp() { + Member member1 = + Member.createMember( + "testEmail1", + "testClokeyId1", + "testNickName1", + OauthInfo.createOauthInfo("testOauthId1", OauthProvider.KAKAO)); + + Member member2 = + Member.createMember( + "testEmail2", + "testClokeyId2", + "testNickName2", + OauthInfo.createOauthInfo("testOauthId2", OauthProvider.KAKAO)); + + Member member3 = + Member.createMember( + "testEmail3", + "testClokeyId3", + "testNickName3", + OauthInfo.createOauthInfo("testOauthId3", OauthProvider.KAKAO)); + memberRepository.saveAll(List.of(member1, member2, member3)); + given(memberUtil.getCurrentMember()).willReturn(member1); + + Situation situation1 = Situation.createSituation("testSituation1"); + situationRepository.save(situation1); + + History history1 = + History.createHistory( + LocalDate.of(2024, 12, 25), + "content1", + memberRepository.findById(1L).orElseThrow(), + situationRepository.findById(1L).orElseThrow()); + historyRepository.save(history1); + } + + @Test + void 좋아요한_유저가_있으면_유저를_반환한다() { + // given + memberLikeRepository.saveAll( + List.of( + MemberLike.createMemberLike( + memberRepository.findById(2L).orElseThrow(), + historyRepository.findById(1L).orElseThrow()), + MemberLike.createMemberLike( + memberRepository.findById(3L).orElseThrow(), + historyRepository.findById(1L).orElseThrow()))); + + followRepository.save( + Follow.createFollow( + memberRepository.findById(1L).orElseThrow(), + memberRepository.findById(2L).orElseThrow())); + + // when + SliceResponse response = + likeService.getLikedMembers(1L, null, 10); + + // then + assertThat(response.content()).hasSize(2); + assertThat(response.isLast()).isTrue(); + + assertThat(response.content()) + .extracting("id", "codiveId", "imageUrl", "nickname", "followStatus") + .containsExactly( + tuple(3L, "testClokeyId3", null, "testNickName3", false), + tuple(2L, "testClokeyId2", null, "testNickName2", true)); + } + + @Test + void 좋아요한_기록이_없으면_빈_리스트를_반환한다() { + // when + SliceResponse response = + likeService.getLikedMembers(1L, null, 10); // then assertThat(response.content()).isEmpty();