jpa

[jpa] μ§€μ—°λ‘œλ”©κ³Ό 쑰회 μ„±λŠ₯ μ΅œμ ν™”

sehaan 2023. 2. 25. 02:32

 

이전 κ°•μ˜ λ‚΄μš© 

 

μ—”ν‹°ν‹°λ₯Ό Request Body 에 직접 λ§€ν•‘ν•  경우 문제점

- 엔티티에 ν”„λ ˆμ  ν…Œμ΄μ…˜ , API 검증을 μœ„ν•œ 둜직이 μΆ”κ°€λœλ‹€. 

  ν•˜μ§€λ§Œ μ‹€λ¬΄μ—μ„œλŠ” μ—”ν‹°ν‹°λ₯Ό μ΄μš©ν•œ μ—¬λŸ¬ API듀이 λ§Œλ“€μ–΄μ§€λŠ”λ° , 각각의 API듀을 μœ„ν•œ λͺ¨λ“  μš”μ²­μ‚¬ν•­λ“€μ„ λ‹΄κΈ°λŠ” μ–΄λ ΅λ‹€.

  λ˜ν•œ μ—”ν‹°ν‹°κ°€ λ³€κ²½λ˜λ©΄ API μŠ€νŽ™μ΄ λ³€ν•œλ‹€ !

 

- λ”°λΌμ„œ μ—”ν‹°ν‹°λ₯Ό Requst Body 에 직접 λ§€ν•‘μ‹œν‚€λŠ” 것이 μ•„λ‹Œ , API λ₯Ό μœ„ν•œ DTOλ₯Ό λ”°λ‘œ λ§Œλ“€μ–΄μ„œ 이것을 νŒŒλΌλ―Έν„°λ‘œ λ°›μ•„μ•Όν•œλ‹€.

 

- DTOλ₯Ό ν†΅ν•΄μ„œ νŒŒλΌλ―Έν„°λ₯Ό λ°›μœΌλ©΄ λ‹€μŒκ³Ό 같은 μž₯점이 μžˆλ‹€.

  1. 엔티티와 ν”„λ ˆμ  ν…Œμ΄μ…˜μ„ μœ„ν•œ λ‘œμ§μ„ 뢄리할 수 μžˆλ‹€.

       - 이제 더 이상 μ—”ν‹°ν‹°κ°€ μ—¬λŸ¬ API듀을 μœ„ν•œ μš”μ²­μ‚¬ν•­λ“€μ„ 담을 ν•„μš”κ°€ μ—†λ‹€. 

  2. 엔티티와 API μŠ€νŽ™μ„ λͺ…ν™•ν•˜κ²Œ 뢄리할 수 μžˆλ‹€.

       - μ—”ν‹°ν‹°κ°€ λ³€κ²½λ˜λ„ API μŠ€νŽ™μ΄ λ³€ν•˜μ§€ μ•ŠλŠ”λ‹€.

 

μ§€μ—°λ‘œλ”©κ³Ό μ‘°νšŒ μ„±λŠ₯ μ΅œμ ν™”


@GetMapping("/api/v1/simple-orders")
 public List<Order> ordersV1() {
 List<Order> all = orderRepository.findAllByString(new OrderSearch());
 for (Order order : all) {
 order.getMember().getName(); //Lazy κ°•μ œ μ΄ˆκΈ°ν™”
 order.getDelivery().getAddress(); //Lazy κ°•μ œ μ΄ˆκΈ°ν™˜
 }
 return all;
 }

APIλ₯Ό κ°œλ°œν•  λ•Œ μœ„μ˜ μ½”λ“œλ₯Ό μ‚¬μš©ν•œλ‹€κ³  μƒκ°ν•΄λ³΄μž !

μœ„μ˜ μ½”λ“œλŠ” 두 κ°€μ§€μ˜ λ¬Έμ œκ°€ μžˆλ‹€.

1. μ—”ν‹°ν‹°λ₯Ό 직접 λ…ΈμΆœν•œλ‹€.

2. ν”„λ‘μ‹œ 객체λ₯Ό μ‘°νšŒν•˜λ―€λ‘œ μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.

 

μ—”ν‹°ν‹°λ₯Ό 직접 λ…ΈμΆœν•˜λŠ” κ±°λŠ” κ·Έλ ‡λ‹€μΉ˜κ³  λ¨Όμ € μ˜ˆμ™Έλ₯Ό ν•΄κ²°ν•΄μ•Όν•œλ‹€.

 

 

μœ„ μ½”λ“œκ°€ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λŠ” μ΄μœ λŠ” μ—”ν‹°ν‹°λ₯Ό λΆˆλŸ¬μ˜€λŠ” 방식이 fetch.LAZY 인데,

JACKSON λΌμ΄λΈŒλŸ¬λ¦¬κ°€ ν”„λ‘μ‹œ 객체λ₯Ό μ–΄λ–»κ²Œ json으둜 생성할지 λͺ¨λ₯΄κΈ° λ•Œλ¬Έμ΄λ‹€.

 

이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„  ν•˜μ΄λ²„λ„€μ΄νŠΈ λͺ¨λ“ˆμ„ μ„€μΉ˜ν•¨μœΌλ‘œμ¨ ν•΄κ²°ν•  수 μžˆλ‹€. 

μ΄λ ‡κ²Œ ν•¨μœΌλ‘œμ¨ μ΄ˆκΈ°ν™”λœ ν”„λ‘μ‹œ 객체만 λ…ΈμΆœν•˜κ³  μ΄ˆκΈ°ν™”λ˜μ§€ μ•ŠλŠ” ν”„λ‘μ‹œ κ°μ²΄λŠ” λ…ΈμΆœμ‹œν‚€μ§€ μ•ŠλŠ”λ‹€.(null둜 λ‚˜μ˜΄)

 

근데 μ—”ν‹°ν‹°λ₯Ό 직접 λ…ΈμΆœμ‹œν‚€λŠ” 것은 맀우 μ•ˆμ’‹κΈ° λ•Œλ¬Έμ— μ• μ΄ˆμ— μ΄λ ‡κ²Œ ν•˜λ©΄ μ•ˆλœλ‹€ !

차라리 DTOλ₯Ό λ°˜ν™˜μ‹œν‚€μž

 

μ£Όμ˜μ‚¬ν•­

1. μ—”ν‹°ν‹°λ₯Ό 직접 λ…ΈμΆœν•  λ•Œ μ–‘λ°©ν–₯ 연관관계가 κ±Έλ¦° 곳은 κΌ­ @JsonIgnore 처리λ₯Ό ν•΄μ•Όν•œλ‹€. (μ•ˆν•˜λ©΄ λ¬΄ν•œλ£¨ν”„ κ±Έλ¦Ό)

2. μ§€μ—° λ‘œλ”© 문제λ₯Ό ν•΄κ²°ν•œλ‹€κ³  EAGER 둜 λ°”κΎΈλ©΄ 더 κ³¨μΉ˜μ•„νŒŒ μ§„λ‹€.

 

μ—”ν‹°ν‹°λ₯Ό DTO둜 λ°˜ν™˜

@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2() {
 List<Order> orders = orderRepository.findAll();
 List<SimpleOrderDto> result = orders.stream()
 .map(o -> new SimpleOrderDto(o))
 .collect(toList());
 return result;
}

@Data
static class SimpleOrderDto {

 private Long orderId;
 private String name;
 private LocalDateTime orderDate; //μ£Όλ¬Έμ‹œκ°„
 private OrderStatus orderStatus;
 private Address address;
 public SimpleOrderDto(Order order) {
 orderId = order.getId();
 name = order.getMember().getName();
 orderDate = order.getOrderDate();
 orderStatus = order.getStatus();
 address = order.getDelivery().getAddress();
 }
 
}

 

이제 μ—”ν‹°ν‹°λ₯Ό 직접 λ…ΈμΆœμ‹œν‚€λŠ” 게 μ•„λ‹Œ DTOλ₯Ό λ°˜ν™˜μ‹œν‚€λŠ” λ°©μ‹μœΌλ‘œ μ½”λ“œλ₯Ό μˆ˜μ •ν–ˆλ‹€.

ν•˜μ§€λ§Œ 이 μ½”λ“œλŠ” 1+N 문제λ₯Ό μΌμœΌν‚¨λ‹€.

λ‘œλ”©λ°©μ‹μ΄ fetch.LAZYμ΄λ―€λ‘œ , orderμ—μ„œ memberλ₯Ό μ‘°νšŒν• λ•Œ N번

                                                orderμ—μ„œ Deliveryλ₯Ό μ‘°νšŒν• λ•Œ N번 μ‘°νšŒλœλ‹€.(1+N+N)

 

*μ§€μ—°λ‘œλ”©μ€ 연속성 μ»¨ν…μŠ€νŠΈμ—μ„œ μ‘°νšŒλ˜λ―€λ‘œ , 이미 쑰회된 경우 쿼리 μƒλž΅ 

 

페치 쑰인 μ΅œμ ν™”

public List<Order> findAllWithMemberDelivery() {
 return em.createQuery(
 "select o from Order o" +
 " join fetch o.member m" +
 " join fetch o.delivery d", Order.class)
 .getResultList();
}

1+N λ¬Έμ œλŠ” λ‹€μŒκ³Ό 같이 페치 쑰인을 μ‚¬μš©ν•΄μ„œ ν•΄κ²°ν•  수 μžˆλ‹€.

μ›λž˜λŠ” member 와  delivery λ₯Ό λ”°λ‘œ λ”°λ‘œ μ‘°νšŒν–ˆμ§€λ§Œ μ΄μ œλŠ” ν•œλ²ˆμ— μ‘°νšŒν•¨μœΌλ‘œμ¨ 쑰회 μ„±λŠ₯을 높일 수 μžˆλ‹€.

(μ§€μ—°λ‘œλ”©μ΄ λ˜μ§€ μ•ŠλŠ”λ‹€)

 

*페치쑰인(fetch join)μ΄λž€? 

  - jpqlμ—μ„œ μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μœ„ν•΄ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯ 

    μ—°κ΄€λœ μ—”ν‹°ν‹°λ‚˜ μ»¬λ ‰μ…˜μ„ ν•œλ²ˆμ— 같이 μ‘°νšŒν•  수 μžˆλ‹€.

 

λ¦¬ν¬μ§€ν† λ¦¬μ—μ„œ DTO 직접 쑰회

public List<OrderSimpleQueryDto> findOrderDtos() {
 return em.createQuery(
 "select new
jpabook.jpashop.repository.order.simplequery.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();
 }

일반적인 SQL문을 μ‚¬μš©ν•˜λŠ” κ²ƒμ²˜λŸΌ μ›ν•˜λŠ” 값을 μ„ νƒν•΄μ„œ μ‘°νšŒν•˜λŠ” 방법이닀.

select μ ˆμ—μ„œ μ›ν•˜λŠ” 데이터λ₯Ό 직접 μ„ νƒν•˜κΈ° λ•Œλ¬Έμ— λ„€νŠΈμ›Œν¬ μš©λŸ‰μ„ 'κ·Όμ†Œν•˜κ²Œ' μ΅œμ ν™” ν•  수 μžˆλ‹€.

ν•˜μ§€λ§Œ κ²°κ΅­ API μŠ€νŽ™μ— 맞좘 μ½”λ“œκ°€ 리포지토리에 λ“€μ–΄κ°„λ‹€λŠ” 점과 νŠΉμ • API λ§Œμ„ μœ„ν•œ κ°’λ§Œμ„ λ°˜ν™˜ν•΄μ€˜μ„œ 리포지토리 μž¬μ‚¬μš©μ΄ λ–¨μ–΄μ§€λŠ” λ‹€λŠ” 점을 κ°μ•ˆν•΄λ³΄λ©΄, 페치 쑰인보닀 κ²°μ½” μ’‹λ‹€κ³  ν•  μˆ˜λŠ” μ—†λŠ” 방식이닀.

 

λ§Œμ•½ 이와 같은 APIλ₯Ό λ§Œλ“€μ–΄μ•Όν•œλ‹€λ©΄ λ”°λ‘œ νŒ¨ν‚€μ§€λ₯Ό λ§Œλ“€μ–΄μ„œ κ΄€λ¦¬ν•΄μ£ΌλŠ” 것이 μ’‹λ‹€.

(DTOλ₯Ό 직접 μ‘°νšŒν•˜λŠ” 것은 화면을 μœ„ν•œ 둜직이기 λ•Œλ¬Έ)

 

μ •λ¦¬ν•˜μžλ©΄,

λ¨Όμ € μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•˜λŠ” 방식을 μ‚¬μš©ν•˜κ³  페치쑰인으둜 μ΅œμ ν™”λ₯Ό ν•œλ‹€.

μ—¬κΈ°μ„œ λŒ€λΆ€λΆ„ λ¬Έμ œκ°€ ν•΄κ²°λ˜μ§€λ§Œ μΆ”κ°€λ‘œ μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μ§„ν–‰ν•΄μ•Ό ν•œλ‹€λ©΄ DTOλ₯Ό 직접 μ‘°νšŒν•˜λŠ” 방법을 μ„ νƒν•œλ‹€.

 

μ΄λž˜λ„ μ•ˆλœλ‹€λ©΄ λ„€μ΄ν‹°λΈŒ SQLμ΄λ‚˜ JDBC ν…œν”Œλ¦Ώμ„ μ΄μš©ν•΄μ„œ SQL을 직접 μ‚¬μš©ν•œλ‹€. (μ΄€ν›„μ˜ 방법)