✏️ 발단
- 지난번 객체를 생성할 때 연관관계에 있는 List 속성이 null 로 생성되는 문제를 해결한 후 발생한 문제이다.
- 생성한 객체를 수정하면 특정 부분만 수정되는 것이 아닌 기존 객체에 수정한 객체가 덮어- 씌워지는 문제를 확인했다.
- 문제를 해결하고 알게된 사실이지만
Builder 는 객체를 수정하는 기능이 아니고 new 로 무조건 생성하는 생성자같은 기능이였다.
📍 Service 계층 - 수정 로직
- 아래 코드에서는 content 속성과 수정된 날짜 속성만 수정하고있다.
- 즉, 생성 날짜는 처음 생성했을 때와 동일한 값이 유지되어야 하지만,
null 값으로 초기화가 되고있었다.
public void modify(Comment comment, String content) {
Comment comment1 = Comment.builder()
.id(comment.getId())
.content(content)
.modifyDate(LocalDateTime.now())
.build();
this.commentRepository.save(comment1);
}
✏️ 문제 원인
📍 builder 로 객체 수정시 id 값을 특정했기 때문일 경우
- id 값을 특정했기 때문에
@Id
어노테이션에 의해 기존에 있던 data 가 수정되지 않고, 삭제가 된다고 생각했다.
- 즉, 기존 data 가 유실되고 수정이 아닌 덮어쓰기가 된다고 생각했다.
✏️ 문제 해결
📍 1차 시도 - 실패
- 변수로 받은 객체에 다시 builder 를 사용하는 방식으로 수정했다.
public void modify(Comment comment, String content) {
comment = Comment.builder()
.content(content)
.modifyDate(LocalDateTime.now())
.build();
this.commentRepository.save(comment1);
}
- 이렇게 실행해보니 수정으로 작동되는것이 아닌 새로운 객체가 새로운 id 값을 부여받고 생성이 되어버렸다.
- 해당 build 로직 자체에 new 라는 속성이 생략되어있어 변수로 받은 comment 에 수정되는 것이 아닌 새로운 객체가 생성되 입력되 작동되고 있다.
📍 2차 시도 - 실패
- 이번엔 save 로직을 제거하고 변경감지를 사용하는 방식을 생각해봤다.
- 하지만 지금 로직 제체가 수정 로직이 아닌 new 로 새롭게 객체를 생성하는 로직이기 때문에
당연히 변경감지가 일어나지 않았다.
public void modify(Comment comment, String content) {
comment = Comment.builder()
.content(content)
.modifyDate(LocalDateTime.now())
.build();
}
📍 3차 시도 - 실패
- 변경감지를 사용하는 방법이 정답이라 생각해 gpt 에 @builder 의 변경감지 사용법을 물어봤다.
- ToBuiler 라는 mehod 를 사용해 문제를 해결할 수 있다고 답변받았다.
- 하지만 ToBuilder 라는 method 는 존재하지 않았다.
- 어노테이션 객체까지 들어가서 찾아봤지만 비슷한 의미의 mehod 조차 발견해지 못했음
📍 4차 시도 - 실패
- 계속해서 gpt 와 구글링을 통해 정보를 수집하던중 merge 라는 method 를 발견했다.
- 로직을 살펴보면 기존 객체를 수정할 때 갑이 존재하면 유지, 수정되면 덮어쓰기 방식으로 작동된다는 걸 알 수 있다.
- 사실 변경감지는 아니고 덮어쓰는 방식이지만 결과만 같다면 이 방식으로 문제를 해결할 수 있다고 생각했다.
return this.toBuilder()
.name(other.getName() != null ? other.getName() : this.getName())
.age(other.getAge() > 0 ? other.getAge() : this.getAge())
.address(other.getAddress() != null ? other.getAddress() : this.getAddress())
.build();
- merge 를 적용하기위해서 method 를 찾아봤지만 merge 로 정의 된 mehod 는 물론, 비슷한 로직의 method 또한 @builder 에는 준비되어있지 않았다.
- 수정기능은 아주 많이 사용되는 로직이기 때문에 이런 로직을 절대로 직접 만들어 사용하지 안을거라고 생각했고 계속 방법을 찾아봤다.
📍 5차 시도 - 성공
- gpt 와 구글링으로 정보를 찾다
toBuilde
라는 method 를 찾았다.
- 이 method 는 앞서 발견한 merge 와 같은 기능이라는 사실도 알아냈다.
- toBuilder 를 사용해기 위해선 @Builder 어노테이션에 toBuilder =true 라는 속성을 추가해주어야 사용할 수 있었다.
- 지금까지 우리가 수정 method 를 찾지 못했던 이유이다.
@Entity
@Builder(toBuilder = true)
- 이제 Service 계층에서 toBuilder 메서드를 사용할 수 있게 되었다.
- test 결과 같은 id 값에 객체가 새로 생성되므로 이전 객체는 삭제되고 수정된 객체가 덮어쓰기 된걸 확인햇다.
- 참고로 삼항연산자에 의해 수정하지 않은 이전 객체의 data 들은 고스란히 새로운 객체에 입력된다.
public void modify(Comment comment, String contents) {
Comment comment1 = comment.toBuilder()
.content(contents)
.modifyDate(LocalDateTime.now())
.build();
this.commentRepository.save(comment1);
}
@Builder(toBuilder = true)
테스트 내용 공유 감사합니다!