API 개발 기본
회원 등록 API
@RestController // controller + responseBody
@RequiredArgsConstructor{
private final MemberService memberservice;
@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);
}
@Data
static class CreateMemberRequest{
private String name;
}
@Data
static class CreateMemberResponse{
private Long id;
public CreateMemberResponse(Long id){
this.id = id;
}
}
}
v1 엔티티를 Request Body에 직접 매핑
문제점
- 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
- 엔티티에 API검증을 위한 로직이 들어간다.
- 실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 모든 요구사항을 담기 어렵다
- 엔티티가 변경되면 API 스펙이 변한다
결론: API요청 스펙에 맞추어 DTO를 파라미터로 받는다.
회원 수정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 ndw UpdateMemberResponse(findMember.getId(), findMember.getName());
}
@Data
static class UpdateMemberRequest{
private String name;
}
@data
@AllArgsConstructor
static class UpdateMemberResponse{
private Long id;
private String name;
}
//MemberService에 추가//
@Transactional
public void update(Long id, String name){
Member member = memberRepository.findOne(id);
member.setName(name);
}
회원 조회API
@RestController
@RequiredArgsConstructor
public class MemberApiController{
private final MemberService memberService;
@GetMapping("/api/v1/members")
public List<Member> membersV1(){
return memberservice.findMembers();
}
@GetMapping("/api/v2/members")
public Result memberV2(){
List<Member> findmembers = memberService.findMembers();
List<MemberDto> collect = finMembers.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 {
private String name;
}
}
응답 값으로 엔티티를 직접 외부에 노출
문제점
- 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
- 기본적으로 엔티티의 모든 값이 노출된다.
- 응답 스펙을 맞추기 위해 로직이 추가된다. (@JsonIgnore, 별도의 뷰 로직 등등)
- 실무에서는 같은 엔티티에 대해 API가 용도에 따라 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 프레젠테이션 응답 로직을 담기는 어렵다.
- 엔티티가 변경되면 API 스펙이 변한다.
- 추가로 컬렉션을 직접 반환하면 항후 API 스펙을 변경하기 어렵다.(별도의 Result 클래스 생성으로 해결)
결론: API 응답 스펙에 맞추어 별도의 DTO를 반환한다.
API개발 고급 준비
조회용 샘플 데이터 입력
UserA
- JPA1 BOOK
- jPA2 BOOK
UserB
- SPRING1 BOOK
- SPRING2 BOOK
@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 = OrderItemcreateOrderItem(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;
}
}
}
댓글남기기