오프셋 기반 페이지네이션
오프셋 기반의 페이지네이션은 페이지를 조회하기 위해 오프셋(offset)과 크기(size)를 사용하는 방식입니다.
이 방식은 특정 페이지의 데이터를 요청할 때, 이전 페이지까지의 데이터를 스캔해야 하는 단점이 있습니다.
이에 따라 성능 이슈가 발생할 수 있습니다.
오프셋 기반의 페이지네이션 구현
레포지터리 구성
먼저 오프셋 기반의 페이지 구성을 사용하도록 레포지터리를 구성해야 합니다.
레포지터리 인터페이스에서 findAllBy 메서드를 정의합니다.
public Page<Post> findAllByMemberId(Long memberId, Pageable pageRequest) {
var params = new MapSqlParameterSource()
.addValue("memberId", memberId)
.addValue("offset", pageRequest.getOffset())
.addValue("size", pageRequest.getPageSize());
Sort sort = pageRequest.getSort();
String query = String.format("""
SELECT *
FROM %s
WHERE memberId = :memberId
ORDER BY %s
LIMIT :offset, :size
""", TABLE, PageHelper.orderBy(sort));
var posts = namedParameterJdbcTemplate.query(query, params, ROW_MAPPER);
return new PageImpl<Post>(posts, pageRequest, getCount(memberId));
}
이 코드에서 findAllByMemberId
메소드는 memberId
및 pageable
매개변수를 허용하여 오프셋 기반 페이지 구성을 기반으로 특정 회원의 게시물을 검색합니다.
서비스 구성
다음으로 오프셋 기반 페이지 구성을 활용하도록 서비스 계층을 구성합니다. 방법은 다음과 같습니다.
- 리포지토리를 서비스 클래스에 삽입합니다.
@Service
public class PostService {
private final PostRepository postRepository;
}
- 서비스 클래스에서 오프셋 기반 페이지 매김을 사용하여 게시물을 검색하는 방법을 구현합니다.
private PostDto toDto(Post post) {
return new PostDto(
post.getId(),
post.getMemberId(),
post.getContents(),
post.getCreatedAt(),
postLikeRepository.countByPostId(post.getId())
);
}
public Page<PostDto> getPostDtos(Long memberId, Pageable pageRequest) {
return postRepository.findAllByMemberId(memberId, pageRequest).map(this::toDto);
}
위의 코드에서는 원하는 페이지와 크기로 PageRequest
개체를 생성한 다음 이를 사용하여 저장소의 findAllByMemberId
메서드를 호출합니다.
컨트롤러 구성
마지막으로 오프셋 기반 페이지 매김 요청을 처리하도록 컨트롤러를 구성합니다. 방법은 다음과 같습니다.
- 컨트롤러 클래스를 정의하고 여기에 서비스를 주입합니다.
@RestController
@RequestMapping("/members")
public class PostController {
private final PostService postService;
}
- 컨트롤러에서 오프셋 기반 페이지 매김을 사용하여, 게시물을 검색하는 엔드포인트를 구현합니다.
@GetMapping("/members/{memberId}")
public Page<PostDto> getPosts(
@PathVariable Long memberId,
Pageable pageRequest
) {
return postReadService.getPostDtos(memberId, pageRequest);
}
이 코드에서는 /members/{memberId}/posts
엔드포인트에 대한 GET 매핑을 정의하고 @RequestParam
주석을 사용하여 오프셋 기반 페이지 매김에 대한 page
및 size
매개변수를 지정합니다.
페이지 정렬
페이지네이션 기능에 추가로 정렬 기능을 구현 할 수 있습니다.
아래는 페이지 정렬을 위해 PageHelper 클래스를 사용하는 예제 코드입니다
public class PageHelper {
public static String orderBy(Sort sort) {
if (sort.isEmpty()) {
return "id DESC";
}
List<Sort.Order> orders = sort.toList();
List<String> orderByList = orders.stream()
.map(order -> order.getProperty() + " " + order.getDirection())
.toList();
return String.join(", ", orderByList);
}
}
PageHelper
클래스는 Sort
객체를 받아서 정렬 정보를 문자열로 변환해주는 유틸리티 클래스입니다.
이를 활용하여 페이지네이션 쿼리에 정렬 정보를 추가할 수 있습니다.
예를 들어, Pageable
객체에 정렬 정보가 포함되어 있다면, PageHelper.orderBy
메서드를 사용하여 정렬 정보를 문자열로 변환한 후 페이지네이션 쿼리에 추가합니다.
컨트롤러에서는 아래와 같이 페이지네이션 및 정렬 기능을 사용할 수 있습니다.
@GetMapping("/members/{memberId}")
public Page<PostDto> getPosts(
@PathVariable Long memberId,
@RequestParam Integer page,
@RequestParam Integer size,
@RequestParam(required = false, defaultValue = "id,desc") String sort
) {
Sort pageableSort = Sort.by(Sort.Direction.DESC, sort);
Pageable pageRequest = PageRequest.of(page, size, pageableSort);
return postReadService.getPostDtos(memberId, pageRequest);
}
위 코드에서는 /members/{memberId}
엔드포인트를 통해 특정 회원의 게시물 목록을 페이지네이션 및 정렬 기능을 통해 조회할 수 있습니다.
page
와 size
는 페이지 정보를, sort
는 정렬 정보를 받아와서 PageRequest
객체를 생성합니다.
이제 오프셋 기반의 페이지네이션을 구현하고 페이지 정렬을 추가할 수 있게 되었습니다.
부족한 점이나 잘못 된 점을 알려주시면 시정하겠습니다 :>
'DEV > Backend' 카테고리의 다른 글
커버링 인덱스 (0) | 2023.06.09 |
---|---|
커서 기반 페이지네이션 (0) | 2023.06.09 |
페이지네이션 (0) | 2023.06.09 |
조회 최적화를 위한 인덱스 (2) | 2023.06.02 |
DTO(Data Transfer Object) (0) | 2023.06.02 |