QueryDsl 을 사용한 페이징
이전 게시글처럼 페이징을 구현하는것도 좋은 방법이지만 QueryDsl 을 사용해서 좀 더 유연한 페이징을 한다면
내가 원하는 페이징 클래스를 따로 만들고 그 안에서 검증을 마친 데이터를 이용할 수 있다.
QueryDsl 을 적용하는 방법은 여기를 참고하자.
PostController
컨트롤러에서 getList() 메서드가 쿼리 파라미터를 통해 PostSearch 를 전달인자로 받는다
테스트에서 헷갈리지 않게 요청 URL 을 잘 기억하자.
@GetMapping("/posts2")
public List<PostResponse> getListQueryDsl(@ModelAttribute PostSearch postSearch) {
return postService.getListQueryDsl(postSearch);
}
ㄴ @ModelAttribute
왜 여기에 이걸 다는지는 복습도 할 겸 여기를 참고하자.
기본값이 @ModelAttribute 이므로 아래처럼 생략해도 동작하지만 가독성을 위해 붙이는것도 좋다.
@GetMapping("/posts2")
public List<PostResponse> getListQueryDsl(PostSearch postSearch) {
return postService.getListQueryDsl(postSearch);
}
PostSearch
페이징에 관련된 정보를 담는 객체이다.
이 객체에서 기본값을 설정을 할 수 있고 서비스 정책에 맞도록 데이터를 가공할 수도 있다.
대부분의 경우 서버 정책대로 기본값을 주기 때문에 여기서 page 및 size 의 기본값을 설정한다.
또한 페이징은 인덱스 0부터 시작하기 때문에 여기서 page 의 값을 조정하고 음수가 될 수 없게 처리한다.
@Getter
@Builder
public class PostSearch {
private static final int MAX_SIZE = 2000;
private static final int MIN_PAGE = 1;
@Builder.Default
private Integer page = 1;
@Builder.Default
private Integer size = 10;
public long getOffset() {
return (long) (Math.max(MIN_PAGE, page) - 1) * MAX_SIZE;
}
}
ㄴ @Builder.Default
위처럼 클래스 단위로 @Builder 를 달고 따로 생성자를 만들지 않으면 클라이언트 요청에서 해당 값이 없을 시 기본값을 입력한다.
여기서는 page 의 기본값은 1이고 size 는 10으로 하겠다.
PostService
컨트롤러에서 받은 PostSearch 객체의 데이터로 postRepository 를 호출하여 페이징 된 Post 를 컨트롤러로 반환한다.
public List<PostResponse> getListQueryDsl(PostSearch postSearch) {
return postRepository.getList(postSearch).stream()
.map(post -> new PostResponse(post))
.collect(Collectors.toList());
}
PostRepositoryImpl
QueryDsl 을 적용하여 원하는 페이징 메서드를 작성한다.
여기서는 PostSearch 에 담긴 데이터를 이용해 size, offset 을 설정하고 Post 의 id 값을 기준으로 내림차순 정렬한다.
@Repository
@RequiredArgsConstructor
public class PostRepositoryImpl implements PostRepositoryCustom{
private final JPAQueryFactory jpaQueryFactory;
@Override
public List<Post> getList(PostSearch postSearch) {
return jpaQueryFactory.selectFrom(QPost.post)
.limit(postSearch.getSize())
.offset(postSearch.getOffset())
.orderBy(QPost.post.id.desc())
.fetch();
}
}
PostServiceTest
페이징을 확인하기 위해 Post 를 32개 집어넣고 Post 의 id 를 기준으로 10개씩 내림차순으로 페이징한다.
이 때 JSON 응답으로 Post 가 10개 내려왔는지 확인하고 첫번째와 마지막 글 제목을 확인하여
내림차순으로 원하는 결과가 나왔는지 확인하는 테스트를 작성한다.
@DisplayName("글 1페이지 내림차순 조회 QueryDsl")
@Test
void getListQueryDsl() throws Exception {
// given
Post post1 = Post.builder()
.title("우리 이쁜 미카공주님")
.content("우리 공주님")
.build();
postRepository.save(post1);
List<Post> requestPosts = IntStream.rangeClosed(1, 30)
.mapToObj(i -> {
return Post.builder()
.title("미카 공주님 찬양 " + i)
.content("찬양내용 " + i)
.build();
})
.collect(Collectors.toList());
postRepository.saveAll(requestPosts);
Post post2 = Post.builder()
.title("짤녀 공주면 자러감")
.content("잘 자")
.build();
postRepository.save(post2);
// expected
mockMvc.perform(
MockMvcRequestBuilders.get("/posts2?page=1&size=10")
.contentType(APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", Matchers.is(10)))
.andExpect(jsonPath("$[0].title", Matchers.is("짤녀 공주면 자러감")))
.andExpect(jsonPath("$[0].content", Matchers.is("잘 자")))
.andExpect(jsonPath("$[4].title", Matchers.is("미카 공주님 찬양 27")))
.andExpect(jsonPath("$[4].content", Matchers.is("찬양내용 27")))
.andDo(print());
}
}
테스트 수행 시 우리가 원했던 대로 동작하며 테스트가 성공함을 알 수 있다.
PostControllerTest
페이징을 확인하기 위해 Post 를 32개 집어넣고 Post 의 id 를 기준으로 10개씩 내림차순으로 페이징하려 한다.
URL 에 쿼리 파라미터 형태로 넘어온 데이터가 PostSearch 에 제대로 맵핑 됐는지 확인하기 위해
JSON 응답으로 내려온 Post 갯수 및 title, content 내용을 확인하는 테스트를 작성한다.
@DisplayName("글 1페이지 내림차순 조회 QueryDsl")
@Test
void getListQueryDsl() throws Exception {
// given
Post post1 = Post.builder()
.title("우리 이쁜 미카공주님")
.content("우리 공주님")
.build();
postRepository.save(post1);
List<Post> requestPosts = IntStream.rangeClosed(1, 30)
.mapToObj(i -> {
return Post.builder()
.title("미카 공주님 찬양 " + i)
.content("찬양내용 " + i)
.build();
})
.collect(Collectors.toList());
postRepository.saveAll(requestPosts);
Post post2 = Post.builder()
.title("짤녀 공주면 자러감")
.content("잘 자")
.build();
postRepository.save(post2);
// expected
mockMvc.perform(
MockMvcRequestBuilders.get("/posts2?page=1&size=10")
.contentType(APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", Matchers.is(10)))
.andExpect(jsonPath("$[0].title", Matchers.is("짤녀 공주면 자러감")))
.andExpect(jsonPath("$[0].content", Matchers.is("잘 자")))
.andExpect(jsonPath("$[4].title", Matchers.is("미카 공주님 찬양 27")))
.andExpect(jsonPath("$[4].content", Matchers.is("찬양내용 27")))
.andDo(print());
}
테스트 수행 시 우리가 원했던 대로 동작하며 테스트가 성공함을 알 수 있다.
'API 만들어 보기 > 게시판 API' 카테고리의 다른 글
[게시글 삭제] 게시글 삭제 (0) | 2023.09.26 |
---|---|
[게시글 수정] 게시글 수정 (0) | 2023.09.26 |
[게시글 조회] 페이징 처리 1 (0) | 2023.09.25 |
[게시글 조회] 게시글 여러개 조회 (0) | 2023.09.24 |
[게시글 조회] 리팩토링 2 (0) | 2023.09.23 |