ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA - API 기본
    Spring/JPA 2022. 3. 21. 19:31
    728x90
    반응형

     

     

     API에서는 하나만 기억하면 된다. 파라미터를 받거나 전송할 때 Entity를 사용하지 않고 DTO를 사용해야하는 것을 명심하자.

     

    DTO 사용 시 이점

     

     

    • 엔티티 대신에 RequestBody와 ResponseBody에 각각 DTO를 매핑한다.
    • 엔티티와 프레젠테이션(화면) 계층을 위한 로직을 분리할 수 있다.
    • 엔티티와 API 스펙을 명확하게 분리할 수 있다.
    • 엔티티가 변해도 API 스펙이 변하지 않는다.
    • 실무에서는 엔티티를 API 스펙에 노출하면 안된다.

     

     

     

    엔티티로 매핑시 문제점

     

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

     

     

    등록 API

     

    등록 DTO

    @Data
    class CreateMemberResponse {
        private Long id;
    
        public CreateMemberResponse(Long id) {
            this.id = id;
        }
    }
    
    @Data
    class CreateMemberRequest {
        private String name;
    }

     

    @PostMapping("/api/v2/members")
    public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
    
        Member member = new Member(request.getName(), null);
    
        Long id = memberService.join(member);
        return new CreateMemberResponse(id);
    
    }

     

    /api/v1/members

    요청

    {
        "name" : "hello"
    }
     
    응답
    {
        "id"3
    }

     


     

    수정 API

     

    • 수정 시 주의할 점은 변경 감지를 사용하여 데이터를 수정해야 한다.(merge 사용 금지)
    • PUT과 PATCH, POST의 사용
            - PUT : 전체 업데이트를 할 때 사용
            - PATCH, POST : 부분 업데이트를 할 때 사용

     

    수정 DTO

    @Data
    class UpdateMemberRequest {
        private String name;
    }
    
    @Data
    @AllArgsConstructor
    class UpdateMemberResponse {
        private Long id;
        private String name;
    }

     

    @PutMapping("/api/v2/members/{id}")
    public UpdateMemberResponse updateMemberV2(@PathVariable("id") Long id,
                                               @RequestBody @Valid UpdateMemberRequest request) {
        memberService.update(id, request.getName());
        Member findMember = memberService.findMember(id);
        return new UpdateMemberResponse(findMember.getId(), findMember.getName());
    }

     

    /api/v1/members

    요청

    {
        "name" : "change hello"
    }
     
    응답
     
    {
        "id"3,
        "name""change hello"
    }

     

     

    조회 API

     

    • 조회 API 역시 DTO를 사용해야 한다.

     

    조회 DTO

    @Data
    class Result<T> {
        private T data;
    
        public Result(T data) {
            this.data = data;
        }
    }
    
    @Data
    class MemberDto {
        private String name;
    
        public MemberDto(String name) {
            this.name = name;
        }
    }

     

    @GetMapping("/api/v4/members")
    public Result membersV4() {
        List<Member> findMembers = memberService.findMembers();
        //엔티티 -> DTO 변환
        List<MemberDto> collect = findMembers.stream()
                .map(m -> new MemberDto(m.getName()))
                .collect(Collectors.toList());
        return new Result(collect);
    }

     

     여기서 중요한점은 Result 클래스로 컬렉션을 감싸서 전달한다는 점이다. 그 이유는 컬렉션을 전송하게 되면 배열로 전송이 되는데 배열로 전송이되면 필드를 추가 할 수 없게 된다. 따라서 컬렉션을 전달할 때는 향후 개별 필드를 추가할 수 있도록 클래스로 감싸서 전달하는게 좋다.

     

     

    /api/v4/members

    요청

     

    응답

    {
        "data": [
            {
                "name""user1"
            },
            {
                "name""user2"
            },
            {
                "name""change hello"
            }
        ]
    }

     위 응답에서 보면 클래스로 감싸서 전달하였기 때문에 필드를 자유롭게 추가할 수 있다.

    (컬렉션으로 전달된 data를 보면 배열로 전달된것을 볼 수 있다.)

     

    728x90
    반응형

    댓글

Designed by Tistory.