Spring data JPA(7) - 확장 기능 - 페이징과 정렬

Chooooo·2023년 9월 8일
0

TIL

목록 보기
19/28

😎 Web 확장 - 도메인 클래스 컨버터

HTTP 파라미터로 넘어온 엔티티의 아이디로 엔티티 객체를 찾아서 바인딩

😁 도메인 클래스 컨버터 사용 전

@RestController
@RequiredArgsConstructor
public class MemberController {
	private final MemberRepository memberRepository;
	@GetMapping("/members/{id}")
	public String findMember(@PathVariable("id") Long id) {
		Member member = memberRepository.findById(id).get();
		return member.getUsername();
	}
}

😁 도메인 클래스 컨버터 사용 후

@RestController
@RequiredArgsConstructor
public class MemberController {
	private final MemberRepository memberRepository;
	@GetMapping("/members/{id}")
	public String findMember(@PathVariable("id") Member member) {
		return member.getUsername();
	}
}

😊 이유

  • HTTP 요청은 회원 id 를 받지만 도메인 클래스 컨버터가 중간에 동작해서 회원 엔티티 객체를 반환
  • 도메인 클래스 컨버터도 리파지토리를 사용해서 엔티티를 찾음

주의: 도메인 클래스 컨버터로 엔티티를 파라미터로 받으면, 이 엔티티는 단순 조회용으로만 사용해야 한다.
(트랜잭션이 없는 범위에서 엔티티를 조회했으므로, 엔티티를 변경해도 DB에 반영되지 않는다.)

  • 근데 외부에 PK를 공개할 일도 거의 없고... 사실 거의 안 쓴다고 말하신다.
  • 그냥 안 쓰는게 마음 편해

😎 Web 확장 - 페이징과 정렬

스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.

😊 페이징과 정렬 예제

@GetMapping("/members")
    public Page<Member> list(Pageable pageable) {
        Page<Member> page = memberRepository.findAll(pageable);
        return page;
    }
  • 파라미터로 Pageable을 받을 수 있다. -> 마지막 파라미터로 넘겨주면 돼 !!
  • Pageable은 인터페이스, 실제는 org.springframework.data.domain.PageRequest 객체 생성

😊 요청 파라미터 -> 파라미터 바인딩 !

예) /members?page=0&size=3&sort=id,desc&sort=username,desc

  • page: 현재 페이지, 0부터 시작한다.
  • size: 한 페이지에 노출할 데이터 건수
  • sort: 정렬 조건을 정의한다. 예) 정렬 속성,정렬 속성...(ASC | DESC), 정렬 방향을 변경하고 싶으면 sort 파라미터 추가 ( asc 생략 가능)

🎃 파라미터 바인딩

Controller의 파라미터에 Pageable 객체를 받는 것으로 설정을 해주면, Request 요청이 오게 되면 내부적으로 Argument Resolver등이 동작해서 Pageable 객체를 만들어준다.
Pageable 객체를 인자로 받게 된다면, 특정 쿼리 파라미터를 바인딩해서 Pageable에 넣어준다. 이 때 사용되는 쿼리 파라미터는 위에서 확인할 수 있다.

[공식문서]를 보면 어떤 식인지 바로 알 수 있다.

Pageable 객체 기본값, 작은 범위에 적용하는 방법 -> @PageableDefault

@PageableDefault 어노테이션을 이용하면 컨트롤러 단위로 Pageable의 default Value를 설정할 수 있다.

  • page : 몇 번째 페이지를 불러올지 설정
  • size : 한 페이지에 가져올 데이터 갯수
  • sort : 어떤 것을 기준으로 정렬할 것인지
  • direction : 어떤 방향으로 정렬할 것인지

😊 기본값 설정

  • 글로벌 설정 : 스프링부트
    spring.data.web.pageable.default-page-size=20 /# 기본 페이지 사이즈/
    spring.data.web.pageable.max-page-size=2000 /# 최대 페이지 사이즈/

  • 개별 설정
    PageableDefault 어노테이션 사용

@RequestMapping(value = "/members_page", method = RequestMethod.GET)
public String list(@PageableDefault(size = 12, sort = "username",
 direction = Sort.Direction.DESC) Pageable pageable) {
 ...
}
  • @PageableDefault 사용 방법 보면 돼

😊 접두사

  • 페이징 정보가 둘 이상이면 접두사로 구분
  • @Qualifier 에 접두사명 추가 "{접두사명}_xxx"
  • 예제: /members?member_page=0&order_page=1
public String list(
 @Qualifier("member") Pageable memberPageable,
 @Qualifier("order") Pageable orderPageable, ...

😊 Page 내용을 DTO로 변환하기

  • 엔티티를 API로 노출하면 다양한 문제가 발생한다. 그래서 엔티티를 꼭 DTO로 변환해서 반환해야 한다.
  • Page는 map() 을 지원해서 내부 데이터를 다른 것으로 변경할 수 있다.

🎈 MemberDto

@Data
public class MemberDto {
    private Long id;
    private String username;
    public MemberDto(Member m) {
        this.id = m.getId();
        this.username = m.getUsername();
    }
}

🎈 Page.map() 사용

@GetMapping("/members")
    public Page<MemberDto> list(Pageable pageable) {
        Page<Member> page = memberRepository.findAll(pageable);
        Page<MemberDto> pageDto = page.map(MemberDto::new);
        return pageDto;
    }

🎈 Page.map() 코드 최적화

@GetMapping("/members")
    public Page<MemberDto> list(Pageable pageable) {
        return memberRepository.findAll(pageable).map(MemberDto::new);
    }

😊 Page를 1부터 시작하기

  • 스프링 데이터는 Page를 0부터 시작한다.
    • 만약 1부터 시작하려면 ?
  1. Pageable, Page를 파리미터와 응답 값으로 사용히지 않고, 직접 클래스를 만들어서 처리한다. 그리고 직접 PageRequest(Pageable 구현체)를 생성해서 리포지토리에 넘긴다. 물론 응답값도 Page 대신에 직접 만들어서 제공해야 한다.

  2. spring.data.web.pageable.one-indexed-parameters 를 true 로 설정한다. 그런데 이 방법은 web에서 page 파라미터를 -1 처리 할 뿐이다. 따라서 응답값인 Page 에 모두 0 페이지 인덱스를 사용하는 한계가 있다.

  • 그래서 합의하에 그냥 0부터 하는게 깔끔하다.
profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글