๊ด€๋ฆฌ ๋ฉ”๋‰ด

Tech Log ๐Ÿ› ๏ธ

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

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์„ ์ง์ ‘ ์‚ฌ์šฉํ•œ๋‹ค. (์ดคํ›„์˜ ๋ฐฉ๋ฒ•)