스프링 부트와 JPA 활용2 - API 개발

JOY·2022년 5월 17일
0
post-thumbnail

📌 스프링 부트와 JPA 활용2 - API 개발

인프런 - 스프링 부트와 JPA 활용2 by 김영한 을 기반으로 작성된 글입니다.
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화


API 개발

1. 회원 등록 API

2. 회원 수정 API

3. 회원 조회 API


회원 등록 API - MemberApiController.java

V1 엔티티를 RequestBody에 직접 매핑

	//회원 등록 API - v1
    @PostMapping("/api/v1/members")
    public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member){
        Long id = memberService.join(member);
        return new CreateMemberResponse(id);
    }

    @Data
    static class CreateMemberResponse{
        //등록된 id 값만 반환
        private Long id;

        public CreateMemberResponse(Long id) {
            this.id = id;
        }
    }

📍 설명

API 어노테이션

  • @Controller @ResponseBody = @RestController
  • @ResponseBody : data 를 xml 이나 json으로 전송
  • @RequestBody : JSON으로 온 Body(data)를 member에 그대로 매핑
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {}

📍 실행

name : hello

send

로직 확인
(city, street, zipcode 는 입력하지 않았기 때문에 null)

그러나 데이터를 입력하지 않고 send 해도 null로 데이터가 입력된다

Member Entity에 따로 제약조건을 걸어둔 것이 없기 때문이다

@Entity
@Getter @Setter
public class Member {

	(생략)
   
   @NotEmpty
   private String name;
}
   

Member Entity의 name 컬럼에 @NotEmpty 제약조건 추가 후
데이터 입력하지 않고 send 하면 Springboot에서 기본으로 설정해놓은 에러 발생

CreateMemberResponse()의 @Vaildjavax.validation 을 검증 한다
Member Entity의 name 컬럼에 @NotEmpty 검증

❗ 엔티티를 RequestBody에 직접 매핑하면 생기는 문제점

  1. 엔티티에 Presentation 계층을 위한 로직이 추가된다
  2. API 검증을 위한 로직(ex. @NotEmpty 등)이 들어간다
  3. 하나의 엔테티에 각 API를 위한 모든 요구사항을 담기는 어렵다
  4. 엔티티가 변경되면 API 스펙 자체가 변경된다
    엔티티와 API 스펙이 1:1 로 매핑되어있기 때문
  • V1 방식의 유일한 장점 : CreateMemberRequest 클래스를 생성하지 않아도 되는 것

Presentation(표현)계층을 위한 검증 로직이 Entity에 모두 들어가 있다
사용하는 API에 따라 @NotEmpty 의 필요 유무가 다르기 때문에 문제가 발생한다

💡 해결
API 스펙을 위한 별도의 DTO(Data Transfer Object)를 생성하여 파라미터로 받아야한다 - 아래 V2에서 해결

중요!
API를 만들 때 항상 엔티티를 파라미터로 받지 마라
엔티티를 웹에 노출하지 말아라


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

//회원 등록 API - v2
    @PostMapping("/api/v2/members")
    public CreateMemberResponse saveMemberV1(@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{
        @NotEmpty
        private String name;
    }

📍 설명

  1. CreateMemberRequest 와 Member 엔티티 대신 RequestBody와 매핑 한다
  2. 엔티티와 Presentation 계층을 위한 로직을 분리할 수 있다.
  3. 엔티티와 API 스펙을 명확하게 분리할 수 있다.
  4. 엔티티가 변경 되어도 API 스펙이 변하지 않는다.

ex. Member Entity의 name 컬럼명이 username으로 변경되었을 때
setter 부분만 변경해주면 해결된다. API가 영향을 받지 않는다.
파라미터와 엔티티를 Controller에서 매핑해주는 것

Member member = new Member();
member.setUsername(request.getName());

엔티티를 외부에 노출하지 않고 API에 맞는 별도의 객체 DTO를 생성하여 만드는 것이 좋다

📍 실행

profile
Just Do IT ------- 🏃‍♀️

0개의 댓글