배경
지난 글에서 트랜잭션에 대해 조금 알아보는 시간을 가졌습니다. 그때 spring의 트랜잭션은 @Transactional을 통해 간단히 연결하고 종료가 가능하다고 했는데요, 하지만 간단한 만큼 언제 써야되고 어디까지 영향을 미치는지 알아야 나중에 문제가 생기지 않겠죠? 오늘은 이 트랜잭션이 어디까지 전파되는지 알아보도록 합시다.
내용
spring에서 트랜잭션 전파는 속성에 뭘 주느냐에 따라 달라지게 됩니다.
이렇게 default 타입으로 REQUIRED 타입이 존재합니다. 다른 전파 타입은 어떤 것이 있을까요?
이렇게 다양한 타입들이 존재하지만 오늘은 required와 requires_new에 대해 알아봅시다.
기존의 트랜잭션이 진행중일 때 새로운 트랜잭션이 시작되면 외부 트랜잭션과 내부 트랜잭션으로 구분짓습니다. 스프링에서는 이걸 하나의 트랜잭션으로 묶어주게 되는데요, 이를 그림으로 보면 아래와 같습니다.
이를 다른 말로 표현하게 되면 다음과 같이 됩니다.
여기서 물리 트랜잭션은 실제 데이터베이스에 적용되는 트랜잭션을 말합니다. 실제 connection을 통해 트랜잭션을 시작하고 종료하는 단위가 되죠. 그렇다면 논리 트랜잭션 내에서 커밋과 롤백을 사용하면 어떻게 될까요? 하나의 논리 트랜잭션이라도 롤백을 하게 된다면, 물리 트랜잭션은 롤백이 되게 됩니다. 커밋의 경우 모든 트랜잭션이 커밋이 되어야 물리 트랜잭션의 커밋이 완료가 되죠.
여기서 중요한 점이 하나 있는데, 신규 트랜잭션만이 물리 트랜잭션을 종료(커밋, 롤백) 할 수 있습니다. 이게 무슨 뜻일까요?
예를 들어서 영화 예매를 하는 서비스가 있다고 합니다. 영화 Service쪽에 @Transactional을 걸어주고 영화 예매 Repository, 로그 저장 Repository에 각각 @Transactional 걸어서 실행하게 되면 어떻게 될까요? 서비스에서 레포지토리로 흐름이 이어진다고 했을 때, 각각의 트랜잭션이 모두 신규 트랜잭션일까요?
위 그림을 보게 되면 처음에 생성된 트랜잭션은 default타입으로 신규로 생성이 된 것이 보입니다. 이후 영화 예매 트랜잭션, 로그 저장 트랜잭션 시작 메시지를 보시면 신규가 아닌 참여로 하게 되죠. 커밋을 보게 되면 로그 저장 트랜잭션 커밋 -> 영화 예매 트랜잭션 커밋 -> 물리 트랜잭션 커밋이 된 것을 보실 수 있습니다. 물리 트랜잭션 내 모든 논리 트랜잭션이 커밋 되어야 물리 트랜잭션도 커밋이 됩니다. 그렇다면 중간에 예외가 발생하면 어떻게 될까요?
위 그림처럼 로그 저장부분에서 예외가 발생했습니다. 이 예외는 서비스쪽으로 돌아가 물리 롤백을 시키게 됩니다. 이렇게 되면 영화 예매의 데이터도 롤백, 로그 저장 데이터도 롤백이 되는거죠.
만약에 로그 저장에 실패해도 영화 예매는 성공적으로 요청을 처리하고 싶으면 어떻게 해야할까요? 이때 사용하는 전파 타입이 바로 REQUIRES_NEW입니다.
이렇게 로그 저장 레포지토리에 @Transactional(propagation = PropagationType.REQUIRES_NEW)로 지정하게 되면 트랜잭션 참여가 되는 것이 아닌 새로운 트랜잭션이 생기면서 따로 관리하게 됩니다. 로그 저장에서 예외가 발생해 롤백이 되어도, 영화 예매와 영화 서비스에서 성공적으로 커밋이 되면 위쪽의 트랜잭션은 정상적으로 커밋이 되는거죠.
하지만 이렇게 새로 트랜잭션을 계속 만들게 되면 DB와의 connection 수도 증가하게 됩니다. 성능이 떨어지는건 당연하겠죠? 언제 새로운 트랜잭션을 써야 할지는 비즈니스 요구사항에 맞게 잘 맞춰서 해야 될 것 같습니다.
'개발 > Spring Boot' 카테고리의 다른 글
MapStruct의 이해 (2) | 2025.01.03 |
---|---|
스프링 @Transactional (1) | 2024.12.02 |