티스토리 뷰
API 구현
회원 생성 API 구현
- 회원 객체 생성 api
- POST ("api/v2/members")
{
"name": "최똥꼬",
"email": "dnstlr2933@naver.com7",
"address": {
"zipcode" : "139xx",
"streetAdr": "안양시 동안구 동편로 xxx",
"detailAdr": "xxx-xxx"
}
}
Member 객체
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String name;
private String email;
@Embedded
private Address address;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
DTO를 사용하지 않고 Entity 그대로 api를 통해 구현하는 경우
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
등록
- 요청 값으로 Member 엔티티를 직접 받는다.
문제점
- 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
- 널값을 방어하기 위해 @NotNull 등을 달아줘야 하는 등 Entity에 대해 추가 제약이 붙는다
- API마다 필수 요소들이 다를수 있는데도 일률적으로 제한해야 한다.
- 엔티티에 API 검증을 위한 로직이 들어간다. (@NotEmpty, @NotNull)
- 엔티티가 변경되면 API 스펙이 같이 변한다.
- 즉 api가 Entity에 의존적이게 된다.
결론
- API 요청 스펙에 맞추어 별도의 DTO를 통해서 받는다.
DTO를 사용하여 api와 Entity를 구분하는 경우
@PostMapping("api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
Member member = new Member();
member.setName(request.getName());
member.setEmail(request.getEmail());
member.setAddress(request.getAddress());
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
등록
- 요청 값으로 Member 엔티티 대신에 별도의 DTO를 받는다
회원 생성을 위한 DTO
@Data
static class CreateMemberRequest {
private String name;
private String email;
private Address address;
}
응답
@Data
@AllArgsConstructor
static class CreateMemberResponse {
private Long id;
}
회원 수정 API 구현
- 회원 이름 수정 api
- PATCH ("api/v2/members/{id}")
{
"name": "운식이짱"
}
회원 이름을 수정하기 위한 DTO를 통해서 api를 받아서 처리
@PatchMapping("api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2 (
@PathVariable("id") Long id,
@RequestBody @Valid UpdateMemberRequest request)
{
memberService.update(id, request.getName());
Member findMember = memberService.findMember(id);
return new UpdateMemberResponse(findMember.getId(), findMember.getName());
}
회원 이름 변경용 DTO (이름만 받아서 변경)
@Data
static class UpdateMemberRequest{
private String name;
}
응답
@Data
@AllArgsConstructor
static class UpdateMemberResponse {
private Long id;
private String name;
}
회원 조회 API 구현
- GET ("api/v2/members")
회원 목록을 바로 반환시 문제점
- 기본적으로 Entity의 모든값이 노출되므로 매우 위험하다.
- 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
- 값을 내보내지 않기 위한 @JsonIgnore 등..
- 하나의 엔티티에 연결된 각각의 API를 위한 프레젠테이션 응답 로직을 담기는 매우 어렵다.
- 엔티티가 변경되면 API 스펙이 변한다.
- 회원의 이름을 name: "이름"으로 가져왔는데, entity의 name이 username으로 변경되어서 username으로 가져오게 되면 전체적인 api구조가 꼬이게 된다.
- 컬렉션을 직접 반환하면 항후 API 스펙을 변경하기 어렵다.
- 따라서 별도의 반환을 위한 Result 클래스를 생성해 해당 클래스를 값으로 갖도록해서 api의 유연한 확장성을 유지하자
- 예를 들어 회원 목록의 개수를 추가한다는 등 수정에 용이해진다.
결론
- API 응답 양식에 맞추어 별도의 DTO를 생성해서 반환하자
회원 조회 용 DTO를 통해서 api 처리
@GetMapping("api/v2/members")
public Result<?> memberV2() {
List<Member> members = memberService.findAllMembers();
List<MemberDto> collect = members.stream()
.map(m -> new MemberDto(m.getName(), m.getEmail()))
.collect(Collectors.toList());
return new Result<>(collect);
}
회원 리스트를 배열로 바로 보내지 않고 data로 포함해서 보내기 위한 응답 객체
@Data
@AllArgsConstructor
static class Result<T> {
private T data;
}
회원 조회용 DTO (이름과 이메일만 반환)
@Data
@AllArgsConstructor
static class MemberDto {
private String name;
private String email;
}
반응형
'스프링 > 스프링부트 API 설계 정리' 카테고리의 다른 글
API 개발 (4) - One to Many관계 - DTO 직접 조회 (0) | 2021.07.29 |
---|---|
API 개발 (3) - One to Many관계 - Entity조회 후 DTO로 변환 (0) | 2021.07.29 |
API 개발 (2) - X to One관계 (0) | 2021.07.28 |
웹 애플리케이션 API 개발 (DTO) (0) | 2021.07.25 |
(springboot) 회원가입시 주소입력을 위한 카카오 주소 API 활용 (1) | 2021.07.17 |
반응형
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday