Spring/API

회원 등록 API 개발

JWonK 2022. 6. 25. 17:41
728x90
반응형

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 

 

@RestController @Requestbody @Responsebody

웹 서비스와 REST 방식이 시스템을 구성하는 주요 요소로 자리 잡으면서 웹 시스템 간 XML이나 JSON 등의 반정형 형식으로 데이터를 주고 받는 경우가 증가하고 있다. 이에 따라 스프링 MVC도 클라이

wonsjung.tistory.com

 

 

위 로직을 이해해보면 회원 정보를 저장/등록 하기 위해 파라미터로 넘어오는 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 스펙을 명확하게 분리해준다. -> 엔티티와 프레젠테이션 계층을 위한 로직 분리!!

728x90
반응형