- 데이터베이스에서 작업의 논리적 단위를 말함
- ACID 원칙을 만족해야 함:
- Atomicity (원자성)
- Consistency (일관성)
- Isolation (격리성)
- Durability (지속성)
@Transactional 어노테이션으로 선언적 트랜잭션 제어
- 트랜잭션 경계는 프록시 기반 AOP로 설정됨
@Transactional
public void doSomething() {
// 이 메서드는 트랜잭션 내에서 실행됨
}
- 하나의 트랜잭션 안에서 다른 트랜잭션과 어떻게 연관되는지 결정하는 전략
| 속성 |
설명 |
REQUIRED (기본값) |
기존 트랜잭션이 있으면 참여, 없으면 새로 시작 |
REQUIRES_NEW |
기존 트랜잭션을 중단하고 새로운 트랜잭션 시작 |
NESTED |
기존 트랜잭션에 중첩되어 실행 (savepoint 사용) |
SUPPORTS |
트랜잭션이 있으면 참여, 없으면 트랜잭션 없이 실행 |
NOT_SUPPORTED |
트랜잭션 없이 실행 |
MANDATORY |
반드시 트랜잭션 내에서 실행 (없으면 예외) |
NEVER |
트랜잭션 있으면 예외 발생 |
- 현재 트랜잭션이 중단(suspended) 되고, 새로운 트랜잭션이 생성되어 독립적으로 실행됨
- 이전 트랜잭션의 rollback이나 commit 여부에 영향을 받지 않음
- 로그, 알림 등 부수 작업은 반드시 저장되어야 하는 경우
- 실패해도 본 트랜잭션에 영향주지 말아야 하는 경우
- 비즈니스 로직 분리 시 독립된 트랜잭션 단위가 필요한 경우
@Service
@RequiredArgsConstructor
public class SettlementService {
private final LogService logService;
@Transactional
public void processSettlement() {
// 메인 트랜잭션
saveMainData();
// 로그는 별도 트랜잭션으로 처리
logService.writeLog("정산 처리 시작");
}
private void saveMainData() {
// DB에 정산 정보 저장
}
}
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void writeLog(String message) {
// 별도의 트랜잭션에서 로그 기록
logRepository.save(new Log(message));
}
}
processSettlement() 실행 → 트랜잭션 A 시작
saveMainData() → A 안에서 실행
writeLog() 호출 → 트랜잭션 A 중단, 트랜잭션 B 시작
- 로그 저장 후 → B 커밋
- 다시 트랜잭션 A 복귀
saveMainData()가 실패해도 writeLog()는 커밋됨
- 반대로
writeLog()가 실패해도 메인 트랜잭션은 롤백되지 않음 (단, 예외 전파 여부 설정 주의)
try {
logService.writeLog("처리 로그");
} catch (Exception e) {
// 로그 실패는 무시, 메인 트랜잭션엔 영향 없음
}
REQUIRES_NEW는 별도 커넥션을 사용하므로, 커넥션 풀에 부담이 될 수 있음
- 내부 메서드 호출로는 AOP 프록시가 적용되지 않음 (self-invocation 문제)
- 예외 전파에 따라 상위 트랜잭션도 롤백될 수 있음 →
try-catch로 감싸야 안전
| 옵션 |
설명 |
rollbackFor |
어떤 예외 발생 시 롤백할지 지정 |
noRollbackFor |
특정 예외는 롤백하지 않도록 지정 |
readOnly |
읽기 전용 트랜잭션 (쓰기 시 예외 발생 가능) |
timeout |
트랜잭션 제한 시간 설정 |
REQUIRES_NEW는 트랜잭션 분리가 필요할 때 강력한 도구
- 하지만 남용 시 커넥션 낭비 및 성능 이슈 유발 가능
- 로깅, 이력 저장, 독립 실행 단위 등에 유용하게 활용 가능
참고 자료