save() 메소드의 반환값

양성준·2025년 3월 13일

스프링

목록 보기
11/49

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()); //DB에 저장된 엔티티의 ID 확인 가능
  • 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); //DB에 저장된 엔티티 반환

    //저장된 엔티티를 이용해 응답 DTO 생성
    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);
    //JPA의 변경 감지(Dirty Checking)로 자동 반영됨
}
  • 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만 반환하면 이후 데이터를 조회해야 하는 문제가 생김
}
  • UUID만 반환하면, 이후 서비스 계층에서 다시 findById()로 엔티티를 조회해야 함.
  • 비효율적이므로 가능하면 save()에서 엔티티를 반환하는 것이 좋음.

Service의 create() 메소드의 반환값은?

  • C-S-R 패턴에서는 엔티티를 반환해도 괜찮지만, 핵심은 엔티티를 생성/수정하는 역할은 서비스단에서만 지켜져야함
  • 클린아키텍처 관점에서는 service 입장에서도 엔티티를 반환하고싶지 않아서, 서비스용 ResultDTO를 따로 만들고 감싸서 반환
  • 하지만, 현업에서는 편의상 엔티티를 반환하는 경우도 많다.
    • 클린 아키텍처의 엄격한 원칙을 따르는 경우는 일부이고,
    • 현업에서는 개발 속도와 유지보수성을 고려하여 엔티티를 반환하는 경우도 많다.
    • 특히, 서비스 로직이 간단할 경우, 엔티티를 반환해도 큰 문제는 발생하지 않음.
  • 클린아키텍처의 경우, 엔티티 자체를 수정하는 것은 도메인 엔티티 영역에서 이뤄져야 하고, 엔티티를 생성해서 이런저런 로직을 적용하는 것은 useCase(service) 영역에서 이뤄져야한다.

결론

  • Repository는 DB와 직접 소통하는 계층이므로, save() 메서드는 반드시 엔티티를 반환해야 한다.
  • 엔티티를 반환해야 최신 상태를 유지할 수 있으며, 비즈니스 로직을 추가하는 것이 가능하다.
  • UUID를 반환할 수도 있지만, 비효율적이므로 가급적이면 엔티티를 반환하는 것이 좋다.

=> 즉, Repository의 save()는 엔티티를 저장하고 반환하는 것이 원칙이며, 이를 통해 서비스 계층이 안정적으로 동작할 수 있도록 한다!

profile
백엔드 개발자

0개의 댓글