내가 만들어놓은 통합검색 기능에 페이징처리를 추가하는 작업을 진행하였다.
페이징 방식은 무한스크롤 방식이다.
No - Offset 방식은 조회 시작하는 부분을 인덱스로 빠르게 찾아서 매번 첫 페이지만 읽도록 하는 방식이다.
기존의 Offset방식은 만약 Offset이 100이고 limit이 10이면 처음부터 100까지 모든 데이터를 가져오고 limit만큼만 반환해 준다. 이 방식을 사용할 수는 있지만 성능상 효율적이지 않아 사용한 방식이 No - Offset이다.
Slice 형태로 응답했을 때 나오는 JSON 형식.
content에 검색결과인 식당이 담기고, last페이지인지 sort는 어떻게 했는지 다 담겨져 나온다.
slice는 카운트쿼리가 나가지 않고 다음 slice가 존재하는지 여부만 확인할 수 있기때문에, 데이터 양이 많으면 많을수록 slice를 사용하는것이 성능상 유리하다.
page는 게시판과 같이 총 데이터 갯수가 필요한 환경에서,
slice는 모바일과 같이 총 데이터 갯수가 필요없는 환경
//통합검색
public Slice<Restaurant> searchByNameAndAddress(String keyword, Pageable pageable) {
List<Restaurant> restaurantList = em.createQuery("select r from Restaurant r where r.name like :keyword or r.address_name like :keyword", Restaurant.class)
.setParameter("keyword", keyword)
.setFirstResult((int) pageable.getOffset())
.setMaxResults(pageable.getPageSize())
.getResultList();
return new SliceImpl<>(restaurantList, pageable, restaurantList.size() >= pageable.getPageSize());
}
//키워드를 포함하는 주소나 가게이름 조회
public Slice<Restaurant> searchByKeywordContaining(String keyword, Pageable pageable) {
List<Restaurant> restaurantList = em.createQuery("select r from Restaurant r where r.name like CONCAT('%', :keyword, '%') or " +
"r.address_name like CONCAT('%', :keyword, '%') order by r.avgRating desc", Restaurant.class)
.setParameter("keyword", keyword)
.setFirstResult((int) pageable.getOffset())
.setMaxResults(pageable.getPageSize())
.getResultList();
return new SliceImpl<>(restaurantList, pageable, restaurantList.size() >= pageable.getPageSize());
}
setFirstResult((int) pageable.getOffset())
를 사용해서 검색 결과의 시작 위치를 설정.setMaxResults(pageable.getPageSize())
를 사용해 한 페이지에 표시할 결과수를 설정하였다.new SliceImple<>()
를 사용하여 Slice 객체를 생성하고restaurantList
는 검색결과를 담고, pageable
은 페이지 정보, restaurantList.size() >= pageable.getPageSize()
은 다음 페이지가 있는지 여부를 확인하는데 쓰인다. //통합 검색
public Slice<Restaurant> searchByNameAndAddress(String keyword, Pageable pageable) {
if (keyword.length() < 2) {
throw new IllegalArgumentException("최소 두 글자 이상 입력해 주세요.");
}
Slice<Restaurant> restaurants = restaurantRepository.searchByNameAndAddress(keyword, pageable);
if (restaurants.isEmpty()) {
restaurants = restaurantRepository.searchByKeywordContaining(keyword, pageable);
}
return restaurants;
}
//통합검색
@GetMapping("api/v1/restaurants/search")
public Slice<RestaurantDTO> keywordSearch(@RequestParam("keyword") String keyword,@PageableDefault(size = 10, page = 0) Pageable pageable) {
Slice<Restaurant> restaurants = restaurantService.searchByNameAndAddress(keyword, pageable);
if (restaurants.isEmpty()) {
restaurants = restaurantService.searchByNameAndAddress(keyword, pageable);
}
/* List<Restaurant> restaurantList = restaurants.getContent();
System.out.println("restaurantList: "+ restaurantList);*/
List<RestaurantDTO> restaurantDTOS = restaurants.getContent().stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
return new SliceImpl<>(restaurantDTOS, restaurants.getPageable(), restaurants.hasNext());
}
@PageableDefault 어노테이션을 사용하여 기본설정을 0페이지 10개 조회되게 하였다.