https://sundaland.tistory.com/22
[ ▶ Slice ]
Spring Data JPA의 Slice는 페이징 처리에서 효율성을 높이기 위한 대안으로, Page 객체와 비교해 일부 간략화된 정보를 제공하는 페이징 결과 객체이다. Slice는 전체 데이터 수를 계산하지 않고도 페이징을 처리할 수 있어 성능 면에서 이점을 제공한다. 더 자세히 설명하자면, Slice는 현재 페이지의 데이터와 다음 페이지로 이동할 수 있는지 여부만 제공하며, 전체 데이터의 수를 포함하지 않는다는 점이 특징이다.
[ ▷ Slice와 Page의 비교 ]
Page와 Slice는 모두 페이징 처리를 위해 사용되지만, 두 클래스는 제공하는 정보가 다르다.
△ Page
- 전체 데이터 개수 (getTotalElements())
- 총 페이지 수 (getTotalPages())
- 현재 페이지에 해당하는 데이터 (getContent())
- 페이지 번호, 페이지 크기
- 전체 페이징 정보를 계산합니다. 이는 성능에 부담이 될 수 있으며, 전체 데이터의 개수를 알기 위해 추가적인 쿼리가 필요할 수 있다.
△ Slice
- 현재 페이지에 해당하는 데이터 (getContent())
- 다음 페이지가 존재하는지 여부 (hasNext())
- Slice는 전체 데이터 개수를 계산하지 않고 데이터의 일부를 가져온다. 전체 데이터 개수를 몰라도, 현재 페이지가 끝났는지 여부와 다음 페이지로 이동할 수 있는지 여부만을 알 수 있다.
[ ▷ Slice의 주요 메서드 ]
- getContent(): 현재 페이지의 데이터 리스트를 리턴한다.
- getNumber(): 현재 페이지의 인덱스를 리턴한다. (0부터 시작)
- getSize(): 요청한 페이지 크기를 리턴한다.
- hasNext(): 다음 페이지가 존재하는지 여부를 확인한다.
- hasPrevious(): 이전 페이지가 존재하는지 여부를 확인한다.
- isFirst(): 현재 페이지가 첫 페이지인지 여부를 확인한다.
- isLast(): 현재 페이지가 마지막 페이지인지 여부를 확인한다.
[ ▷ 성능 면에서의 이점 ]
- 전체 데이터 개수 조회 생략: Page 객체는 전체 데이터의 개수(SELECT COUNT)를 조회하여 총 페이지 수를 계산한다. 이는 데이터 양이 많을 때 성능에 영향을 미칠 수 있다. 반면, Slice는 다음 페이지가 있는지 여부만 확인하기 때문에 추가적인 데이터 카운팅 작업을 하지 않아 성능상 이점을 가진다.
- 다음 페이지 유무만 확인: Slice는 기본적으로 "현재 페이지"와 "다음 페이지로 넘어갈 수 있는지 여부"만을 알면 충분한 상황에서 유용하다. 예를 들어, 무한 스크롤과 같은 기능을 구현할 때 전체 데이터 개수나 총 페이지 수가 필요 없으므로 Slice를 사용하는 것이 적합하다.
[ ▷ 동작 방식 ]
Slice는 기본적으로 요청한 페이지 크기보다 하나 더 많은 데이터를 조회한 후, 추가 데이터가 존재하는지 확인하여 hasNext() 값을 설정한다. 예를 들어, 페이지 크기를 10으로 설정한 경우 실제로는 11개의 데이터를 조회하여 11번째 데이터가 존재하면 hasNext()가 true가 됩니다. 이 방식은 전체 데이터를 카운팅하지 않고도 다음 페이지가 존재하는지 확인할 수 있게 해준다.
[ ▷ Slice를 사용하는 상황 ]
- 무한 스크롤: 사용자가 페이지 하단으로 스크롤할 때마다 데이터를 불러오는 방식의 UI에서, 전체 데이터 개수를 알 필요 없이 다음 페이지로 넘어가는 기능이 중요하다. 이 경우, Slice를 사용하면 성능상 이점을 얻을 수 있다.
- 데이터가 많을 때: 대량의 데이터를 다룰 때, 전체 데이터 개수를 카운트하는 작업이 비용이 많이 들 수 있다. Slice는 이러한 상황에서 불필요한 데이터 카운트를 생략하고 성능을 최적화할 수 있다.
[ ▷ Repository 인터페이스 정의 ]
Slice를 사용하려면 Repository 메서드에서 리턴 타입을 Slice로 정의해야 한다.
public interface UserRepository extends JpaRepository<User, Long> {
Slice<User> findByLastname(String lastname, Pageable pageable);
}
이 메서드는 주어진 lastname을 가진 사용자를 페이지 단위로 조회한다. 리턴 타입이 Slice이므로 전체 데이터 개수를 계산하지 않고 결과를 리턴한다.
[ ▷ 서비스에서 Slice 사용 ]
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public Slice<UserDTO> processUsers(String lastname, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Slice<User> userSlice = userRepository.findByLastname(lastname, pageable);
// User -> UserDTO로 변환
List<UserDTO> userDTOs = userSlice.getContent().stream()
.map(user -> new UserDTO(user.getFirstName(), user.getLastName()))
.toList();
// Slice로 변환해서 반환
return new SliceImpl<>(userDTOs, pageable, userSlice.hasNext());
}
}
위 코드에서 processUsers 메서드는 주어진 성씨를 가진 사용자를 페이징 처리하여 Slice 객체로 리턴받는다. getContent()로 현재 페이지의 데이터를 얻고, hasNext()로 다음 페이지가 있는지 확인할 수 있다.
[ ▷ Controller에서 Slice 사용 ]
package com.intheeast.demo.controller;
import com.intheeast.demo.dto.UserDTO;
import com.intheeast.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Slice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 특정 성을 기준으로 사용자를 페이징 처리하여 조회
*
* @param lastname 성 필터
* @param page 조회할 페이지 번호
* @param size 페이지 크기
* @return 페이징된 사용자 정보(Slice)
*/
@GetMapping("/api/users/process")
public Slice<UserDTO> processUsers(@RequestParam String lastname,
@RequestParam int page,
@RequestParam int size) {
return userService.processUsers(lastname, page, size);
}
}
processUsers 메서드에서 반환값이 정의되지 않았으므로, 현재는 단순히 콘솔 출력만을 하고 있다. 만약 processUsers 메서드가 컨트롤러에서 호출되는 경우, API는 HTTP 응답으로 데이터를 반환해야 하기 때문에 반환값을 정의해줘야 한다.
다음은 processUsers 메서드를 수정하여 Slice<UserDTO>를 반환하고, 그 결과를 컨트롤러에서 JSON 형태로 응답하는 예시이다.
▼ 수정된 UserService
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public Slice<UserDTO> processUsers(String lastname, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Slice<User> userSlice = userRepository.findByLastname(lastname, pageable);
// User -> UserDTO로 변환
List<UserDTO> userDTOs = userSlice.getContent().stream()
.map(user -> new UserDTO(user.getFirstName(), user.getLastName()))
.toList();
// Slice로 변환해서 반환
return new SliceImpl<>(userDTOs, pageable, userSlice.hasNext());
}
}
- processUsers 메서드는 이제 Slice<UserDTO>를 반환한다.
- User 객체를 UserDTO로 변환하여 리스트로 담고, 이를 다시 SliceImpl로 변환해 반환한다.
- hasNext()를 사용하여 다음 페이지가 있는지 여부도 설정한다.
▼ UserController
package com.intheeast.demo.controller;
import com.intheeast.demo.dto.UserDTO;
import com.intheeast.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Slice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 특정 성을 기준으로 사용자를 페이징 처리하여 조회
*
* @param lastname 성 필터
* @param page 조회할 페이지 번호
* @param size 페이지 크기
* @return 페이징된 사용자 정보(Slice)
*/
@GetMapping("/api/users/process")
public Slice<UserDTO> processUsers(@RequestParam String lastname,
@RequestParam int page,
@RequestParam int size) {
return userService.processUsers(lastname, page, size);
}
}
- URL: /api/users/process
- HTTP 메서드: GET
- 파라미터:
- lastname (String) - 필터링할 성
- page (int) - 조회할 페이지 번호
- size (int) - 페이지 당 데이터 크기
- 응답: Slice<UserDTO> (페이징된 사용자 정보)
GET /api/users/process?lastname=Doe&page=0&size=10
- 주어진 lastname으로 페이징된 사용자 데이터를 반환한다.
- 각 페이지의 사용자 정보(firstName, lastName)를 UserDTO로 반환한다.
- 다음 페이지가 있는지 여부도 응답에 포함된다.
이제 processUsers는 실제 데이터를 반환하는 형태로 동작하며, API 호출 시 해당 데이터를 클라이언트가 받을 수 있다.
[ ▷ 요약 ]
- Slice는 페이징 처리 시 전체 데이터 개수를 필요로 하지 않는 상황에서 성능을 최적화할 수 있는 객체다.
- Slice는 현재 페이지 데이터와 다음 페이지로 이동 가능한지 여부만 제공한다.
- 무한 스크롤이나 전체 데이터 개수가 불필요한 상황에서 Slice를 사용하는 것이 적합하다.
- Slice를 통해 성능을 향상시키면서도 필요한 페이징 기능을 제공받을 수 있다.
Slice는 이런 효율성 덕분에, 데이터 양이 많거나 전체 개수를 불필요하게 조회하고 싶지 않을 때 특히 유용하다.
'Spring Boot > Spring Data JPA' 카테고리의 다른 글
Query Lookup Strategies (0) | 2024.10.24 |
---|---|
Paging, Iterating Large Results, Sorting & Limiting (0) | 2024.10.24 |
Repository Methods Returning Collections or Iterables (0) | 2024.10.24 |
Property Expressions (0) | 2024.10.24 |
Reserved Method Names (0) | 2024.10.22 |