public class MemberDto {
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@Query("SELECT new com.example.dto.MemberDto(m.username, m.age) FROM Member m")
List<MemberDto> findMemberDto();
new 키워드를 사용하여 DTO 객체를 직접 생성실무에서 많이 사용되는 방식
방법 1. 생성자 기반
public class MemberDto {
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class, member.username, member.age))
.from(member)
.fetch();
방법 2. 필드 기반
public class MemberDto {
public String username;
public int age;
}
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class, member.username, member.age))
.from(member)
.fetch();
방법 3. Getter/Setter 기반
public class MemberDto {
private String username;
private int age;
// getter, setter
}
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class, member.username, member.age))
.from(member)
.fetch();
처음에는 Entity 그대로 반환하는 게 편했지만 프로젝트 규모가 커질수록 유지보수나 응답 최적화를 위해 DTO를 사용하는 게 맞다는 걸 체감하게 된다.
QueryDSL의 Projections는 다양한 스타일로 매핑할 수 있어서 목적에 맞는 방식을 유연하게 선택할 수 있다는 장점이 있다.
앞으로는 API 설계할 때 DTO를 먼저 정의하고 쿼리는 DTO에 맞게 projection하는 방향으로 개발 습관을 들여야겠다.