[JPA2] API개발 기본

레몬커드요거트·2022년 11월 1일
0

회원 등록 API

package jpabook.jpashop.api;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.service.MemberService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequiredArgsConstructor
public class MemberApiController {

    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;
        }
    }
}

📌 [포스트맨415에러]_@RequestBody를 지워야하는 이유
혹은 Header에서 Content-Type를 application/json로 수정

V1 엔티티를 Request Body에 직접 매핑

  • 문제점
    • 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
    • 엔티티에 API 검증을 위한 로직이 들어간다. (@NotEmpty 등등)
    • 실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 모든 요청 요구사항을 담기는 어렵다.
    • 엔티티가 변경되면 API 스펙이 변한다.
  • 결론
    • API 요청 스펙에 맞추어 별도의 DTO를 파라미터로 받는다.

V2 엔티티 대신에 DTO를 RequestBody에 매핑

  • CreateMemberRequest 를 Member 엔티티 대신에 RequestBody와 매핑한다.
  • 엔티티와 프레젠테이션 계층을 위한 로직을 분리할 수 있다.
  • 엔티티와 API 스펙을 명확하게 분리할 수 있다.
  • 엔티티가 변해도 API 스펙이 변하지 않는다.

실무에서는 엔티티를 API 스펙에 노출하면 안된다.

회원 수정 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;
}
public memberService{
  	...
    @Transactional
    public void update(Long id, String name) {
        Member member = memberRepository.findOne(id);
        member.setName(name);
    }
}

회원조회 API

안좋은 회원 조회 기능 구현

@GetMapping("/api/v1/members")
public List<Member> membersV1(){
  return memberService.findMembers();
}

@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    @Column(name="member_id") 
    private Long id;

    @NotEmpty
    private String name;

    @Embedded 
    private Address address;

  	@JsonIgnore
    @OneToMany(mappedBy = "member") 
    private List<Order> orders = new ArrayList<>();
}

단점: Enitity가 그대로 노출이 됨.

@JsonIgnore 사용 시, postman으로 api를 이용한 서버 응답을 받았을 때 특정 데이터를 노출이 안되게 변경 가능
📌 API 응답 스펙에 맞추어 별도의 DTO를 반환하여 필요한 것만 노출

@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);
}


@Data
@AllArgsConstructor
static class Result<T>{
  private T data;
}

@Data
@AllArgsConstructor
static class MemberDto{
  private String name;
}
profile
비요뜨 최고~

0개의 댓글