[Spring Data JPA] 프로젝션(Projection)
포스트
취소

[Spring Data JPA] 프로젝션(Projection)

프로젝션이란?

  • 엔티티의 특정 속성만 선택하여 조회하는 기능

특징

  • 데이터 추출 최적화
    • 필요한 데이터만 조회하여 성능을 향상시키고 네트워크 트래픽을 줄일 수 있다.
  • DTO 매핑 간소화
    • 별도의 DTO 클래스를 만들지 않고도 원하는 데이터 형식으로 결과를 받을 수 있다.
  • 코드 간결성
    • 쿼리 코드를 간결하고 명확하게 작성할 수 있다.

사용 방법

  • ※ Member 엔티티의 String username 속성만 가져오고 싶다고 가정하고 예시를 작성한다.
  1. 프로젝션 인터페이스를 정의한다.
  • 엔티티에서 가져올 속성에 대해서 getter 메소드명을 작성하는 방식으로 작성한다.
  • 인터페이스만 가능한 건 아니고 구체적인 DTO 형식도 가능하다.
    • 생성자의 파라미터명으로 매칭된다.
public interface UsernameOnly {
	String getUsername();
}
  • 프로젝션은 엔티티에 대해서 target이라는 속성으로 커스텀 값을 만들어서 반환할 수 있다.
  • 대힌 이렇게 SpEL 문법을 사용하면 DB에서 엔티티 필드를 다 조회해온 다음에 계산한다.
    • JPQL SELECT절 최적화가 안된다.
public interface UsernameOnly {
	@Value("#{target.username + '' + target.age + ' ' + target.team.name}")
	String getUsername();
}
  1. 리포지토리에 프로젝션을 반환하는 메소드를 생성한다.
  • 아래 예시에서는 Member 엔티티를 사용하는 리포지토리를 사용했기 때문에 기본적으로는 Member 엔티티에 대한 프로젝션만 사용할 수 있다.
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<UsernameOnly> findProjectionByUsername(@Param("username") String name);
}
  • 다만 제네릭 타입을 명시하면 동적으로 프로젝션 데이터를 변경할 수 있다.
public interface MemberRepository extends JpaRepository<Member, Long> {
    <T> List<T> findProjectionByUsername(String name, Class<T> type);
}
  1. 2번에서 생성한 메소드를 통해서 프로젝션을 반환한다.
@Test
public void projections() {
    //given
    Team teamA = new Team("teamA");
    em.persist(teamA);
    
    Member m1 = new Member("m1", 0, teamA);
    Member m2 = new Member("m2", 0, teamA);
    em.persist(m1);
    em.persist(m2);
    
    em.flush();
    em.clear();
    
    //when
    List<UsernameOnly> result = memberRepository.findProjectionByUsername("m1");
    //List<UsernameOnly> result = memberRepository.findProjectionByUsername("m1", UsernameOnlyDto.class);
    for (UsernameOnly usernameOnly : result) {
        System.out.println("usernameOnly = " + usernameOnly.getUsername());
    }
    
    //then
}

주의 사항

  • 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.
    • 프로젝션 대상이 root 엔티티를 넘어가면 JPQL SELECT 최적화가 안 된다.
    • 프로젝션 대상이 root 엔티티가 아니면 LEFT OUTER JOIN이 되며, 모든 필드를 엔티티로 조회한 다음에 계산한다.
  • 프로젝션은 엔티티의 속성만 선택적으로 조회한다.
    • 엔티티 전체 정보가 필요한 경우에는 사용할 수 없다.
  • 프로젝션은 엔티티 클래스와 밀접하게 연결되어 있다.
    • 엔티티 클래스가 변경되면 프로젝션 인터페이스도 변경해야 한다.
  • 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.
    • 실무에서는 단순할 때만 사용하고, 조금이라도 복잡해지면 QueryDSL을 사용하는 것이 좋다.

출처

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.