[Inflearn] 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
화면 컨트롤러와 API를 위한 컨트롤러를 패키지를 나눠 분리
VIEW 컨트롤러는 @Controller, API 컨트롤러는 @RestController
RequestBody와 ResponseBody에 엔티티를 직접 담는 것은 좋지 않다.
엔티티에 프레젠테이션을 위한 로직이 추가되어 API 스펙에도 혼란을 줄 수 있고 유지보수가 어려워진다. 따라서 별도의 Request 객체나 DTO를 만들어 사용하는 것이 좋다.
OrderApiController
List<Order> all = orderRepository.findAll();
return all;
문제 1) 순환참조 문제 2021.04.14 - [study/spring boot & jpa] - [ERROR] 순환참조 문제
문제 2) 연관관계가 모두 지연로딩으로 설정되어 있어 Member와 Address는 프록시 객체
jackson 라이브러리는 기본적으로 이 프록시 객체를 json으로 어떻게 생성해야 몰라 예외가 발생하게 됨
해결 2.1) Hibernate5Module를 빈으로 등록하여 해결
buil.gradle
// Hibernate5Module
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
빈으로 등록
@Bean
Hibernate5Module hibernate5Module() {
Hibernate5Module hibernate5Module = new Hibernate5Module();
hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true);
return hibernate5Module;
}
기본적으로 초기화된 프록시 객체만 노출되고 초기화되지 않은 프록시 객체는 노출되지 않음
해결 2.2) for문을 통해 LAZY 강제 초기화
List<Order> all = orderRepository.findAll();
// Lazy 강제 초기화
for (Order order : all) {
order.getMember().getName();
order.getDelivery().getAddress();
}
문제 3) 엔티티 직접 노출에 의한 문제 - api 스펙 관련 이슈와 성능 문제
OrderApiController
List<Order> orders = orderRepository.findAll();
// entity -> DTO
List<SimpleOrderDto> result = orders.stream()
.map(o -> new SimpleOrderDto(o))
.collect(toList());
return result;
N + 1 문제로 인한 성능 저하
지연로딩으로 인해 연관관계에 따라 예상치 못한 쿼리가 추가로 발생하는 문제
예) Order 조회 1번 + Order 조회 결과마다 Member 조회 N번 + Order 조회 결과마다 Address 조회 N번
OrderRepository
public List<Order> findAllV3() {
return em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class
).getResultList();
}
페치 조인을 이용하여 쿼리 하나로 해결
# 2021-04-21 00:38:05.299 DEBUG 24340 --- [nio-8080-exec-6] org.hibernate.SQL :
select
order0_.order_id as order_id1_6_0_,
member1_.member_id as member_i1_4_1_,
delivery2_.delivery_id as delivery1_2_2_,
order0_.delivery_id as delivery4_6_0_,
order0_.member_id as member_i5_6_0_,
order0_.order_date as order_da2_6_0_,
order0_.status as status3_6_0_,
member1_.city as city2_4_1_,
member1_.street as street3_4_1_,
member1_.zipcode as zipcode4_4_1_,
member1_.name as name5_4_1_,
delivery2_.city as city2_2_2_,
delivery2_.street as street3_2_2_,
delivery2_.zipcode as zipcode4_2_2_,
delivery2_.status as status5_2_2_
from
orders order0_
inner join
member member1_
on order0_.member_id=member1_.member_id
inner join
delivery delivery2_
on order0_.delivery_id=delivery2_.delivery_id
select절에 모든 컬럼이 포함됨 → 중복 데이터가 많아 데이터 전송 시간과 용량 증가
OrderRepository
public List<OrderSimpleQueryDto> findAllV4() {
return em.createQuery(
"select new jpabook.jpashop.repository.OrderSimpleQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" +
" from Order o" +
" join o.member m" +
" join o.delivery d", OrderSimpleQueryDto.class).getResultList();
}
select절에 원하는 컬럼만 포함하여 성능이 좀 더 최적화됨 (QueryDSL 이용시 더 간편하게 작성할 수 있음)
# 2021-04-21 00:42:16.662 DEBUG 24340 --- [nio-8080-exec-9] org.hibernate.SQL :
select
order0_.order_id as col_0_0_,
member1_.name as col_1_0_,
order0_.order_date as col_2_0_,
order0_.status as col_3_0_,
delivery2_.city as col_4_0_,
delivery2_.street as col_4_1_,
delivery2_.zipcode as col_4_2_
from
orders order0_
inner join
member member1_
on order0_.member_id=member1_.member_id
inner join
delivery delivery2_
on order0_.delivery_id=delivery2_.delivery_id
api 스펙에 맞춘 메서드로써 재사용성이 떨어짐
v3과 v4의 성능이 크게 차이나지 않음
[SPRING] JPA 활용2 | OSIV와 성능 최적화 (0) | 2021.04.21 |
---|---|
[SPRING] JPA 활용2 | API 개발과 성능 최적화(2) (0) | 2021.04.21 |
[SPRING] JPA 활용1 | 웹 애플리케이션 개발 (0) | 2021.04.16 |
[SPRING] JPA 프로그래밍 기본 | 객체지향 쿼리 언어 (0) | 2021.04.08 |
[SPRING] JPA 프로그래밍 기본 | 값 타입 (0) | 2021.04.08 |
댓글 영역