
Repository save() 메소드의 반환값
- Spring Boot에서 Repository의 save() 메소드는 항상 엔티티를 반환해야 한다.
- 이는 서비스 계층에서 비즈니스 로직을 수행하기 위해 엔티티를 직접 조작해야 하기 때문이다.
Repository의 핵심 역할
- Repository는 데이터베이스와 직접 소통하는 계층
- Repository는 애플리케이션과 데이터베이스(DB) 사이의 연결고리 역할을 한다.
- 엔티티를 저장하고, 조회하고, 삭제하는 역할을 담당하며, JPA나 MyBatis 같은 기술을 통해 DB와 직접 연결됨.
Todo savedTodo = todoRepository.save(todo);
- save() 호출 후 반환된 객체는 DB에 저장된 엔티티이며, 보통 ID 값이 자동 생성됨.
- 따라서 서비스 계층에서 이 엔티티를 기반으로 추가적인 로직을 수행할 수 있음.
=> save() 메소드는 엔티티를 반환해야 함
Repository의 반환값이 무조건 엔티티여야 하는 이유
1) 저장 후 변경된 엔티티를 사용하기 위함
- JPA에서는 save() 호출 시 새로운 엔티티가 생성되거나(INSERT), 기존 엔티티가 업데이트(MERGE)됨.
- 따라서 save() 메서드의 반환값을 사용해야 실제 DB에 반영된 최신 상태의 엔티티를 사용할 수 있음.
Todo todo = new Todo(null, "Learn Spring Boot", false);
Todo savedTodo = todoRepository.save(todo);
System.out.println(savedTodo.getId());
- ID가 null이었지만, DB에 저장된 후 save()가 새로운 ID가 부여된 엔티티를 반환.
- 만약 save()가 아무것도 반환하지 않는다면, DB에 저장된 최신 정보를 확인할 방법이 없음.
2) 서비스 계층에서 엔티티를 활용해야 함
- 서비스 계층에서 Repository의 반환값을 이용하여 추가적인 비즈니스 로직을 수행해야 함.
public TodoResponseDTO createTodo(CreateTodoRequestDTO dto) {
Todo todo = new Todo(null, dto.getTitle(), false);
Todo savedTodo = todoRepository.save(todo);
return new TodoResponseDTO(savedTodo.getId(), savedTodo.getTitle(), savedTodo.isCompleted());
}
- 만약 save()가 엔티티를 반환하지 않고 단순히 저장만 한다면,
- 서비스에서 DB에 저장된 엔티티의 정보를 알 수 없어 추가적인 비즈니스 로직을 적용하는 것이 어려워짐.
3) DB와의 일관성을 유지하기 위함
- JPA에서는 영속성 컨텍스트(Persistence Context)를 사용하여 엔티티를 관리함.
- save() 메서드가 반환한 엔티티는 DB와 동기화된 최신 엔티티임.
@Transactional
public void updateTodo(Long id, String title) {
Todo todo = todoRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Todo not found"));
todo.setTitle(title);
}
- save() 메소드가 엔티티를 반환하지 않는다면, JPA의 변경 감지 기능(Dirty Checking)을 사용할 수 없음.
- JPA의 동작 방식과 일관성을 유지하기 위해서라도 Repository는 엔티티를 반환해야 함.
그렇다면 UUID를 반환해도 될까?
- 일부 경우에는 UUID를 반환하여 DB에 저장된 엔티티를 식별할 수도 있음.
- 하지만 UUID만 반환하면 서비스 계층에서 추가적인 DB 조회가 필요함.
public UUID createTodo(CreateTodoRequestDTO dto) {
Todo todo = new Todo(UUID.randomUUID(), dto.getTitle(), false);
todoRepository.save(todo);
return todo.getId();
}
- UUID만 반환하면, 이후 서비스 계층에서 다시 findById()로 엔티티를 조회해야 함.
- 비효율적이므로 가능하면 save()에서 엔티티를 반환하는 것이 좋음.
Service의 create() 메소드의 반환값은?
- C-S-R 패턴에서는 엔티티를 반환해도 괜찮지만, 핵심은 엔티티를 생성/수정하는 역할은 서비스단에서만 지켜져야함
- 클린아키텍처 관점에서는 service 입장에서도 엔티티를 반환하고싶지 않아서, 서비스용 ResultDTO를 따로 만들고 감싸서 반환
- 하지만, 현업에서는 편의상 엔티티를 반환하는 경우도 많다.
- 클린 아키텍처의 엄격한 원칙을 따르는 경우는 일부이고,
- 현업에서는 개발 속도와 유지보수성을 고려하여 엔티티를 반환하는 경우도 많다.
- 특히, 서비스 로직이 간단할 경우, 엔티티를 반환해도 큰 문제는 발생하지 않음.
- 클린아키텍처의 경우, 엔티티 자체를 수정하는 것은 도메인 엔티티 영역에서 이뤄져야 하고, 엔티티를 생성해서 이런저런 로직을 적용하는 것은 useCase(service) 영역에서 이뤄져야한다.
결론
- Repository는 DB와 직접 소통하는 계층이므로, save() 메서드는 반드시 엔티티를 반환해야 한다.
- 엔티티를 반환해야 최신 상태를 유지할 수 있으며, 비즈니스 로직을 추가하는 것이 가능하다.
- UUID를 반환할 수도 있지만, 비효율적이므로 가급적이면 엔티티를 반환하는 것이 좋다.
=> 즉, Repository의 save()는 엔티티를 저장하고 반환하는 것이 원칙이며, 이를 통해 서비스 계층이 안정적으로 동작할 수 있도록 한다!