[Spring] 트랜잭션 전파속성, @Transactional

[verify$y]·2025년 5월 30일

Spring

목록 보기
12/16

💡 Spring 핵심 개념 인터뷰 Q&A


전파속성이란

  • 트랜잭션이 이미 시작된 상태에서 또 다른 트랜잭션을 호출할 때 어떻게 처리할지를 정의
  • 기존 트랜잭션을 계속 사용할지, 새로운 트랜잭션을 시작할지, 아니면 트랜잭션 없이 실행할지를 결정하는 속성
  • 이는 @Transactional(propagation = Propagation.XXX)로 지정할 수 있으며, 기본값은 REQUIRED
  • 총 7가지가 존재하지만, 주로 REQUIRED, REQUIRES_NEW 두 가지만 자주 사용





핵심 2가지 전파 속성


1. REQUIRED (default)

  • 어떤 메서드가 @Transactional(propagation = REQUIRED)로 선언되어 있을 때
  • 그 메서드를 호출한 쪽에서 이미 트랜잭션을 시작한 상태라면, 이 메서드는 새로운 트랜잭션을 만들지 않고, 그 기존 트랜잭션에 참여합니다. 즉, 호출자와 피호출자가 같은 트랜잭션을 공유한다는 뜻
  • 부모 트랜잭션의 범위 내에서 함께 커밋되거나 롤백된다. 부모 메서드(호출자)의 트랜잭션이 끝날 때, 해당 트랜잭션에 포함된 모든 작업이 함께 커밋되거나, 도중에 예외가 발생하면 함께 롤백된다는 의미

정리

  • 같은 트랜잭션으로 묶여서, 하나라도 실패하면 전체 취소(rollback), 전부 성공해야만 커밋(commit)된다.


적절한 예시 : REQUIRED , 카페에서 커피 주문하고 결제하는 상황

상황:
1. 고객이 ‘커피 주문, 포인트 적립’ 을 한 번에 요청
2. ‘주문 처리 서비스’ → ‘포인트 적립 서비스’를 내부에서 호출

설명 :

  • 이런 구조에서는 포인트 적립 중 오류가 나면 커피 주문조차 취소되는 일괄처리 방식이다.
  • orderCoffee() 와 savePointHistory() 두 메서드에서 하는 DB작업이 한 덩어리 작업처럼 묶여서 같은 트랜잭션 안에서 처리된다는 뜻
  • 함께 성공하거나, 함께 실패


코드 :

@Transactional  // 트랜잭션 A 시작, 주문 처리 서비스 (부모)
public void orderCoffee() { 
    saveOrder();             // 커피 주문 저장
    savePointHistory();      // 포인트 적립 호출 (아래)
    // 트랜잭션 A 끝에서 commit 또는 rollback
}

@Transactional(propagation = REQUIRED) // 포인트 적립 서비스 (자식)
public void savePointHistory() {
    // 트랜잭션 A에 그대로 참여함
}

시점동작트랜잭션 상태
1orderCoffee() 호출 → 트랜잭션 A 시작트랜잭션 A 시작됨
2커피 주문 저장 (INSERT orders...)트랜잭션 A 안에서 실행
3savePointHistory() 호출트랜잭션 A를 그대로 사용
4포인트 적립 저장 (INSERT points...)여전히 트랜잭션 A 안
5(성공)예외 발생 없음 → 트랜잭션 A 커밋두 작업 모두 반영됨
5’(실패)도중 예외 발생 → 트랜잭션 A 롤백두 작업 모두 취소됨



"둘 다 트랜잭션 A 하나로 묶인다” 의미

  • 두 메서드(orderCoffee, savePointHistory)가 실행하면서 하나의 트랜잭션을 공유하고,
  • 그 트랜잭션이 끝날 때함께 커밋되거나 함께 롤백된다는 뜻입니다.



@Transactional(propagation = REQUIRED) - JDBC Connection 차원에서의 동작

  • Spring에서 트랜잭션은 내부적으로 JDBC Connection 객체 하나를 공유 해서 구현됩니다.
  • 즉, 로 트랜잭션을 묶으면, 두 메서드 모두 같은 Connection 객체를 사용하게 된다.
  • 전파 속성을 통해 두 메서드가 하나의 트랜잭션에 묶이면,Spring은 같은 Connection 객체를 재사용해서 동일한 트랜잭션 범위로 처리한다.
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false); // 트랜잭션 시작

try {
    // SQL 실행 1
    // SQL 실행 2
    conn.commit(); // 둘 다 성공하면 반영
} catch (Exception e) {
    conn.rollback(); // 하나라도 실패하면 전체 취소
}
  • 이 작업을 Spring이 해줌
  • Spring이 기존 트랜잭션을 감지 → 그 트랜잭션의 JDBC Connection을 그대로 재사용



구체적인 설명 : REQUIRED vs REQUIRES_NEW

전파속성설명
REQUIREDSpring이 기존 트랜잭션을 감지 → 그 트랜잭션의 JDBC Connection을 그대로 재사용
REQUIRES_NEW기존 트랜잭션을 중단(pause)하고, 새 Connection을 요청해서 별도 트랜잭션 수행







2. REQUIRES_NEW

  • 항상 새로운 트랜잭션을 생성합니다.
  • 현재 진행 중인 트랜잭션이 있다면 일시 중단되고,자체적으로 커밋/롤백되는 별도 트랜잭션으로 동작합니다.
  • 항상 새 트랜잭션으로 실행. 기존 트랜잭션과 완전히 분리



REQUIRES_NEW의 JDBC 레벨 동작이 REQUIRED와 어떻게 다를까

  • REQUIRES_NEW는항상 새로운 트랜잭션을 생성 하며, 기존 트랜잭션은 일시 정지(pause)되고, 새로운 JDBC Connection 객체를 새로 열어서 독립적으로 커밋/롤백을 수행합니다.


JDBC 관점에서 REQUIRES_NEW

// 외부 트랜잭션 A
Connection connA = dataSource.getConnection();
connA.setAutoCommit(false); // 트랜잭션 A 시작

// 내부 메서드에서 REQUIRES_NEW
Connection connB = dataSource.getConnection();
connB.setAutoCommit(false); // 트랜잭션 B 시작 (별도 커넥션)

// 트랜잭션 B 작업
try {
    // SQL 실행 (connB)
    connB.commit(); // 내부 작업 커밋
} catch (Exception e) {
    connB.rollback(); // 내부 작업만 롤백
}

// 외부 트랜잭션 계속 진행
try {
    // SQL 실행 (connA)
    connA.commit(); // 외부 트랜잭션 커밋
} catch (Exception e) {
    connA.rollback(); // 외부 트랜잭션 롤백
}


Spring 내부 동작 : REQUIRES_NEW

  • Spring은 트랜잭션 매니저 (PlatformTransactionManager)를 통해 ThreadLocal 기반으로 현재 트랜잭션 상태를 관리하고,
  • REQUIRES_NEW는 기존 트랜잭션의 상태를 일시적으로 suspend한 뒤,
  • 새 트랜잭션 정의와 함께 새로운 커넥션을 얻어 실행합니다.






사용 목적 중심 비교 : REQUIRED vs REQUIRES_NEW


  • REQUIRED는 전체 작업을 하나의 트랜잭션으로 묶어 함께 성공하거나 함께 실패하도록 할 때 사용합니다.

  • REQUIRES_NEW는 주 트랜잭션과는 별개로, 반드시 따로 성공하거나 실패하게 만들고 싶은 작업에 사용합니다



REQUIRED

  • 가장 기본이 되는 전파 속성으로, 하나의 작업 단위를 트랜잭션으로 묶고자 할 때 사용합니다.
  • 예를 들어, "주문 생성 → 결제 처리 → 포인트 적립"과 같이 여러 단계의 비즈니스 로직이 논리적으로 하나의 작업이라면, 각 단계가 전부 성공했을 때만 실제로 DB에 반영되어야 합니다.
  • 이때 모든 메서드에 REQUIRED를 사용하면, 하나의 트랜잭션으로 묶여 전체가 함께 커밋되거나 롤백됩니다
  • 즉, 모든 작업이 성공해야만 DB에 반영되는 일괄 처리(atomic)가 목적입니다.


REQUIRES_NEW

  • 부모 트랜잭션과는 별도로 독립적인 트랜잭션으로 처리해야 할 작업이 있을 때 사용합니다.
  • 대표적인 예는 로그 저장, 이메일 발송, 외부 연동 결과 기록 같은 부가 작업입니다.
  • 예를 들어 주문 처리 중 오류가 나서 롤백되더라도, 그 사실을 기록한 로그는 롤백되지 않고 반드시 저장되어야 하는 경우가 있습니다.
  • 이때 로그 저장 로직에 REQUIRES_NEW를 지정하면, 주문 트랜잭션이 롤백되더라도 로그 저장은 별도 트랜잭션으로 커밋되므로 안전하게 남습니다.
  • 따라서 REQUIRES_NEW의 사용 목적은 주작업과는 분리된 독립적인 작업을 실패 없이 보존하는 데 있습니다.















profile
welcome

0개의 댓글