@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
@GetMapping("/api/v1/members")
public List<Member> membersV1() {
return memberService.findMembers();
}
}
엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
public class Member {
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
기본적으로 엔티티의 모든 값이 노출된다.
{
"id": 1,
"name": "userA",
"address": {
"city": "서울",
"street": "1",
"zipcode": "1111"
}
}
응답 스펙을 맞추기 위해 로직이 추가된다. (@JsonIgnore, 별도의 뷰 로직 등등)
같은 엔티티에 대해 API가 용도에 따라 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 프레젠테이션 응답 로직을 담기는 어렵다.
엔티티가 변경되면 API 스펙이 변한다.
추가로 컬렉션을 직접 반환하면 항후 API 스펙을 변경하기 어렵다.(별도의 Result 클래스 생성으로 해결)
위 결과를 보면, 배열 내에 결과가 출력된다. 만약 조회하는 결과의 count를 표시하고 싶다면 배열 내에 추가할 수 없기 때문에 곤란한 상황이 되는 것이다.
-> API 응답 스펙에 맞추어 별도의 DTO를 반환한다
엔티티를 외부에 노출하면 안된다
member 엔티티의 데이터가 필요한 API가 계속 증가하게 된다면
어떤 API는 name 필드가 필요하지만, 어떤 API는 name 필드가 필요없을 수 있다. 결론적으로 엔티티 대신에 API 스펙에 맞는 별도의 DTO를 노출해야 한다.
조회 시 count도 함께 표시하도록 한다.
@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
@GetMapping("/api/v2/members")
public Result memberV2() {
List<Member> members = memberService.findMembers();
List<MemberDto> collect = members.stream()
.map(member -> new MemberDto(member.getName()))
.collect(Collectors.toList());
return new Result(collect.size(), collect);
}
@Data
@AllArgsConstructor
static class Result<T> {
private T data;
}
@Data
@AllArgsConstructor
static class MemberDto {
private String name;
}
}
참고 :