save()와 saveAndFlush(), 무엇이 다를까?
스프링 데이터(Spring Data)에서 엔티티(Entity)를 저장할 때 사용할 수 있는 대표적인 메서드로 save()와 saveAndFlush()가 있습니다. 두 메서드 모두 엔티티를 저장하는 기능을 수행하지만, 내부적으로 동작 방식에 중요한 차이가 있습니다.
핵심 차이는 데이터베이스 동기화 시점입니다. save()는 변경 사항을 즉시 데이터베이스에 반영하지 않을 수 있으며, 영속성 컨텍스트(Persistence Context)에만 저장됩니다. 반면 saveAndFlush()는 호출 시점에 모델 상태와 데이터베이스의 동기화를 강제로 수행하여, 변경 사항을 즉시 DB에 반영(flush)합니다.
핵심 차이점 비교표
| 번호 | 구분 | save() | saveAndFlush() |
|---|---|---|---|
| 1 | 소속 리포지토리 | CrudRepository에 속함 | JpaRepository에 속함 |
| 2 | 플러시(Flush) 전략 | 명시적으로 flush 또는 commit 메서드를 호출하기 전까지는 데이터를 DB에 직접 반영하지 않음 | 호출 시점에 곧바로 데이터를 DB에 플러시함 |
| 3 | 대량 저장(Bulk Save) | CrudRepository가 대량 저장 메서드(saveAll 등)를 제공 | 대량(bulk) 연산은 지원하지 않음 |
| 4 | 저장 후 데이터 가시성 | DB에 직접 반영되지 않으므로, 트랜잭션 내에서 명시적으로 commit()을 호출하지 않으면 트랜잭션 외부에서 변경 사항이 보이지 않음 | 트랜잭션 외부에서도 변경 사항이 바로 보임 |
| 5 | 사용 사례 | 동일한 트랜잭션 내에서 이후 시점에 저장된 변경 사항을 사용할 필요가 없을 때 적합 | 동일한 트랜잭션 내에서 이후 시점에 저장된 변경 사항을 사용해야 할 때 적합 |
코드 예시로 이해하기
두 메서드의 실제 사용 예시는 다음과 같습니다.
save() 사용 예:
// CrudRepository 인터페이스에 정의<br>User savedUser = userRepository.save(user);</code>saveAndFlush() 사용 예:
// JpaRepository 인터페이스에 정의<br>User savedUser = userRepository.saveAndFlush(user);</code>언제 어떤 메서드를 사용해야 할까?
save()를 선택해야 하는 경우: 단순히 엔티티를 저장만 하면 되고, 같은 트랜잭션 안에서 해당 변경 사항을 즉시 조회하거나 활용할 필요가 없다면 save()를 사용하는 것이 좋습니다. 트랜잭션이 종료될 때 자동으로 플러시되기 때문에 대부분의 일반적인 상황에서 충분합니다.
saveAndFlush()를 선택해야 하는 경우: 같은 트랜잭션 내에서 저장 직후 네이티브 쿼리(native query)로 데이터를 조회하거나, DB 제약 조건 위반 등을 즉시 확인해야 하는 경우에는 saveAndFlush()를 사용해야 합니다. 플러시가 강제로 실행되어 변경 사항이 곧바로 DB에 반영되기 때문입니다.
정리
두 메서드 모두 엔티티 저장이라는 본질적인 역할은 동일합니다. 다만 save()는 유연하고 범용적인 저장 방식이며, saveAndFlush()는 즉각적인 DB 동기화가 필요한 특수한 상황에서 유용합니다. 불필요한 플러시는 성능 저하를 유발할 수 있으므로, 특별한 이유가 없다면 기본적으로 save()를 사용하는 것이 권장됩니다.