트랜잭션 상태를 한눈에! AOP 활용 롤백 테스트 구현기

유수민·2025년 8월 8일
0

📌 개요

트랜잭션은 데이터의 일관성을 보장하기 위한 핵심 메커니즘이다.특히 복잡한 비즈니스 로직에서 여러 서비스가 얽혀 있을 때, 어느 한 단계라도 실패하면 전체 작업이 롤백되어야 한다.

이번 글에서는 스프링 트랜잭션 롤백이 잘 동작하는지 테스트하는 방법과
AOP를 활용해 트랜잭션 상태(롤백 여부, 예외 발생 여부)를 실시간으로 추적하는 방법을 주문 기능과 함께 이야기해보려고 한다.

사실 난 지금까지 부끄럽지만 롤백 테스트를 postman을 이용한 실테스트로 진행해왔다. 이번에는 롤백 테스트도 테스트 코드로 해낼 수 있지 않을까?에서 부터 시작된 이야기이다.

📌 트랜잭션 롤백 테스트의 필요성

예를 들어, 주문 생성 시 재고 차감과 결제 포인트 차감이 동시에 이루어진다.
만약 재고 차감에서 예외가 발생하면, 결제 역시 취소되어야 한다.

이를 보장하기 위해서는 전체 주문 프로세스가 하나의 트랜잭션 내에서 실행되어야 하며,한 부분이라도 실패하면 전체가 롤백되어야 한다.

📌 필요한 구성 요소

1. 트랜잭션 상태 정보 저장용 객체 (TransactionTraceInfo)

트랜잭션 실행 중 발생한 상태(어떤 메서드에서, 어떤 인자와 함께 실행됐고, 롤백 상태인지, 예외가 있었는지)를 한 곳에 모아 구조화된 데이터로 관리하기 위해 필요하다.

  • 메서드 이름과 인자를 기록해 어떤 트랜잭션인지 추적 가능
  • 롤백 여부를 알면 트랜잭션 성공/실패를 판단할 수 있음
  • 예외 발생 여부로 문제 발생 원인 분석 가능

2. 저장소(TransactionTraceHolder)

트랜잭션 상태 정보를 스레드 로컬(ThreadLocal) 에 안전하게 저장하고 꺼내는 저장소 역할이다. 동시에 여러 요청이 병렬로 처리되는 서버 환경에서, 각 요청별로 트랜잭션 상태를 독립적으로 관리하기 위해 필요하다.
ThreadLocal은 각 스레드마다 독립된 저장 공간을 제공하기 때문에, 여러 스레드가 동시에 접근해도 데이터가 섞이지 않는다.

  • ThreadLocal을 이용해 스레드별 독립 저장소 제공
  • 여러 스레드가 하나의 전역 변수나 공유 객체를 건드리는 것을 방지해 데이터 충돌, 혼선 방지
  • 테스트나 로그에서 각 트랜잭션의 상태를 정확히 조회하기 위한 안정적이고 안전한 상태 보관소 역할
  • 요청 처리가 끝나면 clear()로 상태 제거해 메모리 누수 및 정보 오염 방지

3. AOP 처리기 (TransactionTraceAspect)

Spring의 AOP(Aspect-Oriented Programming)를 사용해 @Transactional이 붙은 메서드 호출 시점에 개입하여 트랜잭션 상태(rollback 여부, 예외 발생 여부 등)를 추적하고 기록하는 클래스가 필요하다. 즉, 트랜잭션 동작을 감시하는 관찰자 역할을 하는 TransactionTraceAspect를 만들어냈다.
이를 통해, 트랜잭션 내부에서 발생한 상태(성공, 실패, 롤백 여부)를 코드 변경 없이도 획득할 수 있어 테스트나 로깅등을 할 수 있다.

  • 트랜잭션 동작을 관찰하는 역할
  • 별도 코드 수정 없이 트랜잭션 상태 정보 획득 가능
  • 테스트 및 로깅에 활용 가능

📌 트랜잭션 롤백 테스트와 상태 추적 AOP의 원리

  1. 스프링 트랜잭션 관리 원리
    스프링은 AOP를 구현하기 위해 프록시 패턴을 사용합니다. @Transactional이 붙은 클래스에 대해 동적 프록시를 생성하고, 이 프록시를 통해 메서드 호출을 가로채 트랜잭션 경계를 설정한다.
  • 스프링은 @Transactional이 붙은 클래스에 대해 프록시 객체를 생성한다.
  • 클라이언트가 실제 객체의 메서드를 호출하면, 스프링은 프록시 객체를 통해 호출을 가로챈다.
  • 프록시는 트랜잭션을 시작하고, 실제 객체의 메서드를 실행한다.
  • 메서드가 정상 종료되면 프록시는 트랜잭션을 커밋한다.
  • 메서드 실행 중 예외가 발생하면 프록시는 트랜잭션을 롤백하여 데이터 일관성을 보장한다.
  1. 트랜잭션 롤백 테스트 원리
  • 테스트는 @Transactional이 적용된 최상위 메서드를 대상으로 한다.
  • 이때 모킹 프레임워크(Mockito 등)를 사용하여 내부 서비스 메서드에 강제로 예외를 발생시킨다.
  • 최상위 메서드는 이 예외를 감지하고, 스프링의 트랜잭션 관리자에 의해 롤백이 유도된다.
  • 테스트는 이 롤백이 실제로 발생했는지 검증하여 트랜잭션이 올바르게 동작하는지 확인한다.
  1. 트랜잭션 상태 추적을 위한 AOP 원리
  • AOP(Aspect Oriented Programming)를 이용해 @Transactional 메서드 실행 시점 전후에 추가 로직을 삽입한다.
  • 메서드 실행 전, AOP는 ThreadLocal 기반 저장소(TransactionTraceHolder)에 트랜잭션 추적 정보를 기록한다.
  • 메서드 실행 중 예외가 발생하면 AOP는 이를 감지하고, 트랜잭션 상태(TransactionAspectSupport.currentTransactionStatus())를 읽어와 롤백 여부, 예외 발생 여부를 추적 정보에 업데이트한다.
  • 이 상태 정보는 테스트 코드나 로깅 시스템에서 참조할 수 있어, 트랜잭션의 동작을 정확히 파악할 수 있다.

테스트 진행


롤백 테스트 중 재고차감 실패시 전체 트랜잭션이 롤백되는지의 여부를 확인하는 테스트이다.

핵심 메커니즘

  • TransactionAspectSupport.currentTransactionStatus()를 호출해 현재 스프링 트랜잭션의 상태를 얻는다.
  • isRollbackOnly 값이 true면, 해당 트랜잭션은 롤백 대기 상태임을 의미한다.
  • @Around 어드바이스는 예외 발생 여부를 감지하고, 메서드 실행 결과와 함께 상태 정보를 저장한다.

소감

처음에는 트랜잭션 롤백 테스트가 복잡하고 어려운 주제로 느껴졌다.특히 여러 서비스가 엮여 있고, 동시에 발생하는 예외 상황까지 고려해야 하다 보니 더욱 그랬다.
하지만 이번에 직접 코드를 작성하고, AOP로 트랜잭션 상태를 추적하는 방식을 적용해보면서
스프링이 제공하는 트랜잭션 관리의 강력함과 유연함을 다시 한번 체감할 수 있었다.
테스트 코드로 롤백 동작을 검증하니, 수작업 테스트 대비 훨씬 신뢰도가 높고 반복 가능하다는 점도 큰 장점이었고, 이 덕분에 서비스 안정성을 높이는 데 큰 도움이 될 것이라 생각한다.
또한, AOP를 활용해 트랜잭션 상태 정보를 실시간으로 수집하고 기록하는 구조는
로깅이나 모니터링, 문제 발생 시 원인 분석에도 매우 유용하다는 점에서
운영 환경에서 장애 대응에 큰 힘이 될 것이라는 생각이 들었다.

profile
배우는 것이 즐겁다!

0개의 댓글