개발 메모장

[QueryDSL] QueryDSL Projection 본문

ORM

[QueryDSL] QueryDSL Projection

yyyyMMdd 2024. 2. 1. 17:22
728x90

#. Projection이란?

  • Projection은 테이블의 특정 컬럼만 조회할 수 있게 해주는 역할을 합니다.

  • DB에서 필요한 데이터만 가져오기에 검색하는 데이터의 양을 줄이고 성능을 최적화하며 네트워크 오버헤드를 최소화하는데 도움이 됩니다.

#. Projection을 사용하는 이유

 

  1. 데이터 볼륨 최소화
    - 전체 행이나 테이블을 가져오는 것이 아니라 필요한 정보만 검색할 수 있습니다.
    - DB와 애플리케이션 간에 전송되는 데이터의 양을 줄이므로 대규모 데이터 세트를 처리할 때 유용합니다.

  2. 성능 향상
    - 필요한 열만 선택하면 쿼리의 전반적인 성능과 애플리케이션의 응답성을 향상시킬 수 있습니다.
    - 불필요한 데이터 검색으로 인한 트래픽이 증가 및 쿼리 실행 시간이 지연을 방지할 수 있습니다.

  3. 중복 방지
    - 현재 쿼리와 관련이 없는 열을 제외하여 결과의 중복을 방지하는 데 도움이 됩니다.
    - 이를 통해 애플리케이션의 특정 요구 사항에 맞게 출력을 맞춤화할 수 있습니다.

 


#. 왜 Projection을 사용해야 할까?

 

  • q객체를 이용해 원하는 컬럼만 가져올 수 있지만 이 경우 리턴을 Tuple객체로 해야 합니다.

  • Tuple 객체는 querydsl.core에서 사용하기 때문에 querydsl을 사용하지 않는 곳에서 데이터를 사용하려 하면 제대로 사용하기 어렵고 불편합니다.

  • 따라서 projections를 통해 기존 DTO로 리턴 받을 수 있게 할 수 있습니다.

  • 위처럼 여러 개의 컬럼을 조회하려 할 때 리턴형을 Tuple로 바꾸라고 오류가 발생합니다.

  • 동일한 경우에 Projection을 사용하면 DTO객체 그 자체로 받을 수 있게 됩니다.

 


1. setter를 이용한 projection

 

  • 위 사진과 같이 Projections.bean을 통해 DTO내 setter에 값을 채워줍니다.

  • 장점 :  setter를 이용하기에 생성자가 필요하지 않고 순서를 지키지 않아도 됩니다.

  • 단점 : 불변성 보장이 어렵고 setter에 들어갈 값이 많아지면 가독성이 떨어집니다.
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
	List<MemberEntity> setterResult = jpaQueryFactory.select(Projections.bean(MemberEntity.class, 
										 qMember.name, 
										 qMember.age))
							.from(qMember)
							.where(qMember.age.eq(34))
							.fetch();
	return ResponseEntity.ok(setterResult);
}

 


2. fields를 이용한 projection

 

  • 장점 : 불변성 유지가 가능합니다.

  • 단점 : 기본생성자와 getter가 필요하며, 필드 순서를 동일하게 유지해야 합니다.
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
	List<MemberEntity> fieldResult = jpaQueryFactory.select(Projections.fields(MemberEntity.class, 
                                                               qMember.name, 
                                                               qMember.age))
                                                         .from(qMember)
                                                         .where(qMember.age.eq(34))
                                                         .fetch();
	return ResponseEntity.ok(fieldResult);
}

 


3. constructor를 이용한 projection

 

  • 생성자를 이용한 projection의 경우 select 내에 입력한 값들에 대한 생성자가 있어야 정상적으로 작동합니다.

  • 아래 예제의 경우 name과 age를 사용하기 때문에 MemberEntity에 name, age 2개에 대한 생성자가 있다면 정상적으로 처리됩니다.

  • 장점 : 불변성을 보장하고 생성자의 인자를 통해 필드를 쉽게 지정할 수 있습니다.

  • 단점 : 순서를 정확히 맞춰야 합니다.
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
	List<MemberEntity> consturctorResult = jpaQueryFactory.select(Projections.constructor(MemberEntity.class, 
                                                                       qMember.name, 
                                                                       qMember.age))
                                                                 .from(qMember)
                                                                 .where(qMember.age.eq(34))
                                                                 .fetch();
	return ResponseEntity.ok(consturctorResult);
}

 


4. @QueryProjections를 이용한 projection

 

  • 어노테이션을 사용하여 projection 하는 방법입니다.

  • 어노테이션을 추가한 생성자는 q파일에 존재해야만 사용이 가능합니다.

  • 따라서 어노테이션을 추가했다면 컴파일을 다시 해주시길 바랍니다.

  • 장점 : 코드가 간결하고 불변성이 보장되며 순서 또한 상관없고 런타임에서 오류를 잡을 수 있습니다.

  • 단점 : 생성자에 어노테이션을 붙이기에 지나친 QueryDSL 의존일 수 있습니다.
// MemeberEntity.java
@QueryProjection
public MemberEntity(String name, int age) {
    this.name = name;
    this.age = age;
}

// Controller
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
	List<MemberEntity> consturctorResult = jpaQueryFactory.select(new QMemberEntity(
    									qMember.name,qMember.age))
                                                                 .from(qMember)
                                                                 .where(qMember.age.eq(34))
                                                                 .fetch();
	return ResponseEntity.ok(consturctorResult);
}

 


#. 위와 같이 projection을 처리하는 4가지 방법에 대해 알아보았습니다.


#. 어떤 것을 사용해도 큰 문제는 없습니다만 각각의 장단점이 있는 만큼 상황에 따라 선택하여 사용하시길 바랍니다.

 

 

 

===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================

728x90

'ORM' 카테고리의 다른 글

[QueryDSL] QueryDSL 사용방법(2)  (1) 2024.01.31
[QueryDSL] QueryDSL 사용방법(1)  (1) 2024.01.30
[JPA] JPA의 정의와 사용 예제  (1) 2024.01.10
[QueryDSL] QueryDSL 정의 및 설정방법  (0) 2024.01.08
[myBatis] myBatis 사용방법  (1) 2023.12.26