๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ–ฅ๏ธ ๋ฐฑ์—”๋“œ/SpringBoot

[API] API ๊ณ ๊ธ‰ ์„ค๊ณ„ - ์ง€์—ฐ ๋กœ๋”ฉ๊ณผ ์กฐํšŒ ์„ฑ๋Šฅ ์ตœ์ ํ™”

by OR15A 2023. 11. 28.

โŒ ์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ๋…ธ์ถœ

    @GetMapping("/api/v1/orders")
    public List<Order> ordersV1() {
        List<Order> all = orderRepository.findAll();
        for (Order order : all) {
            order.getMember().getName(); //Lazy ๊ฐ•์ œ ์ดˆ๊ธฐํ™”
            order.getDelivery().getAddress(); //Lazy ๊ฐ•์ œ ์ดˆ๊ธฐํ™”
            List<OrderItem> orderItems = order.getOrderItems();
            orderItems.stream().forEach(o -> o.getItem().getName()); //Lazy ๊ฐ•์ œ ์ดˆ๊ธฐํ™”
        }
        return all;
    }
  • order member ์™€ order address ๋Š” ์ง€์—ฐ ๋กœ๋”ฉ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ ๋Œ€์‹ ์— ํ”„๋ก์‹œ ์กด์žฌ
  • jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ json์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ชจ๋ฆ„ ์˜ˆ์™ธ ๋ฐœ์ƒ
  • [ํ•ด๊ฒฐ๋ฐฉ์•ˆ]
  • Hibernate5Module ๋ชจ๋“ˆ(์Šคํ”„๋ง ๋นˆ) ๋“ฑ๋ก, LAZY=null ์ฒ˜๋ฆฌ
  • ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„ ๋ฌธ์ œ ๋ฐœ์ƒ -> @JsonIgnore๋กœ ํ•ด๊ฒฐ
[ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๋ชจ๋“ˆ ๋“ฑ๋ก]
์Šคํ”„๋ง ๋ถ€ํŠธ ๋ฒ„์ „์— ๋”ฐ๋ผ์„œ ๋ชจ๋“ˆ ๋“ฑ๋ก ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅด๋‹ค.
์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ๋ถ€ํ„ฐ๋Š” javax -> jakarta ๋กœ ๋ณ€๊ฒฝ ๋˜์–ด์„œ ์ง€์› ๋ชจ๋“ˆ๋„ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.
์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ๋ฏธ๋งŒ: Hibernate5Module ๋“ฑ๋ก build.gradle ์— ๋‹ค์Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ž
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5' JpashopApplication ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์ž ...(๋”๋ณด๊ธฐ) P.15
 

โŒ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ด์„œ DTO๋กœ ๋ณ€ํ™˜(fetch join ์‚ฌ์šฉX)

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

        return result;
    }
    • ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์ด๋‹ค.
    • ์ฟผ๋ฆฌ๊ฐ€ ์ด 1 + N + N๋ฒˆ ์‹คํ–‰๋œ๋‹ค. (v1๊ณผ ์ฟผ๋ฆฌ์ˆ˜ ๊ฒฐ๊ณผ๋Š” ๊ฐ™๋‹ค.)
      • order ์กฐํšŒ 1๋ฒˆ(order ์กฐํšŒ ๊ฒฐ๊ณผ ์ˆ˜๊ฐ€ N์ด ๋œ๋‹ค.)
      • order -> member ์ง€์—ฐ ๋กœ๋”ฉ ์กฐํšŒ N ๋ฒˆ
      • order -> delivery ์ง€์—ฐ ๋กœ๋”ฉ ์กฐํšŒ N ๋ฒˆ
      • ์˜ˆ) order์˜ ๊ฒฐ๊ณผ๊ฐ€ 4๊ฐœ๋ฉด ์ตœ์•…์˜ ๊ฒฝ์šฐ 1 + 4 + 4๋ฒˆ ์‹คํ–‰๋œ๋‹ค.(์ตœ์•…์˜ ๊ฒฝ์šฐ)
        • ์ง€์—ฐ๋กœ๋”ฉ์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ์กฐํšŒํ•˜๋ฏ€๋กœ, ์ด๋ฏธ ์กฐํšŒ๋œ ๊ฒฝ์šฐ ์ฟผ๋ฆฌ๋ฅผ ์ƒ๋žตํ•œ๋‹ค.
//OrderSimpleApiController - ์ถ”๊ฐ€
@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();
 	}*/
    
     public SimpleOrderDto(Long orderId, String name, LocalDateTime orderDate, 
     						OrderStatus orderStatus, Address address) {
         this.orderId = orderId;
         this.name = name;
         this.orderDate = orderDate;
         this.orderStatus = orderStatus; 
         this.address = address;
     }
}

 

โœ…  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ด์„œ DTO๋กœ ๋ณ€ํ™˜(fetch join ์‚ฌ์šฉO)

    @GetMapping("/api/v3/orders")
    public List<OrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithItem();
        List<OrderDto> result = orders.stream()
                .map(o -> new OrderDto(o))
                .collect(toList());

        return result;
    }
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŽ˜์น˜ ์กฐ์ธ(fetch join)์„ ์‚ฌ์šฉํ•ด์„œ ์ฟผ๋ฆฌ 1๋ฒˆ์— ์กฐํšŒ
  • ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ order -> member , order -> delivery ๋Š” ์ด๋ฏธ ์กฐํšŒ ๋œ ์ƒํƒœ ์ด๋ฏ€๋กœ ์ง€์—ฐ๋กœ๋”ฉX
  • ์‹ค๋ฌด์—์„œ ๋ฌด์กฐ๊ฑด fetch์กฐ์ธ ์‚ฌ์šฉํ•˜๊ธฐ
  • ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์„ ์—ฐ๊ฒฐํ•œ ๊ธด ๋กœ์šฐ๋ฅผ ์ฐพ๊ฒŒ๋˜๋Š”๋ฐ JPA๊ฐ€ ์ ์ ˆํžˆ ์ž˜๋ผ์คŒ(๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ์ด๋ถ€๋ถ„ ์ตœ์ ํ™”)
//OrderRepository - ์ถ”๊ฐ€ ์ฝ”๋“œ
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();
}
 

 

 

โœ… JPA์—์„œ DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒ

    @GetMapping("/api/v4/orders")
    public List<OrderQueryDto> ordersV4() {
        return orderQueryRepository.findOrderQueryDtos();
    }
  • ์›ํ•˜๋Š” ๊ฒƒ๋งŒ selectํ•ด์˜ด
  • ์ผ๋ฐ˜์ ์ธ SQL์„ ์‚ฌ์šฉํ•  ๋•Œ ์ฒ˜๋Ÿผ ์›ํ•˜๋Š” ๊ฐ’์„ ์„ ํƒํ•ด์„œ ์กฐํšŒ
  • new ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ JPQL์˜ ๊ฒฐ๊ณผ๋ฅผ DTO๋กœ ์ฆ‰์‹œ ๋ณ€ํ™˜
  • SELECT ์ ˆ์—์„œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์„ ํƒํ•˜๋ฏ€๋กœ DB ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋„คํŠธ์› ์šฉ๋Ÿ‰ ์ตœ์ ํ™”(์ƒ๊ฐ๋ณด๋‹ค ๋ฏธ
    ๋น„)
  • ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์žฌ์‚ฌ์šฉ์„ฑ ๋–จ์–ด์ง, API ์ŠคํŽ™์— ๋งž์ถ˜ ์ฝ”๋“œ๊ฐ€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋“ค์–ด๊ฐ€๋Š” ๋‹จ์ 
//OrderSimpleQueryRepository ์กฐํšŒ ์ „์šฉ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ

@Repository
@RequiredArgsConstructor
public class OrderSimpleQueryRepository {
     private final EntityManager em;
     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();
     }
}

 

 

V3์™€ V4๋Š” ์šฐ์œ„๋ฅผ ๊ฐ€๋ฆด ์ˆ˜ ์—†์Œ.
V4๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ค์ง€๋งŒ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋–จ์–ด์ง
V3๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์จ์„œ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ๊ฐ€๋ŠฅV4๋Š” DTO์กฐํšŒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ(์—”ํ‹ฐํ‹ฐ ์•„๋‹ˆ๋ฏ€๋กœ)

์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜, DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒํ•˜๋Š” ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๊ฐ๊ฐ ์žฅ๋‹จ์ ์ด ์žˆ๋‹ค. ๋‘˜์ค‘ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์„œ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค. ์—”ํ‹ฐํ‹ฐ๋กœ ์กฐํšŒํ•˜๋ฉด ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์žฌ์‚ฌ์šฉ์„ฑ๋„ ์ข‹๊ณ , ๊ฐœ๋ฐœ๋„ ๋‹จ์ˆœํ•ด์ง„๋‹ค. ๋”ฐ๋ผ ์„œ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

์ฟผ๋ฆฌ ๋ฐฉ์‹ ์„ ํƒ ๊ถŒ์žฅ ์ˆœ์„œ
1. ์šฐ์„  ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•œ๋‹ค.
2. ํ•„์š”ํ•˜๋ฉด ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™” ํ•œ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ํ•ด๊ฒฐ๋œ๋‹ค.
3. ๊ทธ๋ž˜๋„ ์•ˆ๋˜๋ฉด DTO๋กœ ์ง์ ‘ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.
4. ์ตœํ›„์˜ ๋ฐฉ๋ฒ•์€ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ SQL์ด๋‚˜ ์Šคํ”„๋ง JDBC Template์„ ์‚ฌ์šฉํ•ด์„œ SQL์„ ์ง์ ‘ ์‚ฌ ์šฉํ•œ๋‹ค.