Spring/JPA
JPA - API 기본
jddng
2022. 3. 21. 19:31
728x90
반응형
API에서는 하나만 기억하면 된다. 파라미터를 받거나 전송할 때 Entity를 사용하지 않고 DTO를 사용해야하는 것을 명심하자.
DTO 사용 시 이점
- 엔티티 대신에 RequestBody와 ResponseBody에 각각 DTO를 매핑한다.
- 엔티티와 프레젠테이션(화면) 계층을 위한 로직을 분리할 수 있다.
- 엔티티와 API 스펙을 명확하게 분리할 수 있다.
- 엔티티가 변해도 API 스펙이 변하지 않는다.
- 실무에서는 엔티티를 API 스펙에 노출하면 안된다.
엔티티로 매핑시 문제점
- 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다
- 엔티티에 API 검증을 위한 로직이 들어간다.(@NotEmpty 등)
- 실무에서는 회원 엔티티를 위한 API가 다양하게 만들어지는데, 한 엔티티에
각각의 API를 위한 모든 요청 요구사항을 담기는 어렵다. - 엔티티가 변경되면 API 스펙이 변한다.
등록 API
등록 DTO
@Data
class CreateMemberResponse {
private Long id;
public CreateMemberResponse(Long id) {
this.id = id;
}
}
@Data
class CreateMemberRequest {
private String name;
}
@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
Member member = new Member(request.getName(), null);
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
/api/v1/members
요청
{
"name" : "hello"
}
응답
{
"id": 3
}
수정 API
- 수정 시 주의할 점은 변경 감지를 사용하여 데이터를 수정해야 한다.(merge 사용 금지)
- PUT과 PATCH, POST의 사용
- PUT : 전체 업데이트를 할 때 사용
- PATCH, POST : 부분 업데이트를 할 때 사용
수정 DTO
@Data
class UpdateMemberRequest {
private String name;
}
@Data
@AllArgsConstructor
class UpdateMemberResponse {
private Long id;
private String name;
}
@PutMapping("/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());
}
/api/v1/members
요청
{
"name" : "change hello"
}
응답
{
"id": 3,
"name": "change hello"
}
조회 API
- 조회 API 역시 DTO를 사용해야 한다.
조회 DTO
@Data
class Result<T> {
private T data;
public Result(T data) {
this.data = data;
}
}
@Data
class MemberDto {
private String name;
public MemberDto(String name) {
this.name = name;
}
}
@GetMapping("/api/v4/members")
public Result membersV4() {
List<Member> findMembers = memberService.findMembers();
//엔티티 -> DTO 변환
List<MemberDto> collect = findMembers.stream()
.map(m -> new MemberDto(m.getName()))
.collect(Collectors.toList());
return new Result(collect);
}
여기서 중요한점은 Result 클래스로 컬렉션을 감싸서 전달한다는 점이다. 그 이유는 컬렉션을 전송하게 되면 배열로 전송이 되는데 배열로 전송이되면 필드를 추가 할 수 없게 된다. 따라서 컬렉션을 전달할 때는 향후 개별 필드를 추가할 수 있도록 클래스로 감싸서 전달하는게 좋다.
/api/v4/members
요청
응답
{
"data": [
{
"name": "user1"
},
{
"name": "user2"
},
{
"name": "change hello"
}
]
}
위 응답에서 보면 클래스로 감싸서 전달하였기 때문에 필드를 자유롭게 추가할 수 있다.
(컬렉션으로 전달된 data를 보면 배열로 전달된것을 볼 수 있다.)
728x90
반응형