@PostMapping("/api/v1/members") funsaveMemberV1(@RequestBody member: @ValidMember): CreateMemberResponse { val id: Long = memberService.join(member) return CreateMemberResponse(id) }
dataclassResult<T>( valdata: T )
dataclassCreateMemberRequest( val name: String )
dataclassCreateMemberResponse( val id: Long )
요청 값으로 Member 엔티티를 직접 받는다.
문제점
엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
엔티티에 API 검증을 위한 로직이 들어간다. (@NotEmpty 등등)
실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 모든 요청 요구사항을 담기는 어렵다.
엔티티가 변경되면 API 스펙이 변한다.
등록 v2 엔티티 대신에 DTO를 RequestBody에 매핑
MemberApiController.kt
1 2 3 4 5 6 7 8 9
@PostMapping("/api/v2/members") funsaveMemberV2(@RequestBody request: @ValidCreateMemberRequest): CreateMemberResponse { val member = Member( name = request.name )
val id: Long = memberService.join(member) return CreateMemberResponse(id) }
CreateMemberRequest 를 Member 엔티티 대신에 RequestBody와 매핑한다.
엔티티와 프레젠테이션 계층을 위한 로직을 분리할 수 있다.
엔티티와 API 스펙을 명확하게 분리할 수 있다.
엔티티가 변해도 API 스펙이 변하지 않는다.
해결방법
등록 v2와 같이 API 요청 스펙에 맞추어 별도의 DTO를 파라미터로 받는다. 실무에서는 절대로 엔티티를 API 스펙에 노출하면 안된다.