[๊ฐ์] ์ค์ ! ์คํ๋ง ๋ถํธ์ JPA ํ์ฉ2 - API ๊ฐ๋ฐ๊ณผ ์ฑ๋ฅ ์ต์ ํ 1
์ค์ ! ์คํ๋ง ๋ถํธ์ JPA ํ์ฉ2 - API ๊ฐ๋ฐ๊ณผ ์ฑ๋ฅ ์ต์ ํ - ์ธํ๋ฐ | ๊ฐ์
์คํ๋ง ๋ถํธ์ JPA๋ฅผ ํ์ฉํด์ API๋ฅผ ๊ฐ๋ฐํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ JPA ๊ทนํ์ ์ฑ๋ฅ ์ต์ ํ ๋ฐฉ๋ฒ์ ํ์ตํ ์ ์์ต๋๋ค., - ๊ฐ์ ์๊ฐ | ์ธํ๋ฐ...
www.inflearn.com
1. API ๊ฐ๋ฐ ๊ธฐ๋ณธ
- ํ์ ๋ฑ๋ก API
version 1
//MemberApiController.java
@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member){
//Json์ผ๋ก ์จ ๋ฐ์ดํฐ๋ฅผ member๋ก ์น ๋ฐ๊ฟ์ฃผ๋ @RequestBody
//์ ํจ์ฑ ๊ฒ์ฌ @Valid
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
@Data
static class CreateMemberResponse {
private Long id;
public CreateMemberResponse(Long id) {
this.id = id;
}
}
}
=> ๊ฐ์ด ์์ด๋ ์ ์ฅ์ด ๋๋ค.
๋ฐฉ์งํ๊ณ ์ถ๋ค๋ฉด?
Member.java์ name์ @NotEmpty๋ฅผ ๋ถ์ฌ์ค๋ค.
@Valid ๋ถ์ธ ํจ๊ณผ๋ก name ์ด ๋น์ด์์ผ๋ฉด ๊ฑธ๋ฌ์ค!
=> ์ ๋ฐฉ๋ฒ์ ๋ฌธ์ ๊ฐ ๋ง๋ค.
presentation(ํ๋ฉด)์ ์ํ ๊ฒ์ฆ ๋ก์ง์ด Entity ์ฝ๋์ ๋ค์ด์ ์์.
1. ์ด๋ค API๋ @NotEmpty๊ฐ ํ์ํ์ง ์์ ์๋ ์์
2. Entity ์ name -> userName์ผ๋ก ๋ฐ๊พผ ๊ฒฝ์ฐ, Entity์ ์คํ์ด ๋ณ๊ฒฝ๋จ
ํด๋ผ์ธก์์๋ ๊ฐ์๊ธฐ API๊ฐ ๋์ํ์ง ์์
Entity๋ฅผ ์๋์ ์คํ ์์ฒด๊ฐ ๋ณ๊ฒฝ๋๋ค๋ ๊ฒ์ด ๋ฌธ์ ์
-> ์ด๋ฅผ ์ฐ๊ฒฐํ๋ DTO๋ฅผ ๋ง๋ค์ด์ API ์์ฒญ ์คํ์ด ๋ณ๊ฒฝ๋์ง ์๋๋ก ํด์ผ ํจ
์ํฐํฐ ์ธ๋ถ ๋ ธ์ถ ์๋จ. ์ํฐํฐ๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ง ๋ง
version2
@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
/*์๋ต*/
@PostMapping("/api/v21/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);
}
@Data
private class CreateMemberRequest {
private String name;
}
}
version2์ ์ฅ์
ํด๋์ค๋ฅผ ํ๋ ๋ ๋ง๋ค์ด์ผํ๋ ๋จ์ ์ด ์๊ธด ํ์ง๋ง,
1. ์ด๋ฆ์ ๋ณ๊ฒฝํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋์ค(setter)
2. DTO๋ ์ด๋ค ๊ฐ์ ์ด๋ป๊ฒ ๋ฐ๋์ง ํ ๋์ ๋ณด์ด๊ฒ ํด์ค, ์ ์ง๋ณด์๊ฐ ๋งค์ฐ ํธ๋ฆฌํจ
-> ์ฌํผ ๋ฌด์กฐ๊ฑด 2 ์จ์ผํจ. DTO ์๋ง๋ค๋ฉด ์๋จ
- ํ์ ์์ API
@RestController
@RequiredArgsConstructor
public class MemberApiController {
/*์๋ต*/
//Update๋ ๋ณ๊ฒฝ์ฑ์ด๋ค
//Member๋ฅผ ๋ฐํํ๋ฉด, ์์์ฑ์ด ๋๊ธด member๊ฐ ๋ฐํ์ด ๋๋ค.
//id๋ก ๋ค์ ์กฐํํด์ ์ฐพ์์ค๋ ๋๋
//์ปค๋งจ๋์ ์ฟผ๋ฆฌ๊ฐ ๊ฐ์ด ์๋ ๊ผด์ด ๋จ
//๊ทธ๋ฅ ์์ ์กฐํ๋ฅผ ๋ค์ ํด์ member๋ฅผ ๋ฐํํ๊ฑฐ๋, id๋ง ๋ฐํํ๋ ๊ผด์ด ๋์
//์ ์ง๋ณด์์ฑ์ด ์ฆ๋๋จ
@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());
}
//TIP)) ๊ฐ์ฌ๋๊ป์๋ ์ํฐํฐ์๋ Lombok ์ฌ์ฉ์ ์์ ํ๋ ํธ์ด์์ง๋ง
// DTO์๋ ๋ง ์ฐ์๋ ํธ์ด๋ค๋ค
@Data
static class UpdateMemberRequest {
@NotEmpty
private String name;
}
@Data
@AllArgsConstructor
static class UpdateMemberResponse {
private Long id;
private String name;
}
}
//MemberService.java
@Service
@Transactional(readOnly = true)//์ฝ๊ธฐ
@RequiredArgsConstructor
public class MemberService {
/*์๋ต*/
//Transactional์ ์ํด
//Spring AOP๊ฐ ๋๋๋ ์์ ์ commit์ด ๋๊ณ
//๊ทธ ๋ JPA๋ flushํ๊ณ ์์์ฑ ์ปจํ
์คํธ ์ปค๋ฐ๋ ๋ค ํด๋ฒ๋ฆผ
@Transactional
public void update(Long id, String name) {
Member member = memberRepository.findOne(id);
member.setName(name);
}
}
- ํ์ ์กฐํ API
๋ฌด์ํ version1
@RestController
@RequiredArgsConstructor
public class MemberApiController {
/*์๋ต*/
@GetMapping("/api/v1/members")
public List<Member> membersV1(){
return memberService.findMembers();
}
/*์๋ต*/
}
์ด๋ ๊ฒ ๋๋ฉด ํ์ ์๋ ์ฃผ๋ฌธ ์ ๋ณด๊น์ง ๊ฐ์ด ๋ฐ์์ง
์ด๊ฑธ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ผ๋ก orders์ @JsonIgnore์ ๋ถ์ด๋ ๋ฐฉ๋ฒ์ด ์์
-> ํ์ง๋ง ๋ค๋ฅธ ๋ฐ์ ํ์ํ ๊ฒฝ์ฐ๋ ์๊ณ
presentation ๊ณ์ธต์ ์ํ ๋ก์ง์ด Entity์ ์ถ๊ฐ๋จ (์๋ฐฉํฅ ์์กด ๊ด๊ณ)
๋ ๋ณ์ ์ด๋ฆ ๋ฐ๊พธ๋ฉด(์ํฐํฐ ์คํ์ด ๋ณ๊ฒฝ๋๋ฉด) response์ ๋ณ์ ์ด๋ฆ์ด ๋ฐ๋์ด์ API ์คํ์ด ๋ฐ๋
=> ์ญ์ ๊ทธ๋๋ก Entity๋ฅผ ์ง์ ๋ฐํํ๋ฉด ์๋จ, Array๋ฅผ ๊ทธ๋๋ก ๋ฐํํ๋ ๊ฒ๋ ๋ฌธ์ ๊ฐ ๋จ(๋ค๋ฅธ ๊ฒ๊ณผ ํจ๊ป ๋ฐํํ ์ ์์)
@RestController
@RequiredArgsConstructor
public class MemberApiController {
/*์๋ต*/
@GetMapping("/api/v2/members")
public Result membersV2(){
List<Member> findMembers = memberService.findMembers();
List<MemberDto> collect = findMembers.stream()
.map(m -> new MemberDto(m.getName()))
.collect(Collectors.toList());
return new Result(collect);
}
@Data
@AllArgsConstructor
static class Result<T>{
private T data;
}
@Data
@AllArgsConstructor
static class MemberDto<T>{
private String name;
}
/*์๋ต*/
}
1. ๊ฒ์ด ๋ฐฐ์ด([])์ด ์๋ ์ค๋ธ์ ํธ ({}) ํํ์
์ ์ฐ์ฑ์ด ์๊ธด๋ค, ํ์ํ ํ๋๊ฐ ์๋ค๋ฉด ์ถ๊ฐํด์ ๋ฆฌํดํ๊ธฐ ํธํจ
2. ํ์ํ ์ ๋ณด๋ง ๋ด๊ฒจ ์์
2. API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ค๋น
- ์กฐํ์ฉ ์ํ ๋ฐ์ดํฐ ์ ๋ ฅ
์๋ ์ฝ๋๋ก ๋ฐ์ดํฐ ์ ๋ ฅ
@Component
@RequiredArgsConstructor
public class InitDb {
private final InitService initService;
@PostConstruct
public void init() {
initService.dbInit1();
initService.dbInit2();
}
@Component
@Transactional
@RequiredArgsConstructor
static class InitService {
private final EntityManager em;
public void dbInit1() {
Member member = createMember("userA", "์์ธ", "1", "1111");
em.persist(member);
Book book1 = createBook("JPA1 BOOK", 10000, 100);
em.persist(book1);
Book book2 = createBook("JPA2 BOOK", 20000, 100);
em.persist(book2);
OrderItem orderItem1 = OrderItem.createOrderItem(book1, 10000, 1);
OrderItem orderItem2 = OrderItem.createOrderItem(book2, 20000, 2);
Order order = Order.createOrder(member, createDelivery(member),
orderItem1, orderItem2);
em.persist(order);
}
public void dbInit2() {
Member member = createMember("userB", "์ง์ฃผ", "2", "2222");
em.persist(member);
Book book1 = createBook("SPRING1 BOOK", 20000, 200);
em.persist(book1);
Book book2 = createBook("SPRING2 BOOK", 40000, 300);
em.persist(book2);
Delivery delivery = createDelivery(member);
OrderItem orderItem1 = OrderItem.createOrderItem(book1, 20000, 3);
OrderItem orderItem2 = OrderItem.createOrderItem(book2, 40000, 4);
Order order = Order.createOrder(member, delivery, orderItem1,
orderItem2);
em.persist(order);
}
private Member createMember(String name, String city, String street,
String zipcode) {
Member member = new Member();
member.setName(name);
member.setAddress(new Address(city, street, zipcode));
return member;
}
private Book createBook(String name, int price, int stockQuantity) {
Book book = new Book();
book.setName(name);
book.setPrice(price);
book.setStockQuantity(stockQuantity);
return book;
}
private Delivery createDelivery(Member member) {
Delivery delivery = new Delivery();
delivery.setAddress(member.getAddress());
return delivery;
}
}
}
3. API ๊ฐ๋ฐ ๊ณ ๊ธ - ์ง์ฐ ๋ก๋ฉ๊ณผ ์กฐํ ์ฑ๋ฅ ์ต์ ํ
- ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V1 : ์ํฐํฐ๋ฅผ ์ง์ ๋ ธ์ถ
/*
Order๋ฅผ ์กฐํํ๊ณ ,
Order -> Member m : 1
Order -> Delivery ์ฐ๊ด 1 : 1
๊ฒฐ๋ก : toOne ๊ด๊ณ์์์ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ด๋ป๊ฒ ํ ๊ฒ์ธ๊ฐ
ํ์ง๋ง OrderItem์ toMany ๊ด๊ณ์ -> ์ปฌ๋ ์
์ฌ์ฉ, ๋ณต์ก, ๋ค์์ฅ์์
*/
@RequiredArgsConstructor
@RestController
public class OrderSimpleController {
private final OrderRepository orderRepository;
@GetMapping("/api/v1/sample-orders")
public List<Order> orderV1(){
List<Order> all = orderRepository.findAllByString(new OrderSearch());
return all;
}
}
=> ๋ฌธ์ : ๊บ์์ ๋ฌดํ๋ฃจํ!
ํด๊ฒฐ๋ฒ - ์๋ฐฉํฅ ์ฐ๊ด ๊ด๊ณ๊ฐ ์์ ๊ฒฝ์ฐ, ๋ ์ค ํ๋๋ @JsonIgnore์ ๋ถ์ฌ์ค์ผ ํ๋ค
=> ๋ฌธ์ : Proxy ์ด์ฉ๊ตฌ ์๋ฌ ๋ฐ์
@Entity
@Table(name = "orders") // ํ
์ด๋ธ ์ด๋ฆ๊ณผ class๋ช
์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
/*์๋ต*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
/*์๋ต*/
}
fetch๊ฐ Lazy๋ก ๋์ด์๊ธฐ ๋๋ฌธ์ (1:n ์ ๊ธฐ๋ณธ์ด Lazy)
Order๋ฅผ ๊ฐ์ง๊ณ ์ฌ ๋, null๋ก ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ Proxy Member๋ฅผ ๋์ ๋ฃ์ด๊ฐ์ง๊ณ ๊ฐ์ ธ์ด
Member ๊ฐ์ฒด์ ์์ ๋ ๋, sql ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค ๋ง๋ ๊ฐ์ฒด๋ฅผ ๋ฃ์
json์ด member๋ฅผ ๋ฝ์ ๋๋ proxy๊ฐ ์๊ธฐ์ ์ค๋ฅ๊ฐ ๋๊ฒ ๋จ
-> ์ง์ฐ ๋ก๋ฉ์ธ ๊ฒฝ์ฐ์๋ ์์ json์ผ๋ก ๋ฟ๋ฆฌ์ง ์๋๋ก ํ๋ hibernate module์ด ์์
(์๋ ๋งํฌ์์ ๋ณต์ฌํด์ build.gradle์ ๋ฃ๊ณ ์ฌ์ฉ)
https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-hibernate5/2.9.9
@SpringBootApplication
public class JpashopApplication {
public static void main(String[] args) {
SpringApplication.run(JpashopApplication.class, args);
}
@Bean
Hibernate5Module hibernate5Module(){
Hibernate5Module hibernate5Module = new Hibernate5Module();
return hibernate5Module;
}
}
=> ์ง์ฐํ๋ ์ ๋ค์ ์ ๋ถ null๋ก json์ผ๋ก ๋ฟ๋ ค์ง
์ต์ ์ ๋จน์ฌ์ ๊ฐ์ ๋ก ๋ก๋ฉ๋๊ฒ ํ๋ ๋ฐฉ๋ฒ๋ ์์(์ฌ์ค ์ด๋ฐ๊ฑฐ ์ฐ๋ฉด ์๋จ)
์ต์ ์์ฐ๊ณ ์ด๋ฐ๊ฑฐ ๋ถ์ฌ์ ๊ฐ์ ์ด๊ธฐํ ํ๋ ๋ฐฉ๋ฒ๋ ์์
for( Order order : all){
order.getOrder().getName();
order.getDelivery().getAddress();
}
=> 1. ๊ณ์ ๋ฌธ์ ์๋ ์ํฐํฐ ๋ ธ์ถ
2. ์ฑ๋ฅ ์ ํ, ํ์ ์๋ API ์คํ์ด ๋ ธ์ถ๋จ
์ฃผ์
์ง์ฐ ๋ก๋ฉ(LAZY)์ ํผํ๊ธฐ ์ํด ์ฆ์ ๋ก๋ฉ(EARGR)์ผ๋ก ์ค์ ํ๋ฉด ์๋๋ค!
์ฆ์ ๋ก๋ฉ ๋๋ฌธ์ ์ฐ๊ด๊ด๊ณ๊ฐ ํ์ ์๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ๋ฅผ ํญ์ ์กฐํํด์ ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
์ฆ์ ๋ก๋ฉ์ผ๋ก ์ค์ ํ๋ฉด ์ฑ๋ฅ ํ๋์ด ๋งค์ฐ ์ด๋ ค์ ์ง๋ค.
-> ํญ์ ์ง์ฐ ๋ก๋ฉ์ ๊ธฐ๋ณธ์ผ๋ก ํ๊ณ , ์ฑ๋ฅ ์ต์ ํ๊ฐ ํ์ํ ๊ฒฝ์ฐ์๋ ํ์น ์กฐ์ธ(fetch join)์ ์ฌ์ฉํด๋ผ!(V3)
- ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V2 : ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ
@RequiredArgsConstructor
@RestController
public class OrderSimpleController {
/*์๋ต*/
@GetMapping("/api/v2/sample-orders")
public List<SimpleOrderDto> orderV2(){
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
return orders.stream()
.map(o-> new SimpleOrderDto(o))
.collect(Collectors.toList()); //static import๊น์ง ํ๋ฉด ์ฝ๋๋ฅผ ๋ ์ค์ผ ์ ์์
}
private 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 + N ๋ฒ ์คํ๋๋ค (์ต์ ์ ๊ฒฝ์ฐ)
์ง์ฐ๋ก๋ฉ์ ์์์ฑ ์ปจํ ์คํธ์์ ์กฐํํ๋ฏ๋ก, ์ด๋ฏธ ์กฐํ๋ ๊ฒฝ์ฐ ์ฟผ๋ฆฌ๋ฅผ ์๋ตํ๊ธฐ๋ ํ๊ธฐ ๋๋ฌธ
- ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V3 : ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํ - ํจ์น ์กฐ์ธ ์ต์ ํ
@Repository
@RequiredArgsConstructor
public class OrderRepository {
/*์๋ต*/
public List<Order> findAllWithMemberDelivery(){
//fetch join์ ๋งค์ฐ ์ค์. 100% ์ดํดํด์ผ ํจ
return em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class
).getResultList();
}
}
fetch join์ผ๋ก ํ๋์ ์ฟผ๋ฆฌ๋ก ๋ชจ๋ ๋ด์ฉ์ ๋ค ๊ฐ์ง๊ณ ์จ๋ค -> ์ฑ๋ฅ ์ต์ ํ
@RequiredArgsConstructor
@RestController
public class OrderSimpleController {
/*์๋ต*/
@GetMapping("/api/v3/sample-orders")
public List<SimpleOrderDto> orderV3(){
List<Order> orders = orderRepository.findAllWithMemberDelivery();
return orders.stream()
.map(o-> new SimpleOrderDto(o))
.collect(Collectors.toList());
}
/*์๋ต*/
}
- ๊ฐ๋จํ ์ฃผ๋ฌธ ์กฐํ V4 : JPA์์ DTO๋ก ๋ฐ๋ก ์กฐํ
@Data
public class OrderSimpleQueryDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public OrderSimpleQueryDto(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;
}
}
@Repository
@RequiredArgsConstructor
public class OrderRepository {
/*์๋ต*/
public List<OrderSimpleQueryDto> findOrderDtos() {
return em.createQuery(
"select new jpabook.jpashop.repository.order.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();
}
}
@RequiredArgsConstructor
@RestController
public class OrderSimpleController {
/*์๋ต*/
@GetMapping("/api/v4/sample-orders")
public List<SimpleOrderDto> orderV4(){
List<Order> orders = orderRepository.findAllWithMemberDelivery();
return orders.stream()
.map(o-> new SimpleOrderDto(o))
.collect(Collectors.toList());
}
/*์๋ต*/
}
V3์ V4๋ ์ฅ๋จ์ ์ด ์์
V3์ ๋นํด V4๋ ์ํ๋ ๊ฒ๋ง ์กฐํ
new ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํด์ JPQL์ ๊ฒฐ๊ณผ๋ฅผ DTO๋ก ์ฆ์ ๋ณํ
ํ์ง๋ง,
์ฌ์ฌ์ฉ์ฑ์ด ์์. ์ด DTO๋ฅผ ์ฌ์ฉํ ๋๋ง ์ฌ์ฉํ๊ฒ ๋จ
์ํ๋ ๋ฐ์ดํฐ๋ง ์ง์ SELECT ์ ์์ ์ ํํ๋ฏ๋ก ๋คํธ์ํฌ ์ฉ๋ ์ต์ ํ(์์ฆ์๋ ๋ค ๋นจ๋ผ์ ์ด์ ๋๋ ๋ฏธ๋ฏธ)
๋ฐ์ดํฐ ์ฌ์ด์ฆ๊ฐ ๋๋ฌด ํฌ๋ค๋ฉด ๊ณ ๋ คํด๋ณด๋ ๊ฒ ์ข์
API ์คํ์ ๋ง์ถ ์ฝ๋๊ฐ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ค์ด๊ฐ๋ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด,
Repository์์ ์ ์ ์ฉ์ผ๋ก ์ ์ฝ๋ ๋ถ๋ถ๋ง ๋นผ์ ๋ฃ๋ ๋ฐฉ๋ฒ
์ผ๋จ V3๋ฅผ ์ฐ๊ณ , ์ด๋๋ ์ฝํ๋ฉด V4
์ด๋๋ ์๋๋ฉด JPA๊ฐ ์ ๊ณตํ๋ ๋ค์ดํฐ๋ธ SQL์ด๋ ์คํ๋ง JDBC Template์ ์ฌ์ฉํด์ SQL์ ์ง์
์ฌ์ฉ