소프트웨어 마에스트로에서 "온라인 강의 큐레이션 서비스 - curady"를 개발하며 생긴 일
강의의 목록을 조회할 때 페이지별로 보여줘야한다.
/lectures?page=
위와 같이 쿼리 스트링으로 page를 받아 각 페이지에 맞는 강의 목록을 조회하도록 해야겠다.
만약 파라미터를 안주고 /lectures를 호출하면 1페이지를 반환해주어야겠다고 생각했다.
그런데 페이징 처리를 생각하다보니, 검색, 정렬방식, 필터 등도 함께 적용되어야겠다.
앞에 필터나 검색 조건에 따라 총 개수가 달라지고, 몇 페이지까지 존재할지가 동적으로 변하겠다는 생각이 들었다.
예를 들어 아래와 같이 호출하면
/lectures?page=1&order=recent&level=2&charge=free
난이도가 2이고, 가격은 무료인 강의들을 최신순으로 정렬하여 1페이지만 반환해주어야한다.
처음에는 LIMIT, offset 등을 이용한 쿼리를 고민해봤지만, 현재 Data JPA를 쓰고 있기 때문에 JpaRepository의 부모 인터페이스인 PagingAndSortingRepository을 사용해보기로 했다.
public LecturesResult<ResponseLectures> getAllLectures(Pageable pageable) {
return lectureService.getAllLectures(pageable);
}
@Configuration
public class PageableConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver = new PageableHandlerMethodArgumentResolver();
pageableHandlerMethodArgumentResolver.setOneIndexedParameters(true);
pageableHandlerMethodArgumentResolver.setMaxPageSize(24);
pageableHandlerMethodArgumentResolver.setFallbackPageable(PageRequest.of(0, 24));
resolvers.add(pageableHandlerMethodArgumentResolver);
}
}
Pageable 객체를 활용했고 WebMvcConfigurer를 구현하여 상황에 맞게 세팅해주었다.
그 후 그저 그것을 Repository로 넘겨서 paging을 할 수 있었다.
기존의 Response 방식은 다음과 같았다.
public class Result implements Serializable {
private boolean success;
private int code;
private String msg;
}
public class SingleResult<T> extends Result {
private T data;
}
public class MultipleResult <T> extends Result {
private List<T> data;
}
위처럼 Result 객체를 구성했고
public class ResponseService {
public <T> SingleResult<T> getSingleResult(T data) {
SingleResult<T> result = new SingleResult<>();
setSuccessResult(result);
result.setData(data);
return result;
}
public <T> MultipleResult<T> getMultipleResult(List<T> data) {
MultipleResult<T> result = new MultipleResult<>();
setSuccessResult(result);
result.setData(data);
return result;
}
...
@GetMapping("/lecture/{lectureId}")
public MultipleResult<ResponseLecture> getAllLecturesByCategoryId(@PathVariable Long categoryId) {
List<ResponseLecture> responseLectures = lectureService.getLecturesByCategoryId(categoryId);
return responseService.getMultipleResult(responseLectures);
}
Controller에서 바로 ResponseService를 통해 반환하고 있었다.
Paging된 결과를 반환할 때 리스트와 함께 총 페이지수를 계산해서 넘겨줘야 했다.
public class LecturesResult <T> extends Result {
private int totalPage;
private List<T> data;
}
그래서 위와 같이 총 페이지수와 페이징된 데이터의 리스트를 가지는 Result객체를 하나 추가하고,
@Transactional
public LecturesResult<ResponseLectures> getLecturesByCategoryId(Long categoryId, Pageable pageable) {
Page<Lecture> lecturePage = lectureRepository.findAllByCategoryId(categoryId, pageable);
List<ResponseLectures> responseLectures =
LectureMapper.INSTANCE.lecturesToResponseList(lecturePage.getContent());
return responseService.getLecturesResult(lecturePage.getTotalPages(), responseLectures);
}
@GetMapping("/lectures/{categoryId}")
public LecturesResult<ResponseLectures> getAllLecturesByCategoryId(@PathVariable Long categoryId, Pageable pageable) {
return lectureService.getLecturesByCategoryId(categoryId, pageable);
}
위처럼 Service단에서 ResponseService를 이용하여, Result를 만들어주었다.
그리고 Controller에서는 바로 Service의 메소드를 호출하여 반환하도록 수정했다.
실시간으로 데이터가 계속 추가되어 최신 데이터가 들어오면 페이지가 밀리는거 어떻게하지???
총 페이지수를 리턴하기 위해 카운트하는게 데이터가 정말 많아지면 어떻게 될까.??