Java / Spring 환경에서 회원을 등록하는 API를 이해해보자.
가장 먼저 회원을 등록하기 위해서는 REST API에서 리소스를 새로 생성해야한다.
즉, 리소스를 조회하거나 수정하는 Method가 아닌 생성하는 Method -> POST 방식을 사용한다.
첫 번째 방식으로 API 생성 메서드에 파라미터가 엔티티 일 때를 예로 들어보자.
1. 요청 값으로 Member 엔티티를 직접 받는 경우
@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);
}
@Data
static class CreateMemberResponse{
private Long id;
public CreateMemberResponse(Long id) {
this.id = id;
}
}
}
API 개발을 위해서 가장 먼저 @RestController를 사용한다. @RestController가 무엇인지는 전에 정리했던 글이 있다.
https://wonsjung.tistory.com/387?category=1013361
위 로직을 이해해보면 회원 정보를 저장/등록 하기 위해 파라미터로 넘어오는 Member 엔티티를 @Valid를 통해 원하는 스펙에 맞는지 유효검사를 진행 한 후 @RequestBody를 통해 요청 본문에 담긴 값을 자바 객체로 변환해준다.
그리고 이미 구현해두었던 MemberService를 통해 저장하고 CreateMemberResponse클래스로 반환해준다.
사실 이렇게 표현되면 문제 없이 잘 구현된 것 같다. 하지만 이렇게 개발을 하게 될 경우 매우 취약한 단점이 존재한다.
우선, 엔티티(Member)에 프레젠테이션 계층을 위한 로직이 추가된다. 즉, 역할 분리가 제대로 이루어지지 않았다.
또한, 엔티티에 API 검증을 위한 로직이 들어간다. (@NotEmpty 등등) 실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 모든 요청 요구사항을 담기는 어렵다.
마지막으로 엔티티가 변경될 경우에는 API 스펙 자체가 변해버린다.
위 내용처럼 다양한 문제점이 존재한다. 따라서 우리는 유연하게 상황에 맞는 요구사항을 담고 있는 API 스펙 요청에 맞추어 별도의 DTO 클래스를 생성해 파라미터로 받아주어야한다.
V1 엔티티를 Request Body에 직접 매핑
- 문제점
- 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
- 엔티티에 API 검증을 위한 로직이 들어간다. (@NotEmpty 등등)
- 실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 모든 요청 요구사항을 담기는 어렵다.
- 엔티티가 변경되면 API 스펙이 변한다.
- 결론
- API 요청 스펙에 맞추어 별도의 DTO를 파라미터로 받는다.
2. V2 엔티티 대신에 DTO를 RequestBody에 매핑
@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request){
Member member = new Member();
member.setUsername(request.getName());
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
@Data
static class CreateMemberRequest{
private String name;
}
}
위 코드와 사실상 DTO 제외하고는 모두 동일한 코드이다.
- CreateMemberRequest를 Member 엔티티 대신에 RequestBody와 매핑한다.
- 엔티티와 프레젠테이션 계층을 위한 로직을 분리할 수 있다.
- 엔티티와 API 스펙을 명확하게 분리할 수 있다.
- 엔티티가 변해도 API 스펙이 변하지 않는다.
- 실무에서는 절대 엔티티를 외부에 노출하거나 엔티티를 파라미터에 받는 것은 금물이다.
결론 : 엔티티를 API 스펙에 노출하면 절대 안된다. API 스펙에 맞는 새로운 DTO 클래스를 하나 만들어 엔티티와 API 스펙을 명확하게 분리해준다. -> 엔티티와 프레젠테이션 계층을 위한 로직 분리!!
'Spring > API' 카테고리의 다른 글
API 개발 : 간단한 주문조회 (지연로딩 최적화 - 패치 조인) (0) | 2022.06.26 |
---|---|
API 개발 : 간단한 주문 조회 (지연 로딩) (0) | 2022.06.26 |
회원 조회 API 개발 / Result 클래스로 유연한 JSON 반환 (0) | 2022.06.25 |
회원 수정 API 개발 (0) | 2022.06.25 |
API란? REST API란? (0) | 2022.06.25 |