티스토리 뷰
JPA 프로젝트에서 update는 어떤 방식으로 하는 게 좋을까?
JPA 프로젝트를 하면서 가장 많이 쓰게 되는 기능 중 하나가 update입니다. 개발을 하다 "어떤 방식이 더 좋은 걸까" 하는 고민이 생겼습니다. 이글에서는 그 부분을 정리해보려고 합니다.
제가 고민하는 두 가지 방식
1. JPA의 장점인 Dirty Checking(변경 감지) 활용해서 엔티티의 필드를 직접 수정하는 방법
2. 새로운 객체를 만들어 교체하는 방법
두 가지 방법 모두 장단점이 있다고 GPT가 대답해 줍니다. 이번 글에서는 각각 어떤 특징이 있는지 그리고 실제로 언제 쓰면 좋을지 제 생각을 정리해 보겠습니다.
먼저 변경 감지를 활용해 필드를 직접 수정하는 방법입니다.
@Transactional
public void updateDepartment(Long departmentId, String name) {
Department department = departmentRepository.findById(departmentId).orElseThrow(() -> new CoreException(ErrorType.DEPARTMENT_NOT_FOUND, departmentId));
if (departmentRepository.findByName(name).isPresent()) {
throw new CoreException(ErrorType.DEPARTMENT_ALREADY_EXISTS, name);
}
department.update(name);
}
// entity/Department.java
public void update(String name) {
this.name = name;
}
DB에서 조회한 엔티티 객체의 상태를 변경하면 JPA가 변경을 감지하고 트랜잭션 종료될 때 자동으로 update를 실행해 DB에 반영합니다. save() 메서드를 별도로 호출할 필요 없이 코드가 간결하게 가능하고 JPA의 핵심 기능을 활용했습니다. 하지만 데이터 변경이 save() 호출 없이 마법처럼 일어나는 것처럼 보일 수 있습니다. 특히 복잡한 로직에서 변경 흐름을 추적하기 어려울 수 있습니다.
두 번째 방법인 새 객체 생성 후 저장 하는 방법입니다.
@Transactional
public void updateDepartment(Long departmentId, String name) {
Department department = departmentRepository.findById(departmentId)
.orElseThrow(() -> new CoreException(ErrorType.DEPARTMENT_NOT_FOUND, departmentId));
if (departmentRepository.findByName(name).isPresent()) {
throw new CoreException(ErrorType.DEPARTMENT_ALREADY_EXISTS, name);
}
Department changedDepartment = department.update(name);
departmentRepository.save(changedDepartment);
}
// entity/Department.java
public Department update(String name) {
return new Department(this.departmentId, name);
}
불변성을 지향하는 방식으로 update 할 데이터를 담은 새로운 엔티티 객체를 생성하고 save() 메서드를 명시적으로 호출하여 의도가 명확하고 직관적입니다. 하지만 새로운 객체를 매번 생성해야 하고 코드의 양이 변경 감지보다 약간 더 많아집니다.
고민과 결론
- 상황에 따라 다르게 적용하자
두 가지 방식의 장단점이 있기에 절대적으로 좋다고 단정 지을 수 없을 거 같습니다. 하지만 어떤 방식이 더 올바른 선택일지 고민했습니다. 그리고 과거에 잔액 충전, 사용 등 민감한 데이터를 다루는 는 기능을 개발했을 때 경험을 생각났습니다. 당시 저는 새 객체를 생성한 후 저장하는 방식을 선택했습니다. 그 이유는 잔액 데이터가 매우 중요하고 민감한 정보이고 단 하나의 오류도 허용해서는 안된다고 생각했기 때문입니다. 그래서 예측이 가능하고 명확해야 한다고 생각했습니다.
JPA의 더티 체킹 방식은 코드가 간결하지만 잔액처럼 중요한 데이터의 변경이 마법처럼 일어나는 듯한 느낌을 줄 수 있습니다 반면 새로운 객체를 생성하고 명시적으로 save()를 호출하는 방식은 데이터 변경의 흐름을 투명하게 볼 수 있고 직관적이라고 생각됩니다.
결론적으로 업데이트하려는 데이터의 성격에 따라 적절한 전략을 선택하는 것이 가장 현명하다고 생각됩니다. 일반적인 엔티티 부서명, 사용자 프로필 정보와 같은 일반적인 엔티티는 간결하고 생산성을 높일 수 있는 변경 감지를 사용하고 포인트, 재고, 잔액처럼 민감한 도메인 객체를 다를 때는 새로운 객체를 생성하고 명시적으로 저장하는 방식을 적용하는 것으로 제 생각을 정리하고 싶습니다.
- Total
- Today
- Yesterday
- If
- Python
- counter
- isdigit
- Lambda
- zip
- index
- find
- Method
- permutations
- bool
- function
- operators
- for
- combinations
- isalpha
- Built-in Functions
- Upper
- Lower
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |