
- 회원조회 V1 : 응답 값으로 엔티티를 직접 외부에 노출
- 회원조회 V2 : 응답 값으로 엔티티가 아닌 별도의 DTO 사용
- 결론 미리보기 : 회원조회 API도 회원등록/수정 API와 마찬가지로 엔티티가 아닌 별도의 DTO를 사용하는 것이 좋음
application.yml
ddl-auto를 create에서 none으로 변경spring:
jpa:
hibernate:
ddl-auto: none
...(생략)...

응답 값으로 엔티티를 직접 외부에 노출
@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
/**
* 조회 V1 : 응답 값으로 엔티티를 직접 외부에 노출
* @return List<Member>
*/
@GetMapping("/api/v1/members")
public List<Member> membersV1() {
return memberService.findMembers();
}

@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String name;
@Embedded
private Address address;
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
@JsonIgnore

API는 하나만 존재하는 것이 아니기 때문에 화면에 종속적으로 작성하면 안됨
모든 엔티티가 노출되고 있음
엔티티에 프레젠테이션 계층을 위한 로직이 추가됨
응답 스펙을 맞추기 위해 로직이 추가됨 (@JsonIgnore, 별도의 뷰 로직 등)
실무에서는 같은 에닡티에 대해 API가 용도에 따라 다양하게 만들어짐
엔티티가 변경되면 API 스펙이 변함
[
{
"id": 1,
"name": "member1",
"address": {
"city": "seoul",
"street": "street1",
"zipcode": "101010"
},
"orders": []
},
{
"id": 2,
"name": "member2",
"address": {
"city": "paris",
"street": "street1",
"zipcode": "202020"
},
"orders": []
},
...
]
[
count : 1
data : [
{
"id": 1,
"name": "member1",
"address": {
"city": "seoul",
"street": "street1",
"zipcode": "101010"
},
"orders": []
},
{
"id": 2,
"name": "member2",
"address": {
"city": "paris",
"street": "street1",
"zipcode": "202020"
},
"orders": []
},
...
]
]
Result 클래스 생성으로 해결(V2)
- 응답 값으로 엔티티가 아닌 별도의 DTO 사용
- Result 클래스로 collection을 감싸서 반환
@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
...
/**
* 조회 V2 : 응답 값으로 엔티티가 아닌 별도의 DTO 사용
* @return Result
*/
@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);
}
멤버 엔티티에서 이름을 꺼내서 DTO로 넣고(map으로 바꿔치기) collect해서 toList로 바꾸기
추후 MemberDto를 수정해서 필드를 추가할 수 있음
Result 클래스로 컬렉션을 감싸서 향후 필요한 필드를 추가할 수 있음
MemberDto가 object이고, 이것을 Result 클래스를 이용해서 data로 한 번 감싸준 형태로 출력됨

@RestController
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
...
@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.size(), collect);
}
@Data
@AllArgsConstructor
static class Result<T> {
private int count;
private T data;
}
Result 클래스로 collection을 감싸서 반환하는 방식을 사용함으로써 원하는 필드를 쉽게 추가할 수 있게 됨