Controller
에서 쿼리 스트링
으로 전달되는 페이징에 대한 정보를 Pageable
객체에 매핑시켜서 사용할 것 입니다.
제 기준 가~장 간단한 페이징 기법? 입니다.
@RequestMapping(value = "/expenses")
@RequiredArgsConstructor
@RestController
public class ExpenseController {
private final ExpenseService expenseService;
@GetMapping
public Page<ExpenseResponseDto> getAllExpenses(Pageable pageable) {
return expenseService.getAllExpenses(pageable);
}
}
page
, size
, sort
등 페이징 및 정렬에 필요한 파라미터를 Pagable
객체에 매핑시키고 그대로 Service
계층으로 넘겨주고 있습니다.
Controller
와 Repository
의 인터페이스 역할만을 수행하고 있습니다.
Controller
에서 직접 Repository
를 주입받아 사용하는 것도 큰 무리는 없어보이지만 다른 비즈니스 로직과 함께 두기 위해 Service
계층으로 내렸습니다.
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class ExpenseServiceImpl implements ExpenseService {
private final ExpenseRepository expenseRepository;
@Override
public Page<ExpenseResponseDto> getAllExpenses(Pageable pageable) {
return expenseRepository.findAllExpense(pageable);
}
}
DTO
로 직접 조회하는 방법을 사용했습니다.
Repository
계층에서 Service
계층의 DTO
를 참조하는 것이 설계상 별로인 것처럼 보이지만 Entity
타입으로 조회하고 다시 DTO
로 변환하는 작업을 한 번에 처리할 수 있다는 장점이 있어서 아래와 같은 방법을 사용했습니다.
일단 DTO
로 조회한 것은 무시하고..
Pageable
인자로 받아서 Page
타입으로 반환하는 것이 전부입니다.
public interface ExpenseRepository extends JpaRepository<Expense, Long> {
@Query("select new com.dhk.expensetrackerapi.service.dto.response.ExpenseResponseDto(e.id, e.name, e.description, e.amount, e.category, e.date, e.createdAt, e.updatedAt) from Expense e ")
Page<ExpenseResponseDto> findAllExpense(Pageable pageable);
}
성능을 떠나서 이런 편리함과 간결함이 Spring data JPA
를 사용하는 가장 큰 이유가 아닐까 라고 생각합니다.
/expenses?page=0&size=2
{
"content": [
{
"id": 1,
"name": "수도세",
"description": "수도세",
"amount": 9000,
"category": "청구서",
"date": "2022-03-06",
"createdAt": "2022-03-10T13:22:09",
"updatedAt": "2022-03-10T13:22:09"
},
{
"id": 2,
"name": "전기세",
"description": "전기세",
"amount": 32000,
"category": "청구서",
"date": "2022-03-06",
"createdAt": "2022-03-10T13:22:09",
"updatedAt": "2022-03-10T13:22:09"
}
],
"pageable": {
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"offset": 0,
"pageSize": 2,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 3,
"totalElements": 5,
"size": 2,
"number": 0,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"first": true,
"numberOfElements": 2,
"empty": false
}
/expenses?page=0&size=2&sort=amount
페이징 + amount
필드 기준 오름차순(default)
{
"content": [
{
"id": 5,
"name": "name",
"description": "description",
"amount": 1000,
"category": "category",
"date": "2022-03-09",
"createdAt": "2022-03-10T13:46:29",
"updatedAt": "2022-03-10T13:46:29"
},
{
"id": 1,
"name": "수도세",
"description": "수도세",
"amount": 9000,
"category": "청구서",
"date": "2022-03-06",
"createdAt": "2022-03-10T13:22:09",
"updatedAt": "2022-03-10T13:22:09"
}
],
"pageable": {
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"offset": 0,
"pageSize": 2,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 3,
"totalElements": 5,
"size": 2,
"number": 0,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"first": true,
"numberOfElements": 2,
"empty": false
}
/expenses?page=0&size=2&sort=amount,desc
페이징 + amount
필드 기준 내림차순
{
"content": [
{
"id": 3,
"name": "관리비",
"description": "관리비",
"amount": 60000,
"category": "청구서",
"date": "2022-03-09",
"createdAt": "2022-03-10T13:22:09",
"updatedAt": "2022-03-10T13:22:09"
},
{
"id": 2,
"name": "전기세",
"description": "전기세",
"amount": 32000,
"category": "청구서",
"date": "2022-03-06",
"createdAt": "2022-03-10T13:22:09",
"updatedAt": "2022-03-10T13:22:09"
}
],
"pageable": {
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"offset": 0,
"pageSize": 2,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 3,
"totalElements": 5,
"size": 2,
"number": 0,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"first": true,
"numberOfElements": 2,
"empty": false
}
sort
를 쿼리 스트링으로 넘기는 부분이 신기합니다..
?sort=필드,desc
오호 ...
Page
타입으로 리턴되는 값에 불필요한 값이 너무 많다면 content
부분과 필요한 부분만으로 응답 DTO를 구성하고 List
로 반환하는 것도 좋은 방법이라 생각됩니다
:)