[내일배움캠프 Spring 4기 - 최종 프로젝트] 92일차 TIL : 페이징 구현

서예진·2024년 4월 4일
0

오늘의 학습 키워드

페이징 구현
페이징 적용

📖 페이징 구현


Pageable

  • 페이징을 제공하는 중요한 인터페이스
  • Pageable 만드는 법
PageRequest.of(int page, int size) : 0부터 시작하는 페이지 번호와 개수. 정렬이 지정되지 않음
PageRequest.of(int page, int size, Sort sort) : 페이지 번호와 개수, 정렬 관련 정보
PageRequest.of(int page int size, Sort sort, Direction direction, String ... props) : 0부터 시작하는 페이지 번호와 개수, 정렬의 방향과 정렬 기준 필드들
  • of() 메서드를 호출하면 실제 page 객체가 반환됨
  • Pageable 메서드
pageable.getTotalPages() : 총 페이지 수
pageable.getTotalElements() : 전체 개수
pageable.getNumber() : 현재 페이지 번호
pageable.getSize() : 페이지 당 데이터 개수
pageable.hasnext() : 다음 페이지 존재 여부
pageable.isFirst() : 시작페이지 여부
pageable.getContent(), PageRequest.get() : 실제 컨텐츠를 가지고 오는 메서드. getContext는 List<Entity> 반환, get()Stream<Entity> 반환

정렬

컬럼값으로 정렬하기

Sort sort1 = Sort.by("name").descending();     // 내림차순
Sort sort2 = Sort.by("password").ascending();  // 오름차순
Sort sortAll = sort1.and(sort2);      // 2개이상 다중정렬도 가능하다
Pageable pageable = PageRequest.of(0, 10, sortAll);  // pageable 생성시 추가 / (페이지 넘버, limit, sort)

Pageable 과 실제 페이지사이의 -1 문제 해결하기

  • JPA 페이지는 0부터인데 화면은 1부터시작하는 문제
    • -1 처리를 중복으로 해줘야하는 이슈
  • PageDTO 를 만들어서 toPageable() 메소드를 사용
public class PageDTO {
  @Positive // 0보다 큰수
  private Integer currentPage;
  private Integer size;
  private String sortBy;

  public Pageable toPageable() {
    return PageRequest.of(currentPage-1, size, Sort.by(sortBy).descending());
  }
}

📖 페이징 적용


  • 이벤트를 전체 조회할 때 페이징 구현이 필요했다.
  • JPA를 활용하여 페이징 구현을 했다.

Controller.java

EventController.java

	@GetMapping
	public ResponseEntity<Page<EventResponse>> getAllEvents(
		@RequestParam(defaultValue = "0") int page,
		@RequestParam(defaultValue = "20") int size) {
		Pageable pageable = PageRequest.of(page, size, Sort.by(Direction.DESC, "createdAt"));
		Page<EventResponse> eventResponses = eventService.getAllEvents(pageable);

		return ResponseEntity.ok().body(eventResponses);
	}
  • 현재 정렬은 이벤트가 생성된 일자를 기준으로 내림차순으로 정렬한다.
  • 사용자가 입력한 값 기준으로 정렬하는 부분도 추가로 구현해야할 필요를 느꼈다.
  • PageDTO를 만들어서 적용해볼 필요성도 느꼈다.

Service.java

EventServiceImpl.java

    @Transactional(readOnly = true)
	public Page<EventResponse> getAllEvents(Pageable pageable) {

		return eventRepository.findAll(pageable)
			.map(event -> {
				List<Long> eventProducts = eventQuery.getEventProducts(event.getId());
				return EventResponse.from(event, eventProducts);
			});
	}

Respository.java

EventQueryImpl.java

public List<Long> getEventProducts(Long eventId) {
		return jpaQueryFactory.select(
			QEventProduct.eventProduct.product.id)
			.from(QEventProduct.eventProduct)
			.where(QEventProduct.eventProduct.event.id.eq(eventId))
			.fetch();
	}

조회 결과

  • Page : 기본적인 반환 메서드로 여러 반환 타입 중 하나이다.
{
    "content": [
        {
            "id": 2,
            "title": "제목",
            "content": "내용",
            "limitNum": null,
            "openAt": "2024-05-09",
            "eventProducts": [
                1,
                2
            ]
        },
        {
            "id": 1,
            "title": "제목",
            "content": "내용",
            "limitNum": null,
            "openAt": "2024-05-09",
            "eventProducts": [
                1,
                2
            ]
        }
    ],
    "pageable": {
        "pageNumber": 0, // 페이지 번호 (0번 부터 시작)
        "pageSize": 20, // 페이지 크기
        "sort": {
            "empty": false,    // 정렬 상태
            "unsorted": false,
            "sorted": true
        },
        "offset": 0, // 해당 페이지의 첫번째 요소의 전체 순번
        "paged": true,
        "unpaged": false
    },
    "last": true,
    "totalPages": 1, // 페이지로 제공되는 총 페이지 수
    "totalElements": 2, // 모든 페이지에 존재하는 총 원소 수
    "sort": {
        "empty": false,    // 정렬 사용 여부
        "unsorted": false,
        "sorted": true
    },
    "first": true, // 첫페이지 여부
    "size": 20, // Contents 사이즈
    "number": 0,
    "numberOfElements": 2, // Contents 의 원소 수
    "empty": false // 공백 여부
}

문제점

  • 이벤트를 전체 조회할 때, 이벤트와 연관된 eventProducts를 가져오기 위해 이벤트 수대로 select 쿼리를 날리고 있다.
  • 쿼리 하나로 줄일 방법은 없을까?
    • 아직 찾아보는 중이다.
profile
안녕하세요

0개의 댓글