팅탱팅탱 2024. 3. 25. 00:02
package jpabook.jpashop.api;
 import jpabook.jpashop.domain.Member;
 import jpabook.jpashop.service.MemberService;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 import javax.validation.Valid;
 import java.util.List;
 import java.util.stream.Collectors;
 @RestController
 @RequiredArgsConstructor
 public class MemberApiController {
     private final MemberService memberService;
/**
* 등록 V1: 요청 값으로 Member 엔티티를 직접 받는다.
* 문제점
* - 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
* - 엔티티에 API 검증을 위한 로직이 들어간다. (@NotEmpty 등등)
* - 실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한
모든 요청 요구사항을 담기는 어렵다.
* - 엔티티가 변경되면 API 스펙이 변한다.
* 결론
* - API 요청 스펙에 맞추어 별도의 DTO를 파라미터로 받는다. */
     @PostMapping("/api/v1/members")
     public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member)
 {
         Long id = memberService.join(member);
         return new CreateMemberResponse(id);
     }
     @Data
     static class CreateMemberRequest {
         private String name;
     }
      @Data
     static class CreateMemberResponse {
         private Long id;
         public CreateMemberResponse(Long id) {
             this.id = id;
} }
}

회원 등록 api

 

@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());
}
 @Data
 static class UpdateMemberRequest {
     private String name;
 }
 @Data
 @AllArgsConstructor
 static class UpdateMemberResponse {
     private Long id;
     private String name;
 }

회원 수정 api

 

응답 값으로 엔티티를 직접 외부에 노출한 문제점

엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
기본적으로 엔티티의 모든 값이 노출된다.
응답 스펙을 맞추기 위해 로직이 추가된다. (@JsonIgnore, 별도의 뷰 로직 등등)
실무에서는 같은 엔티티에 대해 API가 용도에 따라 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위 한 프레젠테이션 응답 로직을 담기는 어렵다.
엔티티가 변경되면 API 스펙이 변한다.
추가로 컬렉션을 직접 반환하면 항후 API 스펙을 변경하기 어렵다.(별도의 Result 클래스 생성으로 해결)

결론
API 응답 스펙에 맞추어 별도의 DTO를 반환한다.

 

 

package jpabook.jpashop;
import jpabook.jpashop.domain.*;
import jpabook.jpashop.domain.item.Book;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
@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;
}
} }