페이징 처리를 하는 이유
findAll() 로 모든 Post 를 가지고 오는 정책에는 한계가 있다.
만약 글이 100,000,000 개라면 DB 에 있는 Post 모두 조회하는 경우 DB 가 뻗을 수도 있다.
또한 DB 에서 애플리케이션 서버로 전달하는 시간, 트래픽 비용 등이 많이 발생할 수 있다.
Pageable
페이징 문제는 우리의 고대 선배들부터 꾸준히 겪어온 문제이기 때문에 스프링에서 Pageable 이란 객체를 제공한다.
우리는 이를 이용해서 페이징 로직을 작성해보자.
PostController
기존 컨트롤러의 getList() 메서드에서 Pageable 객체를 이용해서 페이징을 구현한다.
@GetMapping("/posts")
public List<PostResponse> getList(Pageable pageable) {
return postService.getList(pageable);
}
ㄴ Pageable
스프링의 기본 설정이 @ModelAttribute 이므로 앞에 @ModelAttribute 가 생략돼있다.
URL 에 쿼리 파라미터 형태로 넘어온 데이터가 Pageable 객체에 맵핑되고 서비스에 Pageable 객체를 넘겨준다.
localhost:8080/posts?page=1&sort=id,desc
위와 같은 URL 로 요청이 들어오면 page = 1 이고 id 를 기준으로 내림차순으로 정렬하라는 데이터가 Pageable 에 맵핑된다.
대략 아래와 같은 Pageable 객체가 만들어진다.
Pageable pageable = PageRequest.of(0, 5, Sort.by(Sort.Direction.DESC, "id"));
PostService
기존 서비스의 getList() 에서 findAll() 은 매개변수로 Pageable 받을 수 있다.
컨트롤러에서 넘어온 Pageable 객체를 이용해서 DB 에서 페이징 처리된 글들을 받는다.
public List<PostResponse> getList(Pageable pageable) {
return postRepository.findAll(pageable).stream()
.map(post -> new PostResponse(post))
.collect(Collectors.toList());
}
PostServiceTest
페이징을 확인하기 위해 Post 를 32개 집어넣고 Post 의 id 를 기준으로 5개씩 내림차순으로 페이징하려 한다.
이 때 JSON 응답으로 Post 가 5개 내려왔는지 확인하고 첫번째와 마지막 글 제목을 확인하여
내림차순으로 원하는 결과가 나왔는지 확인하는 테스트를 작성한다.
@DisplayName("글 1페이지 내림차순 조회")
@Test
void getListDesc() {
// 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);
Pageable pageable = PageRequest.of(0, 5, Sort.by(Sort.Direction.DESC, "id"));
// when
List<PostResponse> posts = postService.getList(pageable);
// then
assertEquals(5L, posts.size());
assertEquals("짤녀 공주면 자러감", posts.get(0).getTitle());
assertEquals("미카 공주님 찬양 27", posts.get(4).getTitle());
}
ㄴ PageRequest.of()
위와 같이 매개변수를 받을 수 있다. 위 테스트 코드처럼 작성 시 매개변수는 순서대로
페이지 번호, 페이지 당 받을 데이터 수, 정렬순서 및 기준 에 해당한다.
ㄴ Sort.by()
위와 같이 매개변수를 받을 수 있고 기본값은 Sort.Direction.ASC 로 오름차순 정렬이다.
Sort.Direction.DESC 를 추가해주면 내림차순 정렬로 바꿀 수 있으며 테스트 코드 처럼 작성 시 정렬 기준도 정할 수 있다.
테스트 수행 시 우리가 원했던 대로 동작하며 테스트가 성공함을 알 수 있다.
application.yml
서비스 테스트와는 다르게 컨트롤러 테스트는 테스트 전에 application.yml 설정을 해줘야한다.
spring:
h2:
console:
enabled: true
path : /h2-console
data:
web:
pageable:
one-indexed-parameters: true
default-page-size: 5
ㄴ spring.web.pageable.one-indexed-parameters: true
페이징은 1이 아니라 0부터 시작한다.
따라서 우리가 생각하는 id 가 1번부터 5까지의 데이터를 받고싶다면 1부터 시작하도록 설정해줘야한다.
ㄴ spring.web.pageable.default-page-size
페이징의 기본 사이즈는 20이다.
우리는 5개의 데이터를 받기로 했으니 기본 사이즈를 5로 설정한다.
PostControllerTest
페이징을 확인하기 위해 Post 를 32개 집어넣고 Post 의 id 를 기준으로 5개씩 내림차순으로 페이징하려 한다.
URL 에 쿼리 파라미터 형태로 넘어온 데이터가 Pageable 에 제대로 맵핑 됐는지 확인하기 위해
JSON 응답으로 내려온 Post 갯수 및 title, content 내용을 확인하는 테스트를 작성한다.
@DisplayName("글 1페이지 내림차순 조회")
@Test
void getListDesc() 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("/posts?page=1&sort=id,desc")
.contentType(APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", Matchers.is(5)))
.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 |
---|---|
[게시글 조회] 페이징 처리 - Querydsl (0) | 2023.09.25 |
[게시글 조회] 게시글 여러개 조회 (0) | 2023.09.24 |
[게시글 조회] 리팩토링 2 (0) | 2023.09.23 |
[게시글 조회] 단건 조회 (0) | 2023.09.23 |