본문 바로가기
JPA/스프링 DATA JPA

Data JPA 12 Web확장 : 도메인 클래스 컨버터, 페이징과 정렬

by 킹차니 2022. 1. 9.

도메인 클래스 컨버터

 

아래와 같이 member의 id인 pk로 멤버를 @PathVariable을 사용하여 조회하는 컨트롤러가 있다고 해보자.

@RestController
@RequiredArgsConstructor
public class MemberController {
    private final MemberRepository memberRepository;

    @GetMapping("/members/{id}")
    public String findMember(@PathVariable("id") Long id){
        Member member = memberRepository.findById(id).orElseGet(()->new Member("emptyMember"));
        return member.getUsername();
    }
}

member의 id가 1이고, username이 "userA"일 때 아래와 같이 요청하면 위의 findMember 컨트롤러는 "userA"를 반환해줄 것이다. (물론 id가 1인 member가 있는 경우에)

localhost:8080/members/1    //서버로부터 "userA" 응답받음

 

하지만 이와 같은 경우에 도메인 클래스 컨버터를 사용하여 더 쉽게 조회하는 방법이 있다.

@RestController
@RequiredArgsConstructor
public class MemberController {

    @GetMapping("/members2/{id}")
    public String findMember2(@PathVariable("id") Member member){
        return member.getUsername();
    }
}

위와 같이 조회 시 pk를 사용할 경우, @PathVariable("id") Member member를 사용하면 컨트롤러의 코드 상으로 리퍼지토리를 거치치 않고, 바로 조회할 수 있도록 해준다.

 

하지만 도메인 클래스 컨버터는 아주 간단한 경우에만, 사용해야 한다면 조회시에만 사용하는 것을 권장한다.

 

 


 

페이징과 정렬

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

 

이전에 Page인터페이스를 페이징을 위해 사용하였는데, 컨트롤러에서 바로 Page<Member>타입을 반환하고 컨트롤러의 인자로 Pageable 인터페이스를 받으면 그에 맞게 JSON을 반환해준다.(현재 RestController 사용중)

@RestController
@RequiredArgsConstructor
public class MemberController {

private final MemberRepository memberRepository;


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

	//PostConstruct로 회원 100명 생성
    @PostConstruct
    public void init(){
        for(int i=0; i<100; i++)
            memberRepository.save(new Member("user"+i, i, null));
    }
}

이제 해당 url로 요청을 날려보면

localhost:8080/members

 

아래와 같이 JSON으로 데이터가 잘 넘어오는 것을 볼 수 있다.

 

그리고 심지어 아래와 같이 "?page=0" 쿼리 스트링을 붙여주면 첫페이지를 가져다준다.

localhost:8080/members?page=0

(default 값이 20이라 20개의 데이터만 가져다준다. id는 1부터 시작해서 1씩 증가 )

 

 

그리고 아래처럼 size까지 붙여주면 3개만을 limit으로 가져온다.

localhost:8080/members?page=0&size=3

 

또한 아래와 같이 sort조건을 추가할 수도 있다.

http://localhost:8080/members?page=0&size=3&sort=id //3개씩 첫 페이지, id 기준 오름차순 정렬
http://localhost:8080/members?page=0&size=3&sort=id,desc //3개씩 첫 페이지, id 기준 내림차순 정렬

 

 


 

글로벌로 디폴트 값 설정하기 :

 

만약 위처럼 쿼리 스트링을 넣지 않으면 스프링이 지정한 기본값으로 size, sort등이 정해진다. 이와 같은 기본값을 수정할 수 있다.

data:
  web:
    pageable:
      default-page-size: 10 #page default size는 10으로
      max-page-size: 2000 #최대 페이지 size

 

 

 

지역적으로 디폴트 값 설정하기 :

 

@PageableDefault를 사용하여 가능하다.

@GetMapping("/members")
public Page<Member> list(@PageableDefault(size = 5, sort="username") Pageable pageable){
    Page<Member> page = memberRepository.findAll(pageable);
    return page;
}

(물론 지역적 디폴트 값 설정이 글로벌 설정보다 우선한다.)

 

 


 

DTO로 반환하기

 

전에도 엔티티를 그대로 반환하는 것은 절대 안된다하였다. 하여 아래처럼 map을 사용하여 Dto로 변환하여 반환해준다.

@RestController
@RequiredArgsConstructor
public class MemberController {

    private final MemberRepository memberRepository;
    private final TeamRepository teamRepository;

    @GetMapping("/members")
    public Page<MemberDto> list(@PageableDefault(size = 5, sort="username") Pageable pageable){
        Page<MemberDto> dtoPage = memberRepository.findAll(pageable)
                .map((m) -> new MemberDto(m.getId(), m.getUsername(), m.getTeam().getName()));/*map으로 DTO로 전환*/
        return dtoPage;
    }

    @PostConstruct
    public void init(){
        Team team = new Team("ManCity");
        teamRepository.save(team);
        for(int i=0; i<100; i++)
            memberRepository.save(new Member("user"+i, i, team));
    }
}

 

localhost:8080/members 요청 결과 :

 

 


 

 

 

참고:
엔티티는 DTO를 모르게, DTO는 엔티티를 알아도 된다. 즉 아래와 같은 코드는 편리하고, 사용해도 된다. 하지만 그 반대는 안된다.
import lombok.Data;
import study.datajpa.entity.Member;

@Data
public class MemberDto {
    private Long id;
    private String username;
    private String teamName;

    public MemberDto(Member member) {
        this.id = member.getId();
        this.username = member.getUsername();
        this.teamName = member.getTeam().getName();
    }
}

이렇게 하면 컨트롤러의 코드도 줄일 수 있다.

@GetMapping("/members")
public Page<MemberDto> list(@PageableDefault(size = 5, sort="username") Pageable pageable){
    return memberRepository.findAll(pageable).map(MemberDto::new);/*map으로 DTO로 전환*/
}