본문 바로가기
개발

비동기 PDF 합성에서 트랜잭션 전파 실패 보완하기

by sudong 2025. 8. 5.

들어가며

온라인으로 동의서를 작성하고 작성한 결과를 pdf로 저장하는 기능을 개발했습니다.

pdf를 만들어내는 로직을 @Async를 사용해서 비동기 처리해 성능을 개선했었습니다.

최근 Transaction 공부하면서 트랜잭션 전파와 롤백에 대해서 개선 한 경험을 공유합니다.


1) FacadeService 계층을 활용한 Transcation 롤백 개선

동의서 저장을 하면 1) 항목별 동의 여부 2) 동의서 메타 데이터(동의서 타입, 동의서 사인 파일번호 등등) 크게 2가지 정보가 저장됩니다.

기존에는 controller에서 서비스 저장 로직을 각각 호출을 했습니다.

@PostMapping()
public ConsentClauseAgreementResponse save(){
    consentClauseAgreementService.saveConsentClauseAgreement();
    consentFormAgreementService.save();
}

하지만 이렇게 하니깐 하나의 transcation으로 묶여있지 않아서 한 트랜잭션이 롤백되었을 때, 다른 트랜잭션 롤백이 이뤄지지 않았습니다.

이를 개선하기 위해서 FacadeService계층을 두고, 같은 트랜잭션으로 묶어주는 작업을 진행했습니다. 


2) 비동기 작업시 Transcation 전파 실패 보완

consentFormAgreementService.save(); 안에서 pdf 합성 로직이 비동기로 이뤄집니다.

비동기 작업의 경우 Thread풀에서 Thread를 새로 할당받아서 실행하게 됩니다. 이 때, DB커넥션 정보가 ThreadLocal에 저장되는데, 이로 인해서 각 Thread마다 트랜잭션이 독립적으로 실행되게 됩니다.

이로 인해서 부모 트랜잭션에서 롤백이 일어나더라도 자식 트랜잭션에서 롤백이 일어나지 않게 됩니다.

이를 보완하기 위해서 스프링 이벤트를 통해서 실행 이벤트를 발행하고,

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)

를 사용해서 이벤트 발행한 Transcation이 커밋된 후에 비동기 작업을 시작하도록 했습니다.

이를 통해서 기존에 동의서 저장 로직에서 문제가 생겨도 pdf합성 후 저장하는 로직이 실행되던 리소스를 아낄 수 있었습니다.

 


보상 트랜잭션을 통한 부모 쓰레드 롤백

위 로직에서 아직 해결하지 못한 문제점이 있습니다.

자식 스레드가 비동기 작업 중에 에러 발생시에 부모에게 전파가 안된다는 점입니다

이는 실패에 대한 보상 트랜젝션 개념으로 접근해야합니다.

보상트랜잭션을 검색하면 대표적인 방법으로 Two Phase Commit, SAGA 패턴이 나오지만, 분산 환경이아니기 때문에 간단하게 예외처리에서 보상 트랜잭션을 열어서 실행시켜주면 된다.

 

나는 3번 리트라이 후에도 실패시에 보상트랜잭션을 실행하는 것으로 로직을 개선했다.