| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 |
- jqx워터마크제거
- 스프링 파일
- 분산파일시스템
- 자동 빌드 안될 때
- set-cookie 안만들어짐
- 비개인화추천모델
- jsp permission denied
- 존폴결혼예물
- 존폴반지
- 추천시스템steam
- spring 이미지 파일
- 이클립스 자동 빌드
- staem algorithm
- 분산파일
- dialtimeout
- tomcat튜닝
- jqxWidget워터마크
- 존폴결혼반지
- 존폴쥬얼리
- LLM기반 콘텐츠 추천
- steam game score
- Akamai 연구결과
- dial timeout
- 트랜잭션전파옵션
- LLM기반CBF
- jsp 예외 permission
- cookie refreshToke
- 파일 여러개 받기
- 동시 요청 처리
- 스팀 게임 스코어 알고리즘
- Today
- Total
개발 계발
[ Spring ] 왜 트랜잭션 전파가 필요할까? 본문
Spring은 트랜잭션 전파 옵션을 제공한다.
1. 트랜잭션 전파란 ?
말 그대로 트랜잭션을 전파하는 것이다. 부모 트랜잭션에 참여할지, 별개의 트랜잭션으로 실행할지 전파 옵션에 따라 선택할 수 있다.
1-1. 그렇다면 왜 트랜잭션 전파가 필요할까?
하나의 로직에서 여러개의 트랜잭션이 있는 경우 만약 문제가 생겨 롤백 해야할 때, 비즈니스 규칙에 맞춰 설정된 트랜잭션 전파 옵션에 따라서 롤백이 될 트랜잭션을 선택 할 수 있다. 정리하자면 트랜잭션 전파가 필요한 이유는 비즈니스 규칙에 맞춰 유연하게 롤백을 할 수 있기 때문이다.
1-2. 그렇다면 트랜잭션에서 롤백은 언제 일어날까?
기본적으로 언체크드 예외가 발생 시 롤백이 되고, 체크드 예외 발생 시에는 롤백되지 않는다.
이 부분도 비즈니스 규칙에 따라서 언체크드 시에 롤백되지 않도록 또는 체크드 예외 발생 시 롤백 되도록 설정 할 수 있다.
예) - 체크드 예외 시 롤백 되게 설정 @Transactional(rollbackFor = CustomException.class)
- 언체드 예외 시 롤백 되지 않게 설정 @Transactional(noRollbackFor = CustomRuntimeException.class)
2. 트랜잭션 전파 옵션
REQUIRED
- 부모 트랜잭션에 참여하게 됨, 이 말은 곧 커넥션을 같이 쓰게 된다는 것
- 자식에서 언체크드 예외 발생 시 자식 롤백,부모에서 try-catch로 자식 예외를 잡아도 롤백 됨, 왜냐하면 connection은 하나이기 때문
- 부모 트랜잭션 롤백 시, 자식 트랜잭션도 롤백
REQUIRES_NEW
- 부모 트랜잭션에 참여하지 않음, 자신의 트랜잭션에서 따로 실행
- 자식에서 언체크드 예외 발생 시 자식 롤백, 부모에서 try-catch로 자식 예외를 잡은 경우 롤백 되지 않음,
- 부모 트랜잭션 롤백이 되어도 자식은 커밋
NESTED
- 부모 트랜잭션에 참여하게 됨, 세이브 포인트라는 것을 만듦
- 자식에서 언체크드 예외 발생 시 자식 롤백, 부모에서 try-catch로 자식 예외를 잡은 경우 롤백 되지 않음 왜냐하면 자식에서 롤백 될 시 세이브 포인트로 돌아가기 때문
- 부모 트랜잭션 롤백 시, 자식 트랜잭션도 롤백
3. 주의사항
- 트랜잭션이 붙은 메서드가 다른 트랜잭션이 붙은 메서드에 종속된 경우 트랜잭션 전파는 작동하지 않음
아래와 같은 비즈니스 규칙을 만들어 달라는 요청이 있다고 하자.
1. 주문을 생성하고, 로그를 저장하는 기능
2. 주문 요청 로그 저장은 주문 생성이 되든 안되든 저장되야 함
간단하게 요청 사항대로 코드를 아래와 같이 만들었다.
의도는 주문 생성 시 로그도 저장되게 하되, 로그 저장 트랜잭션 전파 옵셥은 REQUIRES_NEW로 두어서 만약 주문 생성이 안되더라도 주문 요청 로그는 남도록 하려는 것이다.
@Service
public class OrderService {
@Transactional // [부모] 트랜잭션 시작 (REQUIRED)
public void createOrder() {
System.out.println("createOrder() 실행");
this.saveLog();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
System.out.println("원본 saveLog() 실행");
}
}
@PostMapping
public void createOrder(){
orderService.createOrder();
}
}
이 경우에 의도대로 작동하지 않는다. 이유는 아래와 같다.
1. 컨트롤러가 호출하는 'orderService'는 실제 원본 객체가 아닌, 스프링이 생성한 프록시 객체
2. 프록시 객체가 요청을 먼저 받아 트랜잭션을 시작한 뒤, 실제 원본 객체의 createOrder() 메서드로 실행을 위임
3. 원본 객체 내부에서 'this.saveLog()'를 실행할 때는 프록시 객체를 거치지 않고 원본 객체 자바의 메모리 주소(this)로 다이렉트 점프
4. 원본 객체 저장되어 있는 saveLog()는 그냥 메서드 일 뿐이기에, 부모 트랜잭션(createOrder)에 그대로 흡수되어 실행
메모리 상에는 원본 객체는 사실 이게 트랜잭션인지 뭔지 모른다. 그냥 메서드만 알고 있을 뿐이다.
즉, 트랜잭션이 동작하기 위해서는 프록시 객체에 요청을 보내야하는 것이다.
그렇다면 어떻게 해야할까?
아래와 같이 saveLog()는 아예 다른 서비스로 빼서 주입하면 된다.
@Service
public class OrderService {
private final OrderLogService orderLogService;
@Transactional
public void createOrder() {
System.out.println("createOrder() 실행");
orderLogService.saveLog();
}
}
@Service
public class OrderLogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
System.out.println("원본 saveLog() 실행");
}
}
@PostMapping
public void createOrder(){
orderService.createOrder();
orderService.saveLog();
}
}
- 느낀점
중요한건 전파 옵션이라는 지식을 안다는 것을 넘어서, 비즈니스 요구사항에 맞춰 전파옵션을 구성하는 것이다. 기술을 아는 것도 중요하고 비즈니스 요구사항에 맞춰 기술을 구현해내는 것도 중요함을 깨달았다. 요즘 같이 AI로 지식을 쉽게 찾는 시대에 개발자는 그 기술을 어떻게 비즈니스 요구사항에 맞춰 풀어내는가가 중요한 것 아닌가 싶다.
'프레임워크 > 스프링' 카테고리의 다른 글
| [ Spring ] 브라우저에서 보낸 파일 받는 과정 (0) | 2026.07.29 |
|---|---|
| [ Test ] 제대로 익히는 Spring Boot Test 방법 #3. 통합 테스트 (0) | 2025.07.15 |
| [ Test ] 제대로 익히는 Spring Boot 테스트 방법 #2. 각 계층 단위 테스트 또는 슬라이스 테스트 (4) | 2025.07.10 |
| [ Test ] 제대로 익히는 Spring Boot 테스트 방법 #1. 기본 (1) | 2025.07.07 |
| [ 문제 해결 ] XLSTransformer java.lang.ClassNotFoundException 과 NoClassDefFoundError 발생 시(feat. ChatGpt도 한계가 있음을) (0) | 2025.06.23 |
