| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Thymeleaf
- Spring
- BOJ
- hexagonal
- spring boot
- 이벤트 기반
- EC2
- event
- android studio
- Hexagonal Architecture
- CSRF
- Domain-Driven
- 클린 아키텍처
- Rabbit MQ
- rabbitmq
- 컨텍스트 맵
- clean architecture
- Ubiquitous Language
- JPA
- 공통 언어
- 헥사고날
- Spring Cloud
- nopasswd
- 헥사고날 아키텍처
- 백준
- Domain Driven
- subdomain
- 안드로이드 스튜디오
- Context map
- DTO Projection
Archives
- Today
- Total
취미겸생업
[Spring boot] JPA Projection 본문
SMALL
문제 상황
JPA를 이용해 로직을 작성하다보면 연관 Entity 여러개를 합쳐 DTO로 만들거나, 필요한 필드만 조회해 최적화해야 하는 경우가 있다
QueryDSL을 사용하지 않고 순수 JPA를 사용할 경우, Object 또는 Tuple을 사용하는 방법 외에 아래 3가지 방법들이 있다
해결법
Interface Projection
특징
- Spring Data JPA가 런타임에 객체를 생성해주며, 가장 단순한 방법이다.
- 방식에 따라 JPA에 의해 자동으로 최적화 되어 필요한 필드만 뽑아서 쓸 수 있다.
Closed vs Open
Closed Projection : Entity 필드 이름과 Interface의 Getter 메서드가 정확히 일치하는 경우. JPA에 의해 성능 자동 최적화 된다.
Open Projection : @Value와 SpEL을 사용해 데이터를 가공하여 조합하는 경우. Entity 전체를 일단 메모리에 모두 올리기 때문에 성능상 이점이 없다.
public interface UserSummary { @Value("#{target.username + ' (' + target.email + ')'}") String getUserInfo(); // 여러 필드를 조합하여 새로운 값을 만듦 }
예제 코드
public interface UserSummary { // User 필드 중 필요한 필드만 써야 할 경우 String getUsername(); String getEmail(); } public interface UserRepository extends JpaRepository<User, Long> { List<UserSummary> findByUsername(String username); }여러 Entity를 합쳐 DTO로 조합할 경우
public interface UserSummary { String getUsername(); Long getCommentCount(); } public interface UserRepository extends JpaRepository<User, Long> { @Query("SELECT u.username as username, COUNT(c) as commentCount" + " FROM User u" + " LEFT JOIN Comment c ON c.author = u" + " WHERE u.id = :id" + " GROUP BY u.id") List<UserSummary> findById(@Param("id") Long id); }
DTO Projection
특징
- 생성자를 통해 직접 필요한 필드를 조회하고 DTO를 조합하기 때문에 직접적으로 관리할 수 있다. 엄밀한 최적화가 필요할 경우 사용할 수 있다.
- 쿼리 문 안에 패키지 경로가 포함되거나 쿼리가 텍스트로 관리되기 때문에 코드를 직접 관리해야 한다.
예제 코드
public class UserSummary { private String username(); private String email(); } public interface UserRepository extends JpaRepository<User, Long> { @Query("SELECT new com.example.UserSummary(u.username, u.email)" + " FROM User u" + " WHERE u.username = :username") List<UserSummary> findByUsername(String username); }
Dynamic Projection
특징
- 쿼리 조건은 같은데 반환 값의 필드를 유동적으로 선택해서 사용하고 싶을 경우 사용해 메서드 폭발을 방지할 수 있다.
- 런타임에서 동작하기 때문에 타입 안정성을 신경써야 한다.
예제 코드
public interface UserRepository extends JpaRepository<User, Long> { // 반환 타입을 제네릭 <T>로 지정하고, 파라미터로 원하는 타입을 전달 <T> List<T> findByUsername(String username, Class<T> type); } List<UserSummary> summary = userRepository.findByUsername("user01", UserSummary.class); List<UserDetails> details = userRepository.findByUsername("user01", UserDetails.class);
LIST
'IT > Spring Boot' 카테고리의 다른 글
| [Spring boot] MSA, Spring Cloud 개요 (0) | 2026.06.24 |
|---|---|
| [Spring boot] @Scheduled (0) | 2026.06.23 |
| [Spring boot] IntelliJ Thymeleaf Live Reloading (0) | 2026.04.12 |
| [Spring boot] Kakao OAuth 소셜로그인 수동 구현 (0) | 2026.04.01 |
| [Spring boot] Null과 Builder (0) | 2026.04.01 |
