-
[๊ฐ์] ์ค์ ! ์คํ๋ง ๋ถํธ์ JPA ํ์ฉ2 - API ๊ฐ๋ฐ๊ณผ ์ฑ๋ฅ ์ต์ ํ 2SPRING/INFLEARN 2022. 8. 2. 03:38
์ค์ ! ์คํ๋ง ๋ถํธ์ JPA ํ์ฉ2 - API ๊ฐ๋ฐ๊ณผ ์ฑ๋ฅ ์ต์ ํ - ์ธํ๋ฐ | ๊ฐ์
์คํ๋ง ๋ถํธ์ JPA๋ฅผ ํ์ฉํด์ API๋ฅผ ๊ฐ๋ฐํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ JPA ๊ทนํ์ ์ฑ๋ฅ ์ต์ ํ ๋ฐฉ๋ฒ์ ํ์ตํ ์ ์์ต๋๋ค., - ๊ฐ์ ์๊ฐ | ์ธํ๋ฐ...
www.inflearn.com
4. API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ปฌ๋ ์ ์กฐํ ์ต์ ํ
- ์ฃผ๋ฌธ ์กฐํ V1: ์ํฐํฐ ์ง์ ๋ ธ์ถ
@RestController @RequiredArgsConstructor public class OrderApiController { private final OrderRepository orderRepository; @GetMapping("/api/v1/orders") public List<Order> ordersV1(){ List<Order> all = orderRepository.findAllByString(new OrderSearch()); for (Order order : all) { order.getMember().getName(); order.getDelivery().getAddress(); List<OrderItem> orderItems = order.getOrderItems(); orderItems.forEach(o -> o.getItem().getName()); } return all; } }
์ ๋ถ lazy๊ธฐ ๋๋ฌธ์ ํ๋ก์๋ฅผ ๊ฐ์ ์ด๊ธฐํ ํ ํ ๋ฐ์ดํฐ๋ฅผ ๋ฟ๋ฆฌ๊ฒ ํจ
๊ฒฐ๊ตญ ์ํฐํฐ ์ง์ ๋ ธ์ถ์ด๋ผ ๋ณ๋ก
- ์ฃผ๋ฌธ ์กฐํ V2: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ
@RestController @RequiredArgsConstructor public class OrderApiController { /*์๋ต*/ @GetMapping("/api/v2/orders") public List<OrderDto> orderV2(){ List<Order> orders = orderRepository.findAllByString(new OrderSearch()); List<OrderDto> collect = orders.stream() .map(o-> new OrderDto(o)) .collect(Collectors.toList()); return collect; } //@Data ๊ฐ ๋๋ฌด ๋ง์ผ๋ฉด ์ ๋งคํด์ ์์ฐ๋ ๊ฒ ๋์ ์๋ ์์ @Getter static class OrderDto { private Long orderId; private String name; private LocalDateTime orderDate; private OrderStatus orderStatus; private Address address; private List<OrderItemDto> orderItems; //๋์น๊ธฐ ์ํ ์ค์ : OrderItem๋ DTO๋ก ๋ฐ๊ฟ์ ๋ณด๋ด์ผํจ //private List<OrderItem> orderItems; public OrderDto(Order order){ orderId = order.getId(); name = order.getMember().getName(); orderDate = order.getOrderDate(); orderDate = order.getOrderDate(); orderStatus = order.getStatus(); address = order.getMember().getAddress(); /*//์ด๊ฑฐ ์์ฐ๋ฉด lazy๋ผ ์๋ฌด๊ฒ๋ ๋์ค์ง ์์ order.getOrderItems().stream().forEach(o -> o.getItem().getName()); orderItems = order.getOrderItems();*/ //-> OrderItem๋ Dto๋ก ๋ณ๊ฒฝ orderItems = order.getOrderItems().stream() .map(orderItem -> new OrderItemDto(orderItem)) .collect(Collectors.toList()); } } @Getter static class OrderItemDto{ private String itemName; private int orderPrice; private int count; public OrderItemDto(OrderItem orderItem){ itemName = orderItem.getItem().getName(); orderPrice = orderItem.getOrderPrice(); count = orderItem.getCount(); } } }
์ํ๋ ์ ๋ณด๋ง ๋ณด์ด๋ ๊ฒ์ ์ ์ ์๋ค.
์ง์ฐ ๋ก๋ฉ์ด ๋ง์์
Order ์กฐํ์ ๋๊ฐ๋์ด
๊ฐ Order๋ง๋ค
๋ฉค๋ฒ ํ ๋ช ์กฐํ
delivery ์ ๋ณด
Items ๋๊ฐ ์กฐํ
Items๋ง๋ค Item ์กฐํ์ 2๋ฒ์ฉ
...
SQL ์ฟผ๋ฆฌ๊ฐ ์กฐ์ง๊ฒ ๋ง์ด ๋๊ฐ
๋ฑ ๋ด๋ ์ฑ๋ฅ ๋ ๋จ์ด์ ธ ๋ณด์
- ์ฃผ๋ฌธ ์กฐํ V3: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ - ํจ์น ์กฐ์ธ ์ต์ ํ
@Repository @RequiredArgsConstructor public class OrderRepository { /*์๋ต*/ public List<Order> findAllWithItem() { return em.createQuery( "select o from Order o" + " join fetch o.member m" + " join fetch o.delivery d" + " join fetch o.orderItems oi" + " join fetch oi.item i", Order.class) .getResultList(); } }
DB์์ ์ผ๋๋ค์ผ๋ ๋ค์ ๋ง์ถฐ์ ๋ฆฌํดํ๊ธฐ ๋๋ฌธ์ order 4๊ฐ๊ฐ ๋ฆฌํด๋๋ค -> distinct๋ฅผ ์ถ๊ฐํ๋ฉด 2๊ฐ ๋ฆฌํด
์ฌ์ค ์ฟผ๋ฆฌ์์๋ ์์ ํ ๋๊ฐ์์ผ ์ค๋ณต์ ๊ฑฐ์ด๋ฏ๋ก, ์ฌ์ ํ 4๊ฐ๊ฐ ๋์ค์ง๋ง,
JPA์์ ์์ฒด์ ์ผ๋ก distinct๋ฅผ ๋ณด๋ฉด ์ค๋ณต์ ๊ฑธ๋ฌ์ ์ปฌ๋ ์ ์ ๋ด์์ค๋ค.distinct๊ฐ ์ค๋ณต ์กฐํ๋ฅผ ๋ง์์ค๋ค
@RestController @RequiredArgsConstructor public class OrderApiController { /*์๋ต*/ @GetMapping("/api/v3/orders") public List<OrderDto> orderV3(){ List<Order> orders = orderRepository.findAllWithItem(); List<OrderDto> collect = orders.stream() .map(o-> new OrderDto(o)) .collect(Collectors.toList()); return collect; /*์๋ต*/ } }
ํจ์น ์กฐ์ธ์ผ๋ก SQL์ด ํ ๋ฒ๋ฐ ์คํ๋จ
ํ์ง๋ง ๋จ์ , ์ผ๋๋ค๋ฅผ ํ๋ ์๊ฐ ํ์ด์ง์ด ๋ถ๊ฐ๋ฅํ๋ค
.setFirstResult(1), .setMaxResults(100) : 1๋ถํฐ 100๊ฐ ๊ฐ์ ธ์ด
WARN : firstResult/maxResults sepcified with collection fetch; applying in memory
Distinct๋ ๊ฒฐ๊ณผ ๊ธฐ์ค์ด ์๋, ๋ปฅํ๊ธฐ ๋ ์ํ, order item์ ๊ธฐ์ค์ผ๋ก ์ ์ฉ์ด ๋จ
์ ์ ํ ๊ฐ์๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ด ๋งค์ฐ ์ํํจ(ex) OutOfMemory), ํ์ด์ง์ด ๋ถ๊ฐ๋ฅํจ
๋ํ ์ปฌ๋ ์ ํจ์น ์กฐ์ธ์ ํ ๋ฒ๋ง ์ฌ์ฉํ ์ ์๋ค.
๋๊ฐ ์ด์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ๋ปฅํ๊ธฐ์ ๋ปฅํ๊ธฐ ํด์ ํผ๋์ ์ผ์ผํฌ ์ ์์
- ์ฃผ๋ฌธ ์กฐํ V3.1: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ - ํ์ด์ง๊ณผ ํ๊ณ ๋ํ
@Repository @RequiredArgsConstructor public class OrderRepository { public List<Order> findAllWithMemberDelivery(int offset, int limit) { return em.createQuery( "select o from Order o" + " join fetch o.member m" + " join fetch o.delivery d", Order.class) .setFirstResult(offset) .setMaxResults(limit) .getResultList(); }
@RestController @RequiredArgsConstructor public class OrderApiController { /*์๋ต*/ @GetMapping("/api/v3/orders") public List<OrderDto> orderV3(){ List<Order> orders = orderRepository.findAllWithItem(); List<OrderDto> collect = orders.stream() .map(o-> new OrderDto(o)) .collect(Collectors.toList()); return collect; } @GetMapping("/api/v3.1/orders") public List<OrderDto> orderV3_page( @RequestParam(value = "offset" , defaultValue = 0) int offset, @RequestParam(value = "limit" , defaultValue = 100) int limit){ //ToOne ๊ด๊ณ๊น์ง๋ง ๋ญํฑ์ด๋ก ํ๋ฒ์ ๊ฐ์ ธ์จ๋ค List<Order> orders = orderRepository.findAllWithMemberDelivery(offset, limit); // ์ฌ๊ธฐ๊น์ง๋ง ํ๋ฉด 1 + orders ๋ฃจํ (2๋ฒ + 1) * (orderItems 1 + orderitem 2 * item 2)... ๊ณ์ฐ ๋ค์์ ๋ง์ ์ผ๋จ ์ด๋ผ ๋ง์ List<OrderDto> collect = orders.stream() .map(o-> new OrderDto(o)) .collect(Collectors.toList()); return collect; } /*์๋ต*/ }
jpa.hibernate.properties.default_batch_fetch_size: 100 ์ถ๊ฐ
100์ ์๋ฏธ๋ inquery์ ๊ฐ์๋ฅผ ๋ช๊ฐ๋ก ํ ๊ฒ์ธ๊ฐ
ํ ๋ฐฉ์ 100๊ฐ๋ฅผ ๋ค ๋ก๊ฒจ์จ๋ค
์ฌ์ด์ฆ : ๊ณ ๊ณ ์ต์ ํ์ง๋ง ์๊ฐ ๋ถํ๋ฅผ ๊ฒฌ๋ ์ ์์ด์ผํจ ์ฃผ๋ก 100~1000 ์ฌ์ด
1 + N + N๋ฌธ์ ํด๊ฒฐ
๋ฐ์ดํฐ ์์ด ์์ฒญ ๋ง์์ง๋ฉด ์คํ๋ ค ํ๋ฒ์ ์ ๋ถ joinํด ๊ฐ์ ธ์ค๋ ์ ๋ฐฉ๋ฒ๋ณด๋ค ์ด ๋ฐฉ๋ฒ์ด ๋ ์ข์ ์ ๋ ์๋ค
๊ฒฐ๋ก ์ฅ์ :
์ฟผ๋ฆฌ ํธ์ถ ์๊ฐ 1 + N -> 1 + 1 ๋ก ์ต์ ํ
์กฐ์ธ๋ณด๋ค DB ๋ฐ์ดํฐ ์ ์ก๋์ด ์ต์ ํ , ์ค๋ณต๋ ๋ฐ์ดํฐ ์ ์ก ์ํด๋ ๋จํจ์น ์กฐ์ธ ๋ฐฉ์๊ณผ ๋น๊ตํด์ ์ฟผ๋ฆฌ ํธ์ถ ์๊ฐ ์ฝ๊ฐ ์ฆ๊ฐํ์ง๋ง, DB ๋ฐ์ดํฐ ์ ์ก๋์ ๊ฐ์
์ปฌ๋ ์ ํ์น ์กฐ์ธ์ ํ์ด์ง ๋ถ๊ฐ๋ฅ ํ์ง๋ง ์ด ๋ฐฉ๋ฒ์ ํ์ด์ง ๊ฐ๋ฅ- ์ฃผ๋ฌธ ์กฐํ V4: JPA์์ DTO ์ง์ ์กฐํ
repository ์๋ก ์์ฑ
@Repository @RequiredArgsConstructor public class OrderQueryRepository { private final EntityManager em; public List<OrderQueryDto> findOrderQueryDtos(){ //์ปฌ๋ ์ ์ ๋ฐ๋ก ๋ฃ์ ์๋ ์์ List<OrderQueryDto> result = findOrders(); //์ปฌ๋ ์ ์ ์ง์ ์ฑ์ด๋ค result.forEach(o ->{ List<OrderItemQueryDto> orderItems = findOrderItems(o.getOrderId()); o.setOrderItems(orderItems); }); return result; } private List<OrderItemQueryDto> findOrderItems(Long orderId){ return em.createQuery("select new jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name, oi.orderPrice,oi.count)" + " from OrderItem oi" + " join oi.item i" + " where oi.order.id = :orderId", OrderItemQueryDto.class) .setParameter("orderId", orderId) .getResultList(); } private List<OrderQueryDto> findOrders() { return em.createQuery("select new jpabook.jpashop.repository.order.query.OrderQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" + " from Order o" + " join o.member m" + " join o.delivery d", OrderQueryDto.class) .getResultList(); } }
@RestController @RequiredArgsConstructor public class OrderApiController { /*์๋ต*/ @GetMapping("/api/v4/orders") public List<OrderQueryDto> orderV4(){ return orderQueryRepository.findOrderQueryDtos(); } /*์๋ต*/ }
DTO ์์ฑ
@Data public class OrderQueryDto { private Long orderId; private String name; private LocalDateTime orderDate; private OrderStatus orderStatus; private Address address; private List<OrderItemQueryDto> orderItems; public OrderQueryDto(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; } }
@Data public class OrderItemQueryDto { @JsonIgnore private Long orderId; private String itemName; private int orderPrice; private int count; public OrderItemQueryDto(Long orderId, String itemName, int orderPrice, int count) { this.orderId = orderId; this.itemName = itemName; this.orderPrice = orderPrice; this.count = count; } }
๊ฒฐ๊ณผ๋ ๋ญ... ๋น์ท
row ์๊ฐ ์ฆ๊ฐํ์ง ์๋ ToOne ๊ด๊ณ๋ ์กฐ์ธ์ผ๋ก ์ต์ ํ ํ๊ธฐ ์ฌ์ฐ๋ฏ๋ก ํ๋ฒ์ ์กฐํํ๊ณ , ToMany
๊ด๊ณ๋ ์ต์ ํ ํ๊ธฐ ์ด๋ ค์ฐ๋ฏ๋ก findOrderItems() ๊ฐ์ ๋ณ๋์ ๋ฉ์๋๋ก ์กฐํํ๋ค.๋ณ๋์ ๋ฉ์๋๋ฅผ ์ฐ๋ ๊ณผ์ ์์ N+1๋ฒ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๋จ์ ์ด ์์
- ์ฃผ๋ฌธ ์กฐํ V5: JPA์์ DTO ์ง์ ์กฐํ - ์ปฌ๋ ์ ์กฐํ ์ต์ ํ
@RestController @RequiredArgsConstructor public class OrderApiController { /*์๋ต*/ @GetMapping("/api/v5/orders") public List<OrderQueryDto> orderV5(){ return orderQueryRepository.findAllByDto_optimization(); } /*์๋ต*/ }
@Repository @RequiredArgsConstructor public class OrderQueryRepository { public List<OrderQueryDto> findAllByDto_optimization(){ List<OrderQueryDto> result = findOrders(); List<Long> orderIds = result.stream().map(o -> o.getOrderId()) .collect(Collectors.toList()); List<OrderItemQueryDto> orderItems = em.createQuery("select new jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name, oi.orderPrice,oi.count)" + " from OrderItem oi" + " join oi.item i" + " where oi.order.id in :orderIds", OrderItemQueryDto.class) .setParameter("orderIds", orderIds) .getResultList(); //์ฌ์ฉํ๊ธฐ ํธํ๊ฒ ๋งต ํ์์ผ๋ก ๋ณ๊ฒฝ Map<Long, List<OrderItemQueryDto>> orderItemMap = orderItems.stream() .collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId())); result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId()))); return result; } }
์ฟผ๋ฆฌ ๋ ๋ฒ์ผ๋ก ์ต์ ํ ๊ฐ๋ฅ
๋ฃจํธ 1๋ฒ, ์ปฌ๋ ์ 1๋ฒ
Map์ ์ฌ์ฉํด์ ๋งค์นญ ์ฑ๋ฅ ํฅ์
- ์ฃผ๋ฌธ ์กฐํ V6: JPA์์ DTO ์ง์ ์กฐํ, ํ๋ซ ๋ฐ์ดํฐ ์ต์ ํ
์ฟผ๋ฆฌ ํ๋ฒ์ผ๋ก ํด๊ฒฐ ๊ฐ๋ฅ
@RestController @RequiredArgsConstructor public class OrderApiController { /*์๋ต*/ //์ฟผ๋ฆฌ๊ฐ ํ ๋ฒ๋ง ๋๊ฐ //ํ์ง๋ง join์ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ ๋ค์ ๋ง์ถฐ์ ๋ฟ๋ ค์ ธ ๋์ด //๋ฐ๋ผ์ ํ์ด์ง์ ๋ชปํจ //API ์คํ์ด ์๋ง๋ ๋ฌธ์ ๋ ๊ทธ๋ฅ ๋ ธ๊ฐ๋ค๋ก ๋ฐ๋ผ๋ด๋ ์ฝ๋๋ฅผ ์ง๋ฉด ๋จ @GetMapping("/api/v6/orders") public List<OrderFlatDto> orderV6(){ return orderQueryRepository.findAllByDto_flat(); } /*์๋ต*/ }
package jpabook.jpashop.repository.order.query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Repository @RequiredArgsConstructor public class OrderQueryRepository { /*์๋ต*/ public List<OrderFlatDto> findAllByDto_flat() { return em.createQuery( "select new jpabook.jpashop.repository.order.query.OrderFlatDto(o.id, m.name, o.orderDate, o.status, d.address, i.name, oi.orderPrice, oi.count)" + " from Order o" + " join o.member m" + " join o.delivery d" + " join o.orderItems oi" + " join oi.item i", OrderFlatDto.class) .getResultList(); } }
์ง์ ์ ๋ฆฌํ๋ ์ฝ๋(์ ๋ฆฌ ์๋์ด์์, ๊ทธ๋ฅ ๋ณต๋ถ)
๋๋ณด๊ธฐreturn flats.stream()
.collect(groupingBy(o -> new OrderQueryDto(o.getOrderId(),
o.getName(), o.getOrderDate(), o.getOrderStatus(), o.getAddress()),
mapping(o -> new OrderItemQueryDto(o.getOrderId(),
o.getItemName(), o.getOrderPrice(), o.getCount()), toList())
)).entrySet().stream()
.map(e -> new OrderQueryDto(e.getKey().getOrderId(),
e.getKey().getName(), e.getKey().getOrderDate(), e.getKey().getOrderStatus(),
e.getKey().getAddress(), e.getValue()))
.collect(toList());๊ทธ๋ฆฌ๊ณ OrderQueryDto์ @EqualsAndHashCode(of = orderId) ์ถ๊ฐ
orderId ๊ธฐ์ค์ผ๋ก ๋ฌถ์ด์ค
๋จ :
์ฟผ๋ฆฌ๋ ํ๋ฒ์ด์ง๋ง ์กฐ์ธ์ผ๋ก ์ธํด DB์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ฌํ๋ ๋ฐ์ดํฐ์ ์ค๋ณต ๋ฐ์ดํฐ๊ฐ ์ถ๊ฐ
-> V5 ๋ณด๋ค ๋ ๋๋ฆด ์ ๋
์ ํ๋ฆฌ์ผ์ด์ ์์ ์ถ๊ฐ ์์ ์ด ํผ
ํ์ด์ง ๋ถ๊ฐ๋ฅ- API ๊ฐ๋ฐ ๊ณ ๊ธ ์ ๋ฆฌ
์ํฐํฐ ์กฐํ
- ์ํฐํฐ๋ฅผ ์กฐํํด์ ๊ทธ๋๋ก ๋ฐํ: V1
- ์ํฐํฐ ์กฐํ ํ DTO๋ก ๋ณํ: V2
- ํ์น ์กฐ์ธ์ผ๋ก ์ฟผ๋ฆฌ ์ ์ต์ ํ: V3
- ์ปฌ๋ ์
ํ์ด์ง๊ณผ ํ๊ณ ๋ํ: V3.1
์ปฌ๋ ์ ์ ํ์น ์กฐ์ธ์ ํ์ด์ง์ด ๋ถ๊ฐ๋ฅ
ToOne ๊ด๊ณ๋ ํ์น ์กฐ์ธ์ผ๋ก ์ฟผ๋ฆฌ ์ ์ต์ ํ
์ปฌ๋ ์ ์ ํ์น ์กฐ์ธ ๋์ ์ ์ง์ฐ ๋ก๋ฉ์ ์ ์งํ๊ณ , hibernate.default_batch_fetch_size ,
@BatchSize ๋ก ์ต์ ํ\
DTO ์ง์ ์กฐํ
- JPA์์ DTO๋ฅผ ์ง์ ์กฐํ: V4
- ์ปฌ๋ ์ ์กฐํ ์ต์ ํ - ์ผ๋๋ค ๊ด๊ณ์ธ ์ปฌ๋ ์ ์ IN ์ ์ ํ์ฉํด์ ๋ฉ๋ชจ๋ฆฌ์ ๋ฏธ๋ฆฌ ์กฐํํด์ ์ต์ ํ: V5
- ํ๋ซ ๋ฐ์ดํฐ ์ต์ ํ - JOIN ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ์กฐํ ํ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ํ๋ ๋ชจ์์ผ๋ก ์ง์ ๋ณํ: V6
๊ถ์ฅ ์์
1. ์ํฐํฐ ์กฐํ ๋ฐฉ์์ผ๋ก ์ฐ์ ์ ๊ทผ
1. ํ์น์กฐ์ธ์ผ๋ก ์ฟผ๋ฆฌ ์๋ฅผ ์ต์ ํ
2. ์ปฌ๋ ์ ์ต์ ํ
1. ํ์ด์ง ํ์ hibernate.default_batch_fetch_size , @BatchSize ๋ก ์ต์ ํ
2. ํ์ด์ง ํ์X ํ์น ์กฐ์ธ ์ฌ์ฉ
2. ์ํฐํฐ ์กฐํ ๋ฐฉ์์ผ๋ก ํด๊ฒฐ์ด ์๋๋ฉด DTO ์กฐํ ๋ฐฉ์ ์ฌ์ฉ
3. DTO ์กฐํ ๋ฐฉ์์ผ๋ก ํด๊ฒฐ์ด ์๋๋ฉด NativeSQL or ์คํ๋ง JdbcTemplate5. API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ค๋ฌด ํ์ ์ต์ ํ
- OSIV์ ์ฑ๋ฅ ์ต์ ํ
'SPRING > INFLEARN' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[๊ฐ์] ์ค์ ! Querydsl 1 (0) 2022.08.22 [๊ฐ์] ์ค์ ! ์คํ๋ง ๋ฐ์ดํฐ JPA 1 (0) 2022.08.15 [๊ฐ์] ์ค์ ! ์คํ๋ง ๋ถํธ์ JPA ํ์ฉ2 - API ๊ฐ๋ฐ๊ณผ ์ฑ๋ฅ ์ต์ ํ 1 (0) 2022.07.25 [๊ฐ์] ์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ - ๊ธฐ๋ณธํธ 2 (0) 2021.12.01 [๊ฐ์] ์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ - ๊ธฐ๋ณธํธ 1 (0) 2021.11.29