SpringJPA-API

GGOMG·2022년 10월 1일
0

1. 회원 등록 API

방법 1

    @PostMapping("/api/v1/members")
    public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
        Long id = memberService.join(member);
        return new CreateMemberResponse(id);
    }
    
    @Data // V1
    static class CreateMemberResponse {
        private Long id;

        public CreateMemberResponse(Long id) {
            this.id = id;
        }
    }
@Entity
@Getter @Setter
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    @NotEmpty
    private String name;

    @Embedded
    private Address address;

    @OneToMany(mappedBy = "member") // (거울, 읽기전용) order 테이블의 "member" 필드에 의해서 매핑됨
    private List<Order> orders = new ArrayList<>();
}

@Valid
javax validation을 검증한다
ex) @NotEmpty

문제점 1
화면(프레젠테이션) 계층의 검증 로직이 엔티티에 들어감

문제점 2
엔티티 스펙을 바꾸면 API 스펙도 바뀐다
private String name 을 username으로 바꾸면 API에서도 바뀌어버림
엔티티는 특성상 자주 바뀌는데 이것이 API 스펙에 영향을 주지 않아야 한다
따라서 API 스펙을 위한 별도의 DTO를 만들어야 한다

실무에서는 여러개의 등록 API가 만들어질 확률이 높다 (간편가입, SNS 가입 등)
API를 만들 때는 엔티티를 파라미터로 받지 말자, 노출도 하지 말자

방법 2 (DTO, 정석)

    @PostMapping("/api/v2/members")
    public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {

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

        Long id = memberService.join(member);
        return new CreateMemberResponse(id);
    }
    
	@Data // V2
    static class CreateMemberRequest {
        @NotEmpty
        private String name;
    }
  • 엔티티의 변경이 API 스펙에 영향을 주지 않음
  • CreateMemberRequest만 확인하면 API 스펙을 확인할 수 있음

2. 회원 수정 API

    @PutMapping("/api/v2/members/{id}")
    public UpdateMemberResponse updateMemberV2(
            @PathVariable("id") Long id,
            @RequestBody @Valid UpdateMemberRequest request) {

        memberService.update(id, request.getName());
        Member findMember = memberService.findOne(id);
        return new UpdateMemberResponse(findMember.getId(), findMember.getName());

    }

등록과 수정은 API 스펙이 거의 다르다. 따라서 별도의 DTO를 만든다.

3. 회원 조회 API

방법 1

    @GetMapping("/api/v1/members")
    public List<Member> membersV1() {
        return memberService.findMembers();
    }

postman json 결과

[
    {
        "id": 1,
        "name": "new-hello",
        "address": null,
        "orders": []
    },
    {
        "id": 2,
        "name": "member1",
        "address": {
            "city": "서울",
            "street": "마조로51",
            "zipcode": "123123"
        },
        "orders": []
    },
    {
        "id": 3,
        "name": "memer2",
        "address": {
            "city": "부산",
            "street": "부조로",
            "zipcode": "3321"
        },
        "orders": []
    }
]

문제점 1
회원에 대한 정보만 요청했지만, orders 목록도 같이 포함되어있다.
엔티티를 직접 노출하면, 엔티티에 대한 정보도 외부로 노출된다.

엔티티에 @JsonIgnore 를 추가하면 제외할 수 있지만,
다른 다양한 API를 만들 때마다 엔티티에 대한 변경이 어렵다
또한 엔티티에 표현계층 로직이 추가되어 유지보수성을 해친다. (양방향 의존관계)

문제점 2
엔티티 스펙을 바꾸면 API 스펙도 바뀐다

문제점 3
컬렉션을 직접 반환하면 안되고, json 스펙에 맞게 보내야한다 (유연성이 떨어짐)

방법 2

    @GetMapping("/api/v2/members")
    public Result membersV2() {
        List<Member> findMembers = memberService.findMembers();
        List<MemberDTO> collect = findMembers.stream()
                .map(m -> new MemberDTO(m.getName()))
                .collect(Collectors.toList());

        return new Result(collect);
    }

    @Data
    @AllArgsConstructor
    static class Result<T>{
        private T data;
    }

    @Data
    @AllArgsConstructor
    static class MemberDTO{
        private String name;
    }

DTO만 만들어도 많은 문제를 해결할 수 있다.


0개의 댓글