diff --git a/src/main/java/com/dokdok/meeting/api/MeetingListApi.java b/src/main/java/com/dokdok/meeting/api/MeetingListApi.java index 6480e27..636ad7c 100644 --- a/src/main/java/com/dokdok/meeting/api/MeetingListApi.java +++ b/src/main/java/com/dokdok/meeting/api/MeetingListApi.java @@ -1,14 +1,10 @@ package com.dokdok.meeting.api; import com.dokdok.global.response.ApiResponse; -import com.dokdok.global.response.CursorResponse; import com.dokdok.global.response.PageResponse; import com.dokdok.meeting.dto.MeetingListFilter; -import com.dokdok.meeting.dto.MeetingListItemCursorResponse; import com.dokdok.meeting.dto.MeetingListItemPageResponse; import com.dokdok.meeting.dto.MeetingListItemResponse; -import com.dokdok.meeting.dto.MeetingListCursor; -import com.dokdok.meeting.dto.MeetingListCursorRequest; import com.dokdok.meeting.entity.MeetingStatus; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -51,7 +47,7 @@ public interface MeetingListApi { responseCode = "200", description = "약속 리스트 조회 성공", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, - schema = @Schema(implementation = MeetingListItemCursorResponse.class), + schema = @Schema(implementation = MeetingListItemPageResponse.class), examples = @ExampleObject(value = """ { "code": "SUCCESS", @@ -69,12 +65,9 @@ public interface MeetingListApi { } ], "totalCount": 12, - "pageSize": 4, - "hasNext": true, - "nextCursor": { - "meetingId": 2, - "startDateTime": "2025-02-03T14:00:00" - } + "currentPage": 0, + "pageSize": 5, + "totalPages": 3 } } """)) @@ -100,11 +93,11 @@ public interface MeetingListApi { {"code": "E000", "message": "서버 에러가 발생했습니다. 담당자에게 문의 바랍니다.", "data": null} """))) }) - ResponseEntity>> getMeetingList( + ResponseEntity>> getMeetingList( @PathVariable Long gatheringId, @RequestParam MeetingListFilter filter, - @ParameterObject MeetingListCursorRequest cursor, - @RequestParam(defaultValue = "4") int size + @ParameterObject + @PageableDefault(size = 5, sort = {"meetingStartDate", "id"}) Pageable pageable ); @Operation( diff --git a/src/main/java/com/dokdok/meeting/controller/MeetingListController.java b/src/main/java/com/dokdok/meeting/controller/MeetingListController.java index 6be331a..2c7779f 100644 --- a/src/main/java/com/dokdok/meeting/controller/MeetingListController.java +++ b/src/main/java/com/dokdok/meeting/controller/MeetingListController.java @@ -1,11 +1,8 @@ package com.dokdok.meeting.controller; import com.dokdok.global.response.ApiResponse; -import com.dokdok.global.response.CursorResponse; import com.dokdok.global.response.PageResponse; import com.dokdok.meeting.api.MeetingListApi; -import com.dokdok.meeting.dto.MeetingListCursor; -import com.dokdok.meeting.dto.MeetingListCursorRequest; import com.dokdok.meeting.dto.MeetingListFilter; import com.dokdok.meeting.dto.MeetingListItemResponse; import com.dokdok.meeting.entity.MeetingStatus; @@ -31,15 +28,14 @@ public class MeetingListController implements MeetingListApi { @Override @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity>> getMeetingList( + public ResponseEntity>> getMeetingList( @PathVariable Long gatheringId, @RequestParam MeetingListFilter filter, - @ParameterObject MeetingListCursorRequest cursor, - @RequestParam(defaultValue = "4") int size + @ParameterObject + @PageableDefault(size = 5, sort = {"meetingStartDate", "id"}) Pageable pageable ) { - MeetingListCursor cursorValue = cursor == null ? null : cursor.toCursorOrNull(); - CursorResponse response = - meetingService.meetingList(gatheringId, filter, size, cursorValue); + PageResponse response = + meetingService.meetingList(gatheringId, filter, pageable); return ApiResponse.success(response, "약속 리스트 조회에 성공했습니다."); } diff --git a/src/main/java/com/dokdok/meeting/service/MeetingService.java b/src/main/java/com/dokdok/meeting/service/MeetingService.java index 867f787..161a779 100644 --- a/src/main/java/com/dokdok/meeting/service/MeetingService.java +++ b/src/main/java/com/dokdok/meeting/service/MeetingService.java @@ -483,24 +483,22 @@ private void validateMeetingDates(MeetingUpdateRequest request, Meeting meeting) * 내가 참여한 약속 : 완전히 끝난 약속 중 내가 참여한 약속 * @param gatheringId 모임 식별자 * @param filter 약속 리스트 필터 - * @param size 페이지 크기 - * @param cursor 커서 - * @return CursorResponse + * @param pageable 페이지 정보 + * @return PageResponse */ - public CursorResponse meetingList( + public PageResponse meetingList( Long gatheringId, MeetingListFilter filter, - int size, - MeetingListCursor cursor + Pageable pageable ) { Long userId = SecurityUtil.getCurrentUserId(); gatheringValidator.validateMembership(gatheringId, userId); return switch (filter) { - case ALL -> getAllMeetings(gatheringId, size, cursor, userId); - case UPCOMING -> getUpcomingMeetings(gatheringId, size, cursor, userId); - case DONE -> getDoneMeetings(gatheringId, size, cursor, userId); - case JOINED -> getJoinedMeetings(gatheringId, size, cursor, userId); + case ALL -> getAllMeetingsPage(gatheringId, pageable, userId); + case UPCOMING -> getUpcomingMeetingsPage(gatheringId, pageable, userId); + case DONE -> getDoneMeetingsPage(gatheringId, pageable, userId); + case JOINED -> getJoinedMeetingsPage(gatheringId, pageable, userId); }; } @@ -674,138 +672,88 @@ public MyMeetingTabCountsResponse getMyMeetingTabCounts() { } /** - * 모임의 약속 중 확정된 리스트를 전부 반환한다. + * 모임의 약속 중 확정된 리스트를 페이지로 반환한다. */ - private CursorResponse getAllMeetings( + private PageResponse getAllMeetingsPage( Long gatheringId, - int size, - MeetingListCursor cursor, + Pageable pageable, Long userId ) { - Pageable pageable = cursorPageable(size); - List meetings = meetingRepository.findByGatheringIdAndMeetingStatusAfterCursor( + Page meetingPage = meetingRepository.findByGatheringIdAndMeetingStatus( gatheringId, MeetingStatus.CONFIRMED, - cursorStartDateTime(cursor), - cursorMeetingId(cursor), pageable ); - Integer totalCount = cursor == null - ? meetingRepository.countByGatheringIdAndMeetingStatus(gatheringId, MeetingStatus.CONFIRMED) - : null; - return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount); + return buildMeetingPageResponse(meetingPage, userId, gatheringId); } /** - * 다가오는 약속 리스트를 반환한다. + * 다가오는 약속 리스트를 페이지로 반환한다. */ - private CursorResponse getUpcomingMeetings( + private PageResponse getUpcomingMeetingsPage( Long gatheringId, - int size, - MeetingListCursor cursor, + Pageable pageable, Long userId ) { LocalDateTime now = LocalDateTime.now(); - Pageable pageable = cursorPageable(size); - - List meetings = meetingRepository - .findByGatheringIdAndMeetingStatusAndMeetingStartDateBetweenAfterCursor( - gatheringId, - MeetingStatus.CONFIRMED, - now, - now.plusDays(3), - cursorStartDateTime(cursor), - cursorMeetingId(cursor), - pageable - ); - - Integer totalCount = cursor == null - ? meetingRepository.countUpcomingMeetings( - gatheringId, - MeetingStatus.CONFIRMED, - now, - now.plusDays(3) - ) - : null; - return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount); + Page meetingPage = meetingRepository.findByGatheringIdAndMeetingStatusAndMeetingStartDateBetween( + gatheringId, + MeetingStatus.CONFIRMED, + now, + now.plusDays(3), + pageable + ); + return buildMeetingPageResponse(meetingPage, userId, gatheringId); } /** - * 완료된 약속 리스트를 반환한다. + * 완료된 약속 리스트를 페이지로 반환한다. */ - private CursorResponse getDoneMeetings( + private PageResponse getDoneMeetingsPage( Long gatheringId, - int size, - MeetingListCursor cursor, + Pageable pageable, Long userId ) { - Pageable pageable = cursorPageable(size); - List meetings = meetingRepository.findByGatheringIdAndMeetingStatusAfterCursor( + Page meetingPage = meetingRepository.findByGatheringIdAndMeetingStatus( gatheringId, MeetingStatus.DONE, - cursorStartDateTime(cursor), - cursorMeetingId(cursor), pageable ); - Integer totalCount = cursor == null - ? meetingRepository.countByGatheringIdAndMeetingStatus(gatheringId, MeetingStatus.DONE) - : null; - return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount); + return buildMeetingPageResponse(meetingPage, userId, gatheringId); } /** - * 완료된 약속 중 내가 참여했던 약속 리스트를 반환한다. + * 완료된 약속 중 내가 참여했던 약속 리스트를 페이지로 반환한다. */ - private CursorResponse getJoinedMeetings( + private PageResponse getJoinedMeetingsPage( Long gatheringId, - int size, - MeetingListCursor cursor, + Pageable pageable, Long userId ) { - Pageable pageable = cursorPageable(size); - List meetings = meetingMemberRepository.findMeetingsByUserIdAndStatusAfterCursor( + Page meetingPage = meetingMemberRepository.findMeetingsByUserIdAndStatus( userId, gatheringId, MeetingStatus.DONE, - cursorStartDateTime(cursor), - cursorMeetingId(cursor), pageable ); - Integer totalCount = cursor == null - ? meetingMemberRepository.countMeetingsByUserIdAndStatus( - userId, - gatheringId, - MeetingStatus.DONE - ) - : null; - return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount); + return buildMeetingPageResponse(meetingPage, userId, gatheringId); } /** - * 약속 리스트 커서 응답을 구성한다. + * 약속 리스트 페이지 응답을 구성한다. */ - private CursorResponse buildMeetingListResponse( - List meetingCandidates, - int size, + private PageResponse buildMeetingPageResponse( + Page meetingPage, Long userId, - Long gatheringId, - Integer totalCount + Long gatheringId ) { - boolean hasNext = meetingCandidates.size() > size; - List meetings = hasNext ? meetingCandidates.subList(0, size) : meetingCandidates; - if (meetings.isEmpty()) { - return CursorResponse.of(List.of(), size, false, null, totalCount); - } - - List items = buildMeetingItems(meetings, userId, gatheringId); - - MeetingListCursor nextCursor = null; - if (hasNext) { - Meeting last = meetings.get(meetings.size() - 1); - nextCursor = new MeetingListCursor(last.getMeetingStartDate(), last.getId()); - } - - return CursorResponse.of(items, size, hasNext, nextCursor, totalCount); + List items = buildMeetingItems(meetingPage.getContent(), userId, gatheringId); + return PageResponse.of( + items, + meetingPage.getTotalElements(), + meetingPage.getNumber(), + meetingPage.getSize() + ); } /** diff --git a/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java b/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java index d3ef4bb..44612c7 100644 --- a/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java +++ b/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java @@ -39,7 +39,6 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Page; @@ -1083,7 +1082,7 @@ void givenGatheringIdAndAllFilter_whenGetMeetingList_thenReturnItems() { // given Long gatheringId = 100L; Long userId = 55L; - int size = 10; + Pageable pageable = org.springframework.data.domain.PageRequest.of(0, 10); Book book1 = Book.builder().id(1L).bookName("book1").build(); Book book2 = Book.builder().id(2L).bookName("book2").build(); Meeting meeting1 = Meeting.builder() @@ -1106,13 +1105,12 @@ void givenGatheringIdAndAllFilter_whenGetMeetingList_thenReturnItems() { .build(); List meetings = List.of(meeting1, meeting2); - given(meetingRepository.findByGatheringIdAndMeetingStatusAfterCursor( + Page meetingPage = new PageImpl<>(meetings, pageable, meetings.size()); + given(meetingRepository.findByGatheringIdAndMeetingStatus( eq(gatheringId), eq(MeetingStatus.CONFIRMED), - any(), - any(), any() - )).willReturn(meetings); + )).willReturn(meetingPage); given(topicRepository.findTopicTypesByMeetingIds(List.of(1L, 2L))) .willReturn(List.of( new Object[]{1L, TopicType.FREE}, @@ -1126,14 +1124,14 @@ void givenGatheringIdAndAllFilter_whenGetMeetingList_thenReturnItems() { mock.when(SecurityUtil::getCurrentUserId).thenReturn(userId); // when - CursorResponse response = - meetingService.meetingList(gatheringId, MeetingListFilter.ALL, size, null); + PageResponse response = + meetingService.meetingList(gatheringId, MeetingListFilter.ALL, pageable); // then assertThat(response.items()).hasSize(2); assertThat(response.pageSize()).isEqualTo(10); - assertThat(response.hasNext()).isFalse(); - assertThat(response.nextCursor()).isNull(); + assertThat(response.currentPage()).isEqualTo(0); + assertThat(response.totalPages()).isEqualTo(1); MeetingListItemResponse item1 = response.items().stream() .filter(item -> item.meetingId().equals(1L)) .findFirst()