Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -21,10 +23,12 @@ public class MyBarController {
@GetMapping
public RsData<MyBarListResponseDto> 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"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<MyBarItemResponseDto> items;
private boolean hasNext; // 다음 페이지 존재 여부
private Integer nextPage; // 다음 페이지 번호(없으면 null)
}
private boolean hasNext; // 다음 페이지 존재 여부
private LocalDateTime nextKeptAt; // 다음 페이지 시작용 keptAt
private Long nextId; // 다음 페이지 시작용 id
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<MyBar, Long> {
/** 나만의 bar(킵) 목록: ACTIVE만, id desc */
Page<MyBar> 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<MyBar> findSliceByCursor(Long userId, KeepStatus status, LocalDateTime keptAt, Long id, Pageable pageable);

/** 프로필/요약용: ACTIVE 개수 */
long countByUser_IdAndStatus(Long userId, KeepStatus status);

Expand Down
36 changes: 29 additions & 7 deletions src/main/java/com/back/domain/mybar/service/MyBarService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<MyBar> 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<MyBar> rows;
Pageable pageable = PageRequest.of(0, fetchSize);

if (lastKeptAt == null || lastId == null) {
Page<MyBar> 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<MyBar> myBars = myBarPage.getContent();
List<MyBarItemResponseDto> 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
Expand Down Expand Up @@ -75,3 +96,4 @@ public void unkeep(Long userId, Long cocktailId) {
myBarRepository.softDeleteByUserAndCocktail(userId, cocktailId);
}
}