예외와 트랜잭션 커밋, 롤백
트랜잭션에서 예외가 발생하면 항상 롤백될까요?
우리는 흔히 try-catch 블록 안에서 데이터베이스 작업을 수행하고, 예외가 발생하면 롤백되겠지… 하고 생각합니다. 하지만 Spring에서는 반드시 그렇지 않습니다.
체크 예외는 롤백되지 않는다?
Spring의 트랜잭션 처리 기본 정책은 다음과 같습니다:
- Unchecked 예외 (RuntimeException, Error 등) → 자동 롤백
- Checked 예외 (Exception 상속, RuntimeException 제외) → 자동 롤백 X
즉, 개발자가 명시적으로 롤백을 선언하지 않으면, SQLException 같은 예외가 발생해도 트랜잭션이 커밋될 수 있습니다.
@Transactional
public void saveData() throws IOException {
// I/O 오류 발생 시 checked exception이므로 자동 롤백되지 않음
throw new IOException("디스크 I/O 에러");
}
위 코드는 실제로는 커밋될 수 있습니다.
의도한 롤백이 일어나지 않는 거죠.
🤔 왜 이런 정책이 있을까?
이건 Java의 설계 철학과 관련이 있습니다.
Checked Exception은 회복 가능한 예외를 의미하기 때문에, 비즈니스적으로 처리할 수도 있다는 전제가 깔려 있습니다.
하지만 대부분의 실무에서는 예외가 발생하면 롤백되어야 하므로, 많은 개발자는 이런 기본 정책을 모르고 "롤백 안 됐어요" 버그에 당황하게 됩니다.
💡 그래서 어떻게 해야 할까?
1. @Transactional에 rollbackFor 명시
@Transactional(rollbackFor = IOException.class)
public void saveData() throws IOException {
throw new IOException("디스크 오류");
}
2. 가능한 한 RuntimeException 사용
@Transactional
public void saveData() {
throw new CustomBusinessException("중복된 예약입니다.");
}
3. 또는 직접 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 호출
예외가 발생했는데도 데이터가 저장되어 있던 적이 있나요?
👉 예외는 발생했지만 커밋은 되었다 → 대부분 위 정책을 이해하지 못했을 때 생기는 버그입니다.
특히 catch 블록에서 예외를 삼키고 아무런 조치를 하지 않으면 커밋되어버릴 수 있습니다.
@Transactional
public void save() {
try {
repository.save(...);
} catch (Exception e) {
log.warn("예외 발생: {}", e.getMessage());
}
// 예외는 처리했지만, rollback이 선언되지 않아 commit됨
}
마무리 정리
- 커밋은 되돌릴 수 없는 확정 명령이다.
- 롤백은 커밋 이전까지만 가능하다.
- Spring에서는 예외 종류에 따라 자동 롤백 여부가 다르다.
- 무조건 롤백되길 원한다면 rollbackFor를 명시하거나, 커스텀 런타임 예외를 활용하자.
'Dev Framework > Spring' 카테고리의 다른 글
[Spring] 스프링 데이터 JPA Auditing 완전 정복 (0) | 2025.04.28 |
---|---|
[Spring] Hibernate에서 FROM 절 서브쿼리를 만들 수 없는 이유 (0) | 2025.04.28 |
[Spring] MessageConverter (0) | 2025.04.07 |
[Spring] @Configuration과 CGLIB 프록시 마법 (0) | 2025.04.01 |
[Spring] HttpServlet 완벽 정리 (0) | 2025.03.31 |