티스토리 뷰
V1
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member){
//@RequestBody request로 받은 json 데이터를 객체에 넣어준다.
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
entity
package jpabook2.jpashop2.domain;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
public class Member {
@Id @GeneratedValue
@Column(name="member_id")
private Long id;
@NotEmpty
private String name;
@Embedded // jpa 내장타입 일 수 있다는 표시
private Address address;
@OneToMany(mappedBy = "member") //하나의 member에서 여러개의 order이다. mappedBy를 연관관계의 주인을 지정한다. 주인인 order 클래스 쪽은 그대로 두고 아래의 무엇으로 지정이 되는지 필드명을 적어준다. 이곳의 값을 변경한다고 해서 fk값이 변경되지 않는다.
private List<Order> orders = new ArrayList<>();
}
다음과 같은 코드는 문제점이 있다.
- api를 만드는 팀에서 name이 아닌 username으로 변경할 경우 데이터를 받지 못한다. (그럴 일이 있을까 싶지만 그런 일이 있다고한다.
- name을 NotEmpty valid하며 사용하는데, 만약 같은 객체를 여기저기서 사용할 때에 어떤 상황에서는 NotEmpty를 사용하지 않을 수도 있다.
- 여러가지 가입의 경우 (소셜, 일반 등)에 하나의 Entity로 받을 경우 위험하다. 하나로 감당할 수 없다.
V2
@PostMapping("/api/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);
}
@Data
static class CreateMemberRequest{
private String name;
}
@Data
static class CreateMemberResponse{
private Long id;
public CreateMemberResponse(Long id) {
this.id = id;
}
}
V2의 장점
- api 스펙은 바뀌지 않는다
- username으로 바꿀 경우 어차피 개발단에서 에러가 나기에 (setName이 에러날 것이다) 바로 인지할 수 있고 바꿀 수 있다.
- entity는 필요없는 address 등등 많은 값을 받는 것처럼 되어있다. api 스펙을 알기 힘들다. 하지만 CreateMemberRequest값을 보고 name만 받는 것으로 스펙을 바로 알 수가 있다. (유지보수 시에 얘만 보면되는구나, 라고 생각할 수 있다)
업데이트
- controller
@PutMapping("/api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2(
@PathVariable("id") Long id
, @RequestBody @Valid UpdateMemberRequest request){
memberService.update(id, request.getName());
Member findMember = memberService.findOne(id); //유지보수를 위해 커맨드와 쿼리를 분리한다.
return new UpdateMemberResponse(findMember.getId(),findMember.getName());
}
@Data
@AllArgsConstructor
static class UpdateMemberResponse{
private Long id;
private String name;
}
@Data
static class UpdateMemberRequest{
private Long id;
private String name;
}
- service
@Transactional
public void update(Long id, String name) {
Member member = memberRepository.findOne(id);
member.setName(name);
//member를 반환해도 되지만, 커맨드와 쿼리를 철저히 분리하는 편이다.(김영한) id 정도만 따로 보낸다거나,
}
Controller부분을 살펴보면,findMember가 아닌 service에서 update 후 member를 리턴하면 같은 객체가 되지만, 이럴 경우 트랙잭션안에 있던(쿼리를 날리는 엔티티)를 그대로 반환하게 된다. 김영한 개발자님은 그 둘을 철저히 분리하여 나중에 나올 이슈를 방지하는 편이라한다. 내 생각에도 같은 엔티티를 보내게 되면 나중에 유지보수시에 여러 문제가 발생할 불안감이 있기도 하다.
조회
V1
@GetMapping("/api/v1/members")
public List<Member> membersV1() {
return memberService.findMembers();
}
간단하게 이렇게 작성하면 문제가 있다.
- 이렇게 엔티티 그대로 받게 되면 필요없는 orders객체가 반환된다. (@JsonIgnore 어노테이션으로 반환시키지 않을 수 있다. 하지만 다른 api에서는 필요하다면..?)
- name이 username으로 변경되면 또 오류가 있을 것이다.
- 여기에 count를 추가해달라고 할 경우 entity가 깨져버린다.
V2
@GetMapping("/api/v2/members")
public Result memberV2(){
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;
}
@Data
@AllArgsConstructor
static class MemberDto{
private String name;
}
이렇게 DTO를 만들어 반환된 findMembers의 값을 모두 넣고 result 객체로 묶어 반환하면 위의 문제점이 모두 해소된다.
'dev > spring JPA 활용 웹만들기' 카테고리의 다른 글
JPA api (3) (성능최적화 2) (toOne) (조회시 DTO 변환) (0) | 2024.03.02 |
---|---|
JPA API (2) (성능최적화 1)(toOne)(엔티티로 반환..근데 이건 쓰지말자) (0) | 2024.03.02 |
경로 표현식 (0) | 2024.02.21 |
페이징 API (0) | 2024.02.20 |
프로젝션(SELECT) (0) | 2024.02.19 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 리터럴
- 로그인
- Intercepter
- JPA
- react실행
- BindingResult
- 인터셉터
- 향해플러스백엔드
- ArgumentResolver
- 항해플러스
- 컨트
- 백엔드 개발자 역량
- thymleaf
- 스프링부트
- reject
- 예외처리
- React
- hypertexttransferprotocol
- 항해99
- 스프링공부
- rejectValue
- 향해99
- 백엔드 개발자 공부
- 향해플러스
- jpa api
- Java
- filter
- exception
- HTTP
- SpringBoot
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
글 보관함