# ๊ฐ์ข ์๊ฐ
# ๊ฐ์ข ์๊ฐ
# ์์ ์๋ฃ
# ๊ฐ์ ์์ค ์ฝ๋
# 2ํธ ์ถ๊ฐ ์๋ฃ
# API ๊ฐ๋ฐ ๊ธฐ๋ณธ
# ํ์ ๋ฑ๋ก API
// ๊ฐ๋ฐ ์ Request, Response์ ํด๋นํ๋ class๋ ๋ฐ๋ก ์์ฑ
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
Member member = new Member();
member.setName(request.getName());
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
# ํ์ ์์ API
// ENtity์์ ์ ๋ณ๊ฒฝ๊ฐ์ง๋ฅผ ํ์ฉ
// Update๋ ๋ฐํ๊ฐ์ด ์๋๊ฒ ์ข์(์ปค๋งจ๋์ ์ฟผ๋ฆฌ๋ฅผ ๋ถ๋ฆฌ)
@PutMapping("/api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2(@PathVariable("id") Long id, @RequestBody @Valid UpdateMemberRequest request) {
memberService.update(id, request.getName());
Member findMember = memberService.findOne(id);
return new UpdateMemberResponse(findMember.getId(), findMember.getName());
}
# ํ์ ์กฐํ API
// Entity๋ฅผ @JSonIgnore๋ฅผ ํ๋ ํํ๋ก๋ ๊ฐ๋ฅํ์ง๋ง ์ ๋ํ์ง ์๊ธฐ
// Entity์ ๊ฐ์ ๋ณ๊ฒฝ์ api์ Spec์ด ๋ณ๊ฒฝ๋๋ฉด ์๋จ
// Spec ํ์ฅ์ ๊ณ ๋ คํด์ data์ Array๋ฅผ ๋ฃ๋ ํํ๋ก ์งํ
@GetMapping("/api/v2/members")
public Result membersV2() {
List<Member> findMembers = memberService.findMembers();
//์ํฐํฐ -> DTO ๋ณํ
List<MemberDto> collect = findMembers.stream()
.map(m -> new MemberDto(m.getName()))
.collect(Collectors.toList());
return new Result(collect);
}
# API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ค๋น
# API ๊ฐ๋ฐ ๊ณ ๊ธ ์๊ฐ
์กฐํ์ฉ ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
์ง์ฐ ๋ก๋ฉ๊ณผ ์กฐํ ์ฑ๋ฅ ์ต์ ํ
์ปฌ๋ ์
์กฐํ ์ต์ ํ
ํ์ด์ง๊ณผ ํ๊ณ ๋ํ
OSIV์ ์ฑ๋ฅ ์ต์ ํ
# ์กฐํ์ฉ ์ํ ๋ฐ์ดํฐ ์ ๋ ฅ
userA
-JPA1 BOOK
-JPA2 BOOK
userB
-SPRING1 BOOK
-SPRING2 BOOK
initService.dbInit1();
initService.dbInit2();
jpa:
hibernate:
ddl-auto: create
# API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ง์ฐ ๋ก๋ฉ๊ณผ ์กฐํ ์ฑ๋ฅ ์ต์ ํ
# ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V1: ์ํฐํฐ๋ฅผ ์ง์ ๋ ธ์ถ
์ฃผ๋ฌธ + ๋ฐฐ์ก์ ๋ณด + ํ์์ ์กฐํํ๋ API๋ฅผ ๋ง๋ค์
์ง์ฐ ๋ก๋ฉ ๋๋ฌธ์ ๋ฐ์ํ๋ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ๋จ๊ณ์ ์ผ๋ก ํด๊ฒฐํด๋ณด์.
์ฐธ๊ณ : ์ง๊ธ๋ถํฐ ์ค๋ช
ํ๋ ๋ด์ฉ์ ์ ๋ง ์ค์ํฉ๋๋ค. ์ค๋ฌด์์ JPA๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด 100% ์ดํดํด์ผ ํฉ๋๋ค.
ManyToOne, OneToOne ์ฑ๋ฅ์ต์ ํ
localhost:8080/api/v1/simple-orders
์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๊ฐ ์์ ๊ฒฝ์ฐ @JsonIgnore
fetch =LAZY ์ง์ฐ๋ก๋ฉ ์.. ์๋ฌ๊ฐ ๋ฐ์ํด์ Hibernate5Module
Configure๋ฅผ ์์ ํด์ ๊ฐ์ Lazy๋ก๋ฉ์ ํ๊ฒํ ์๋ ์์ง๋ง ํ๋ฉด์๋จ..
hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true);
์๋์ ๊ฐ์ด ๊ฐ์ ์ด๊ธฐํํ ์ ์์
for (Order order : all) {
order.getMember().getName(); //Lazy ๊ฐ์ ์ด๊ธฐํ
order.getDelivery().getAddress(); //Lazy ๊ฐ์ ์ด๊ธฐํ
}
์ง์ฐ ๋ก๋ฉ(LAZY)๋ฅผ ํผํ๊ธฐ ์ํด ์ฆ์ ๋ก๋ฉ(EARER)์ผ๋ก ์ค์ ํ๋ฉด ์๋จ. ์ฆ์๋ก๋ฉ์ ์ฐ๊ด๊ด๊ณ๊ฐ ํ์์์๋๋ ๋ฐ์ดํฐ๋ฅผ ํญ์์กฐํํด์ ์ฑ๋ฅ๋ฌธ์ ๋ฅผ ๋ฐ์ํ ์ ์์
์ฆ์ ๋ก๋ฉ์ผ๋ก ์ค์ ์ ์ฑ๋ฅ ํ๋์ด ์ด๋ ค์์ง
ํญ์ ์ง์ฐ๋ก๋ฉ์ ๊ธฐ๋ณธ์ผ๋กํ๊ณ , ์ฑ๋ฅ ์ต์ ํ๊ฐ ํ์ํ ๊ฒฝ์ฐ ํ์น์กฐ์ธ(fetch join)์ ์ฌ์ฉ
# ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V2: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ
# org.hibernate.type: trace
@ManyToOne(fetch = EAGER) ํ๋ฉด ์๋๋ ์ด์
@ManyToOne(fetch = LAZY)๋ฅผ ์ฌ์ฉ
์ํฐํฐ๋ฅผ DTO๋ก ๋ณํํ๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ
์ฟผ๋ฆฌ๊ฐ ์ด 1 + N + N๋ฒ ์คํ๋๋ค. (v1๊ณผ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ ๊ฐ๋ค.)
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2() {
// ORDER 2๊ฐ
// N + 1 -> 1 + ํ์ N + ๋ฐฐ์ก N
List<Order> orders = orderRepository.findAll();
List<SimpleOrderDto> result = orders.stream()
.map(o -> new SimpleOrderDto(o))
.collect(toList());
return result;
}
# ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V3: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ - ํ์น ์กฐ์ธ ์ต์ ํ
# ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V4: JPA์์ DTO๋ก ๋ฐ๋ก ์กฐํ
# API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ปฌ๋ ์ ์กฐํ ์ต์ ํ
# ์ฃผ๋ฌธ ์กฐํ V1: ์ํฐํฐ ์ง์ ๋ ธ์ถ
# ์ฃผ๋ฌธ ์กฐํ V2: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ
# ์ฃผ๋ฌธ ์กฐํ V3: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ - ํ์น ์กฐ์ธ ์ต์ ํ
# ์ฃผ๋ฌธ ์กฐํ V3.1: ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ - ํ์ด์ง๊ณผ ํ๊ณ ๋ํ
# ์ฃผ๋ฌธ ์กฐํ V4: JPA์์ DTO ์ง์ ์กฐํ
# ์ฃผ๋ฌธ ์กฐํ V5: JPA์์ DTO ์ง์ ์กฐํ - ์ปฌ๋ ์ ์กฐํ ์ต์ ํ
# ์ฃผ๋ฌธ ์กฐํ V6: JPA์์ DTO๋ก ์ง์ ์กฐํ, ํ๋ซ ๋ฐ์ดํฐ ์ต์ ํ
ํ๋ฒ์ ์ฟผ๋ฆฌ๋ก ์คํ์ด ๊ฐ๋ฅ
// 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();
}
// OrderApiController
@GetMapping("/api/v6/orders")
public List<OrderQueryDto> ordersV6() {
List<OrderFlatDto> flats = orderQueryRepository.findAllByDto_flat();
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")
# 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 Spring JdbcTemplate
์ฐธ๊ณ : ์ํฐํฐ ์กฐํ ๋ฐฉ์์ ํ์น ์กฐ์ธ์ด๋, hibernate.default_batch_fetch_size, @BatchSize ๊ฐ์ด ์ฝ๋๋ฅผ ๊ฑฐ์ ์์ ํ์ง ์๊ณ , ์ต์
๋ง ์ฝ๊ฐ ๋ณ๊ฒฝํด์, ๋ค์ํ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์๋ํ ์ ์๋ค. ๋ฐ๋ฉด์ DTO๋ฅผ ์ง์ ์กฐํํ๋ ๋ฐฉ์์ ์ฑ๋ฅ์ ์ต์ ํ ํ๊ฑฐ๋ ์ฑ๋ฅ ์ต์ ํ ๋ฐฉ์์ ๋ณ๊ฒฝํ ๋ ๋ง์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํด์ผ ํ๋ค.
์ฐธ๊ณ : ๊ฐ๋ฐ์๋ ์ฑ๋ฅ ์ต์ ํ์ ์ฝ๋ ๋ณต์ก๋ ์ฌ์ด์์ ์คํ๊ธฐ๋ฅผ ํด์ผํ๋ค. ํญ์ ๊ทธ๋ฐ ๊ฒ์ ์๋์ง๋ง, ๋ณดํต ์ฑ๋ฅ ์ต์ ํ๋ ๋จ์ํ ์ฝ๋๋ฅผ ๋ณต์กํ ์ฝ๋๋ก ๋ชฐ๊ณ ๊ฐ๋ค.
์ํฐํฐ ์กฐํ๋ฐฉ์์ JPA๊ฐ ๋ง์ ๋ถ๋ถ์ ์ต์ ํ ํด์ฃผ๊ธฐ ๋๋ฌธ์, ๋จ์ํ ์ฝ๋๋ฅผ ์ ์งํ๋ฉด์, ์ฑ๋ฅ์ ์ต์ ํ ํ ์ ์๋ค.
๋ฐ๋ฉด์ DTO ์กฐํ ๋ฐฉ์์ SQL์ ์ง์ ๋ค๋ฃจ๋ ๊ฒ๊ณผ ์ ์ฌํ๊ธฐ ๋๋ฌธ์, ๋ ์ฌ์ด์ ์คํ๊ธฐ๋ฅผ ํด์ผ ํ๋ค.
DTO ์กฐํ ๋ฐฉ์์ ์ ํ์ง
- DTO๋ก ์กฐํํ๋ ๋ฐฉ๋ฒ๋ ๊ฐ๊ฐ ์ฅ๋จ์ด ์๋ค. V4, V5, V6์์ ๋จ์ํ๊ฒ ์ฟผ๋ฆฌ๊ฐ 1๋ฒ ์คํ๋๋ค๊ณ V6์ด ํญ์ ์ข์ ๋ฐฉ๋ฒ์ธ ๊ฒ์ ์๋๋ค.
-V4๋ ์ฝ๋๊ฐ ๋จ์ํ๊ฐ. ํน์ ์ฃผ๋ฌธ ํ๊ฑด๋ง ์กฐํํ๋ฉด ์ด ๋ฐฉ์์ ์ฌ์ฉํด๋ ์ฑ๋ฅ์ด ์ ๋์จ๋ค. ์๋ฅผ ๋ค์ด์ ์กฐํํ Order๋ฐ์ดํฐ๊ฐ 1๊ฑด์ด๋ฉด OrderItem์ ์ฐพ๊ธฐ ์ํ ์ฟผ๋ฆฌ๋ 1๋ฒ๋ง ์คํํ๋ฉด ๋๋ค.
-V5๋ ์ฝ๋๊ฐ ๋ณต์กํ๋ค. ์ฌ๋ฌ ์ฃผ๋ฌธ์ ํ๊บผ๋ฒ์ ์กฐํํ๋ ๊ฒฝ์ฐ์๋ V4 ๋์ ์ ์ด๊ฒ์ ์ต์ ํํ V5 ๋ฐฉ์์ ์ฌ์ฉํด์ผ ํ๋ค. ์๋ฅผ ๋ค์ด์ ์กฐํํ Order๋ฐ์ดํฐ๊ฐ 1000๊ฑด์ธ๋ฐ, V4 ๋ฐฉ์์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ฉด, ์ฟผ๋ฆฌ๊ฐ ์ด 1 + 1000๋ฒ ์คํ๋๋ค. ์ฌ๊ธฐ์ 1์ Order๋ฅผ ์กฐํํ ์ฟผ๋ฆฌ๊ณ , 1000์ ์กฐํ๋ Order์ row ์๋ค. V5 ๋ฐฉ์์ผ๋ก ์ต์ ํ ํ๋ฉด ์ฟผ๋ฆฌ๊ฐ ์ด 1 + 1๋ฒ๋ง ์คํ๋๋ค. ์ํฉ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ง๋ง ์ด์ ํ๊ฒฝ์์ 100๋ฐฐ ์ด์์ ์ฑ๋ฅ ์ฐจ์ด๊ฐ ๋ ์ ์๋ค.
-V6๋ ์์ ํ ๋ค๋ฅธ ์ ๊ทผ๋ฐฉ์์ด๋ค. ์ฟผ๋ฆฌ ํ๋ฒ์ผ๋ก ์ต์ ํ ๋์ด์ ์๋นํ ์ข์๋ณด์ด์ง๋ง, Order๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ด์ง์ด ๋ถ๊ฐ๋ฅํ๋ค. ์ค๋ฌด์์๋ ์ด์ ๋ ๋ฐ์ดํฐ๋ฉด ์๋ฐฑ์ด๋, ์์ฒ๊ฑด ๋จ์๋ก ํ์ด์ง ์ฒ๋ฆฌ๊ฐ ๊ผญ ํ์ํ๋ฏ๋ก, ์ด ๊ฒฝ์ฐ ์ ํํ๊ธฐ ์ด๋ ค์ด ๋ฐฉ๋ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ๊ฐ ๋ง์ผ๋ฉด ์ค๋ณต ์ ์ก์ด ์ฆ๊ฐํด์ V5์ ๋น๊ตํด์ ์ฑ๋ฅ ์ฐจ์ด๋ ๋ฏธ๋นํ๋ค.
# API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ค๋ฌด ํ์ ์ต์ ํ
# OSIV์ ์ฑ๋ฅ ์ต์ ํ
# ๋ค์์ผ๋ก
# ์คํ๋ง ๋ฐ์ดํฐ JPA ์๊ฐ
# QueryDSL ์๊ฐ
โ - springboot-jsp - jenkins โ