[4] 스프링 부트와 JPA 활용 (8) - API 개발 고급 1 (지연로딩 & 조회 성능 최적화)
Intro
연관된 객체
가 컬렉션
이 아닌 경우
최적화 하는 과정
orders
에 있는 모든 주문을 조회하는 api
를 만드는 과정
order
에는 주문한 member
와 delivery
객체가 참조
되어 있음
Version
V1
: Entity 직접 노출
V2
: DTO사용
V3
: DTO
+ fetch join
V4
: DTO
+ fetch join
+ JPQL DTO적용
V1 (Entity 반환)
(API 로직)
- 문제점
jackson
의 프록시 객체
인식 문제
: 기본적으로 LAZY를 통한 지연로딩 설정
을 가지고 있어서 order
에 있는 member
와 address
는 프록시 객체
를 가리키고 있다
(프록시 객체 초기화
가 이루어지지 않았기 때문
)
이러한 상황에서 jackson 라이브러리
에 의해 json화 되는 과정
에서 프록시 객체
를 인식하지 못하기 때문
에 예외가 발생
& 무한루프
에 빠짐
- 해결 방법
Hibernate5Module
+ @JsonIgnore
: 초기화된 프록시 객체
만 노출되게 하며, 양방향 연관관계 무한루프를 해결
(실무에 적합하지 않은 방법
이니 존재만 확인
할 것)
DTO 사용
: 반환하는 객체
에 맞는 DTO
를 생성
해서 사용 --> 실무에 적합!
V2 (DTO 사용)
(API 로직)
- 문제점
N+1 문제
발생
: 반환 DTO
에 데이터를 생성하는 과정
에서(생성자)
map
에 따라서 차례로 관련 데이터를 가져오는 쿼리가 추가
로 발생된다 --> N+1 문제
발생
(DTO)
생성자
를 통해 name
과 address
값을 지정하는 과정
에서 LAZY로딩 수행
됨
--> 반복문 순회
할 때 마다 쿼리가 생김
--> N+1 문제
발생
V3 (DTO + fetch join)
(API 로직)
- 해결 방법
- 필요한 데이터만 받기
--> JPQL을 직접 받는 DTO 설정
- 주의
JPQL 직접 받는 DTO설정
은 생각보다 효과가 미비
함
DTO
+ fetch join
이면 대부분의 최적화 문제는 해결
된다
JPQL에 직접 DTO설정
을 하는 것은 선택적
으로 가져가자
(설정하기 조금 귀찮기 때문 - V4에서 설명함
)
(fetch join 코드 추가 - repository)
fetch join
을 사용해서 연관 객체들의 정보
를 모두 가져와 영속성 컨텍스트
에서 관리
--> 추가적으로 쿼리가 나가는 것
을 방지
함
V4 (DTO + fetch join + JPQL DTO)
(API 로직)
OrderSimpleQueryDto
라는 DTO를 사용
orderRepository,findOrderDtos
: JPQL에 DTO를 직접 설정
해서 원하는 필드
만 받게하는 것
(DTO)
- 반드시 생성자에
객체가 아닌
필드를 직접 받아서 매핑
해줘야 함
(조금 번거로움)
(JPQL 튜닝 - repository)
new 패키지명.dto(필드 나열)
을 통해서 직접 받는 DTO 설정
- 결과적으로
성능
은 V4가 제일 좋음
정리
Entity를 직접 반환
하지 말고
반드시 DTO를 생성해서 반환
프록시 객체
문제 해결
json 무한루프
문제 해결
지연로딩
으로 발생하는 N+1 문제
는 fetch join
으로 해결
JPQL에 값을 직접 받는 DTO설정
은 선택적
으로 사용
- 생각보다
성능 개선
이 미비
API스펙에 맞춘 코드
가 repository
에 들어가는 단점
이 있음
필드를 직접 나열
해야하는 불편함
이 있음
- 결과
: V3
를 기본적
으로 가져가며 V4설정
은 선택적
으로 사용