“김영한 강사님의 JPA 활용편 2- API 개발과 성능 최적화"를 듣고 간단하게 정리하기”
- @RestController는 기존 @Controller와 @ResponseBody가 합쳐진 형태이다.
- @RequestBody는 JSON으로 들어온 데이터를 받은 객체로 매핑해서 변환해준다.
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
- @Valid는 javax.validation을 검증한다.
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
@NotEmpty // javax.validation
private String name;
...
}
- 위의 예제는 프레젠테이션 계층 (화면 - Controller)에서 들어온 데이터에 대한 검증 로직이 엔티티계층에 있는 경우이다.
- 이러한 경우 API 별로 name에 대해 요구사항이 다른 경우(A는 name이 empty 가능, B는 불가능) 문제가 발생한다.
- 또한 엔티티 컬럼을 변경하는 경우 API에 대한 스펙이 바뀌어버린다.
- API 요청 스펙에 맞춰서 별도의 DTO를 만들어서 사용하고 검증해야한다. → 엔티티를 외부에 노출하지 말자! 엔티티 계층과 프레젠테이션 계층을 구분하자!
@Data
static class CreateMemberRequest{
@NotEmpty
private Stringname;
}
@Data
static class CreateMemberResponse{
private Longid;
public CreateMemberResponse(Long id){
this.id= id;
}
}
- DTO를 사용해서 데이터를 요청받고 응답한다.
@PostMapping("ap1/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);
}
- 엔티티의 수정은 변경감지를 사용한다. 예시는 setter를 사용하지만 setter가 아닌 의미있는 이름을 가진 메서드를 엔티티 계층에 넣어 생성하자!
@Transactional
public void update(Long id, String name) {
Member member = memberRepository.findMember(id);
member.setName(name);
}
- 커맨드와 쿼리를 분리한다. Update는 엔티티의 데이터를 변경하는 커맨드 성 메서드, 조회는 쿼리성 메서드이다.
@PutMapping("api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2(@PathVariable("id") Long id,
@RequestBody @Valid UpdateMemberRequest request) {
// update는 커맨드 메서드
memberService.update(id, request.getName());
// 조회는 쿼리 메서드
Member member = memberService.findOne(id);
return new UpdateMemberResponse(id, member.getName());
}
- 엔티티를 직접 노출하는 경우 연관된 모든 데이터가 함께 노출된다. 엔티티에 @JsonIgnore를 사용해 제외할 수 도 있다.
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
- 하지만 이 방법 역시 API 별로 요구사항이 다르기 때문에 엔티티에 녹이는 경우 문제가 발생할 수 있다.
- 그리고 엔티티 데이터 이외에 다른 데이터도 함께 보내달라고 요구사항이 들어오는 경우 엔티티를 반환하면 응답값 자체를 바꿀 수가 없다.
- 결국 DTO를 이용해서 처리하는 것이 정답이다!
@GetMapping("api/v2/members")
public Result memberV2() {
List<Member> findMembers = memberService.findMembers();
List<MemberDto> collect = findMembers.stream()
.map(member -> new MemberDto(member.getName()))
.collect(Collectors.toList());
return new Result(collect);
}
@Data
@AllArgsConstructor
static class Result<T> {
private T data;
}
반응형
'Backend > Spring Boot' 카테고리의 다른 글
[API 고급] 컬렉션 조회 최적화 (0) | 2022.07.12 |
---|---|
[API 개발 고급] 지연 로딩과 조회 성능 최적화 (0) | 2022.07.07 |
도메인 개발 팁 간단 정리 (0) | 2022.07.05 |
RestTemplate (0) | 2022.07.01 |
Converter를 이용해 URI에 Enum 타입 매핑하기 (0) | 2022.06.26 |