diff --git a/src/main/java/com/back/domain/mybar/controller/MyBarController.java b/src/main/java/com/back/domain/mybar/controller/MyBarController.java index 022c3e52..65fe4425 100644 --- a/src/main/java/com/back/domain/mybar/controller/MyBarController.java +++ b/src/main/java/com/back/domain/mybar/controller/MyBarController.java @@ -9,6 +9,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; @RestController @RequestMapping("/me/bar") @@ -21,10 +23,12 @@ public class MyBarController { @GetMapping public RsData getMyBarList( @AuthenticationPrincipal(expression = "id") Long userId, - @RequestParam(defaultValue = "0") @Min(0) int page, - @RequestParam(defaultValue = "20") @Min(1) @Max(100) int pageSize + @RequestParam(required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime lastKeptAt, + @RequestParam(required = false) Long lastId, + @RequestParam(defaultValue = "20") @Min(1) @Max(100) int limit ) { - MyBarListResponseDto body = myBarService.getMyBar(userId, page, pageSize); + MyBarListResponseDto body = myBarService.getMyBar(userId, lastKeptAt, lastId, limit); return RsData.successOf(body); // code=200, message="success" } diff --git a/src/main/java/com/back/domain/mybar/dto/MyBarListResponseDto.java b/src/main/java/com/back/domain/mybar/dto/MyBarListResponseDto.java index 040a3806..7c39dc91 100644 --- a/src/main/java/com/back/domain/mybar/dto/MyBarListResponseDto.java +++ b/src/main/java/com/back/domain/mybar/dto/MyBarListResponseDto.java @@ -3,12 +3,14 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import java.time.LocalDateTime; import java.util.List; @Getter @AllArgsConstructor public class MyBarListResponseDto { private List items; - private boolean hasNext; // 다음 페이지 존재 여부 - private Integer nextPage; // 다음 페이지 번호(없으면 null) -} \ No newline at end of file + private boolean hasNext; // 다음 페이지 존재 여부 + private LocalDateTime nextKeptAt; // 다음 페이지 시작용 keptAt + private Long nextId; // 다음 페이지 시작용 id +} diff --git a/src/main/java/com/back/domain/mybar/repository/MyBarRepository.java b/src/main/java/com/back/domain/mybar/repository/MyBarRepository.java index 0ba75c50..b8aa380d 100644 --- a/src/main/java/com/back/domain/mybar/repository/MyBarRepository.java +++ b/src/main/java/com/back/domain/mybar/repository/MyBarRepository.java @@ -10,12 +10,23 @@ import org.springframework.stereotype.Repository; import java.util.Optional; +import java.util.List; +import java.time.LocalDateTime; @Repository public interface MyBarRepository extends JpaRepository { /** 나만의 bar(킵) 목록: ACTIVE만, id desc */ Page findByUser_IdAndStatusOrderByKeptAtDescIdDesc(Long userId, KeepStatus status, Pageable pageable); + @Query(""" + select m from MyBar m + where m.user.id = :userId + and m.status = :status + and (m.keptAt < :keptAt or (m.keptAt = :keptAt and m.id < :id)) + order by m.keptAt desc, m.id desc + """) + List findSliceByCursor(Long userId, KeepStatus status, LocalDateTime keptAt, Long id, Pageable pageable); + /** 프로필/요약용: ACTIVE 개수 */ long countByUser_IdAndStatus(Long userId, KeepStatus status); diff --git a/src/main/java/com/back/domain/mybar/service/MyBarService.java b/src/main/java/com/back/domain/mybar/service/MyBarService.java index aea90bdc..fe934d62 100644 --- a/src/main/java/com/back/domain/mybar/service/MyBarService.java +++ b/src/main/java/com/back/domain/mybar/service/MyBarService.java @@ -10,6 +10,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,18 +27,38 @@ public class MyBarService { private final UserRepository userRepository; private final CocktailRepository cocktailRepository; + // 커서: lastKeptAt + lastId를 그대로 파라미터로 사용 @Transactional(readOnly = true) - public MyBarListResponseDto getMyBar(Long userId, int page, int pageSize) { - Page myBarPage = myBarRepository.findByUser_IdAndStatusOrderByKeptAtDescIdDesc(userId, KeepStatus.ACTIVE, PageRequest.of(page, pageSize)); + public MyBarListResponseDto getMyBar(Long userId, LocalDateTime lastKeptAt, Long lastId, int limit) { + int safeLimit = Math.max(1, Math.min(limit, 100)); + int fetchSize = safeLimit + 1; // 다음 페이지 여부 판단용으로 1개 더 조회 + + List rows; + Pageable pageable = PageRequest.of(0, fetchSize); + + if (lastKeptAt == null || lastId == null) { + Page page0 = myBarRepository + .findByUser_IdAndStatusOrderByKeptAtDescIdDesc(userId, KeepStatus.ACTIVE, pageable); + rows = page0.getContent(); + } else { + rows = myBarRepository.findSliceByCursor(userId, KeepStatus.ACTIVE, lastKeptAt, lastId, pageable); + } + + boolean hasNext = rows.size() > safeLimit; + if (hasNext) rows = rows.subList(0, safeLimit); - List myBars = myBarPage.getContent(); List items = new ArrayList<>(); - for (MyBar myBar : myBars) items.add(MyBarItemResponseDto.from(myBar)); + for (MyBar myBar : rows) items.add(MyBarItemResponseDto.from(myBar)); - boolean hasNext = myBarPage.hasNext(); - Integer nextPage = hasNext ? myBarPage.getNumber() + 1 : null; + LocalDateTime nextKeptAt = null; + Long nextId = null; + if (hasNext && !rows.isEmpty()) { + MyBar last = rows.get(rows.size() - 1); + nextKeptAt = last.getKeptAt(); + nextId = last.getId(); + } - return new MyBarListResponseDto(items, hasNext, nextPage); + return new MyBarListResponseDto(items, hasNext, nextKeptAt, nextId); } @Transactional @@ -75,3 +96,4 @@ public void unkeep(Long userId, Long cocktailId) { myBarRepository.softDeleteByUserAndCocktail(userId, cocktailId); } } +