말이 좀 이상하다
정렬 기준 하나로 어떻게 정렬을 두 개 설정?한다는거지 싶겠지만
차차 설명해보도록 하겠다.
@GetMapping("/movie/{tmdbId}")
public ResponseEntity<PageResponse<ReviewResponseDTO>> getReviewsByMovieId(
@PathVariable Long tmdbId,
@RequestParam(value = "certifiedFilter", required = false, defaultValue = "false") Boolean certifiedFilter,
@PageableDefault(size = 20, sort = "createdAt", direction = Direction.DESC) Pageable pageable
) {
Page<ReviewResponseDTO> reviews = reviewService.getReviewByMovieId(tmdbId, pageable, certifiedFilter);
return ResponseEntity.ok(new PageResponse<>(reviews));
}
@PageableDefault로 디폴트 정렬을 사이즈는 20, createdAt 최신순으로 리뷰 데이터를 조회할 수 있도록 했다.
그러면 /api/reviews/movie/{tmdbId}로만 요청을 하면 기본적으로 작성일시 기준 최신순으로 조회가 된다.
Review 엔티티에는 좋아요 수(likeCount)도 있기 때문에
/api/reviews/movie/{tmdbId}?sort=likeCount,desc
로 요청을 하면 좋아요 수를 기준으로 내림차순으로 보여준다.
그런데 좋아요 수가 같은 경우가 있지 않은가!
이럴 때를 위해 두 번째 정렬이 필요하기 때문에
/api/reviews/movie/{tmdbId}?sort=likeCount,desc&sort=createdAt,desc
이런 식으로 정렬 기준을 2개를 같이 적어줘야
좋아요 순으로 내림차순 정렬하되, 동수가 발생하면 작성일을 기준으로 최신순으로 보여주게 된다.
- BE
: FE님! 좋아요순으로 정렬하고 싶으시면 ?sort=likeCount,desc&sort=createdAt,desc 이런 식으로 요청하시면 됩니다~- FE
: 정렬 기준은 좋아요 수 한 개만 주세요. 근데 동수 발생시 최신순으로는 나오게 해주세요- BE
: 앗...! 넹... (일단 해본다고 한다)
@PageableDafault 대신 @RequestParam을 사용하여 수동으로 정렬로직을 구현해봤다.
디폴트 정렬은 createdAt으로 하되,
좋아요 순 정렬 요청이 들어오면(sortType이 "likeCount"인 경우,) 좋아요 수 내림차순을 정렬을 하고,
좋아요 수가 동점인 경우 createdAt(작성일시) 기준으로 추가 정렬을 해주는 것이다.
@GetMapping("/movie/{tmdbId}")
public ResponseEntity<PageResponse<ReviewResponseDTO>> getReviewsByMovieId(
@PathVariable Long tmdbId,
@RequestParam(value = "certifiedFilter", required = false, defaultValue = "false") Boolean certifiedFilter,
@RequestParam(value = "sort", required = false, defaultValue = "createdAt") String sortType,
@PageableDefault(size = 20) Pageable pageable
) {
Sort sort;
if (sortType.equals("likeCount")) {
sort = Sort.by(Sort.Order.desc("likeCount"), Sort.Order.desc("createdAt"));
} else {
sort = Sort.by(Sort.Order.desc("createdAt"));
}
Pageable sortedPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
Page<ReviewResponseDTO> reviews = reviewService.getReviewByMovieId(tmdbId, sortedPageable, certifiedFilter);
return ResponseEntity.ok(new PageResponse<>(reviews));
}
특히 likeCount가 정렬 기준으로 들어오면 sort = Sort.by(Sort.Order.desc("likeCount"), 이 부분을 통해 좋아요 수 내림차순 정렬로 고정을 하게 된다.
좋아요 수는 대체로 내림차순 정렬을 하니까 고정하는 게 효율적이라고 생각했다.
그리고 Sort.Order.desc("createdAt")); 이 부분을 통해 좋아요 수 정렬 후, 최신순으로 정렬을 하게 된다.
- BE
: FE님!?sort=likeCount이렇게 요청하시면 desc도 작성하실 필요 없고 다른 정렬 기준도 작성하실 필요 없어용! 효율적이죠!!- FE
: 그냥 다른 거 주던 거랑 똑같이 주세요. sort=likeCount,desc 이렇게요. 나중에 다른 정렬 기준도 생길 수 있어서 확장성을 위해서 동일하게 하는 게 낫지 않을까요?- BE
: 앗... 넹.... (이제 어떻게 해야할지 모르겠지만 일단 해본다고 한다)
다시 원상태로 돌아왔다.
?sort=likeCount,desc로든 ?sort=likeCount,asc로든 정렬 기준과 정렬 방향을 적어주되,
동수가 나와서 정렬 기준이 한 개 더 필요함에도 불구하고 정렬 기준은 한 개만 달라.....
어떻게 하면 좋을까?
일단 다시 @PageableDefault를 사용하여 ?sort=정렬기준,정렬방향 형식으로 정렬 요청을 할 수 있도록 수정했다.
추후에 likeCount 외에도 rating(별점)도 정렬 기준으로 사용할 수 있기 때문에 Sort.Order 객체들을 리스트에 담기로 했다.
이를 위해 pageable.getSort().toList 를 이용해서 현재 요청된 정렬 조건을 List<Sort.Order>형태로 꺼내서 새 리스트에 복사했다.
즉, pageable 객체에 들어 있는 정렬 조건들을 꺼내서 리스트로 만든 것이다.
List<Sort.Order> orders = new ArrayList<>(pageable.getSort().toList());
그리고 orders 리스트를 .stream()으로 순회하며
order.getProperty().anyMatch()를 이용하여 좋아요수("likeCount")가 있는지 확인하고 있으면 true를 반환한다.
orders.stream().anyMatch(order -> order.getProperty().equals("likeCount")
만약 이게 참인데 (좋아요 수 정렬 요청했는데) 정렬 조건에 createdAt이 포함되지 않은 경우, 동점 처리용으로 createdAt 기준 내림차순 정렬을 추가로 넣어줬다:
boolean hasCreatedAt = orders.stream()
.anyMatch(order -> order.getProperty().equals("createdAt"));
if (!hasCreatedAt) {
orders.add(Sort.Order.desc("createdAt"));
}
마지막으로 orders 리스트를 기반으로 새로운 Sort 객체를 만들고,
이를 반영한 새로운 Pageable을 생성하여 내가 원하는 복합 정렬 조건이 적용되도록 해줬다.
@Operation(summary = "특정 영화에 대한 리뷰 조회", description = "댓글은 포함되어있지 않습니다." )
@GetMapping("/movie/{tmdbId}")
public ResponseEntity<PageResponse<ReviewResponseDTO>> getReviewsByMovieId(
@PathVariable Long tmdbId,
@RequestParam(value = "certifiedFilter", required = false, defaultValue = "false") Boolean certifiedFilter,
@PageableDefault(size = 20, sort = "createdAt", direction = Direction.DESC) Pageable pageable
) {
List<Sort.Order> orders = new ArrayList<>(pageable.getSort().toList());
if (orders.stream().anyMatch(order -> order.getProperty().equals("likeCount"))) {
boolean hasCreatedAt = orders.stream().anyMatch(order -> order.getProperty().equals("createdAt"));
if (!hasCreatedAt) {
orders.add(Sort.Order.desc("createdAt"));
}
}
Pageable newPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(orders));
Page<ReviewResponseDTO> reviews = reviewService.getReviewByMovieId(tmdbId, newPageable, certifiedFilter);
return ResponseEntity.ok(new PageResponse<>(reviews));
}
이러면 ?sort=likeCount,desc와 같이 정렬 기준을 likeCount 한 개로 요청시,
좋아요 순으로 정렬이 되고, 동수가 발생하면 작성 일시 기준 최신순으로 정렬이 되는 복합 정렬이 잘 이루어진다!
문제는 해결이 됐다만,
그런데 뭔가 좀..... 지저분한 거 같은데
좀 더 좋은 방법이 있으면 알려주시기를 부탁드립니다..!!!!!