BackEnd/Spring Boot

트랜잭션 설정 및 아주아주 간단한 팁

Raconer 2024. 2. 3. 23:40
728x90

트랜잭션 설정 방법


1. @Transactional 어노테이션 사용

import org.springframework.transaction.annotation.Transactional
  • 가장 일반적인 트랜잭션 선언 방식
  • AOP 기반 Proxy 방식으로 동작
  • 선언 위치: 클래스 또는 메서드 단위

주의사항

  • Proxy 방식으로 동작
    • @Transactional프록시 객체(대리 객체) 를 생성하여 트랜잭션을 적용합니다.
    • 내부에서 자기 자신의 메서드 호출 시 프록시를 우회하게 되어 트랜잭션이 동작하지 않음
    • 즉, 같은 클래스 내부에서 @Transactional 메서드를 호출하면 트랜잭션 적용되지 않음
// 잘못된 예시: 내부 메서드 호출로 트랜잭션이 적용되지 않음
class MemberService {

    fun create(member: Member): Member {
        return getMember(member) // 트랜잭션 적용 안 됨
    }

    @Transactional
    fun getMember(member: Member): Member {
        val saved = repository.save(member)
        val error = 1 / 0 // 예외 발생, rollback 기대
        return saved
    }
}

해결 방법

  • 해당 메서드를 외부에서 호출되도록 구조 변경
  • 또는 getMember()다른 클래스로 분리해서 별도의 빈으로 만들고 호출

2. TransactionTemplate 사용

val result = transactionTemplate.execute {
    // 트랜잭션 내 로직
    repository.save(...)
    ...
}
  • 명시적 트랜잭션 제어
  • @Transactional보다 정교한 제어 가능
  • 실무에서는 잘 사용하지 않음 (복잡한 경우에 한함)
  • execute를 하여 트랜잭션 처리를 하지만 잘 사용하지 않는다.

3. MySQL 직접 쿼리에서 트랜잭션 사용

START TRANSACTION;

-- 트랜잭션에 포함할 쿼리
UPDATE users SET balance = balance - 100 WHERE id = 1;
UPDATE users SET balance = balance + 100 WHERE id = 2;

COMMIT;
  • 롤백 시: ROLLBACK;
  • JDBC 또는 MyBatis에서 수동 트랜잭션 제어 가능

트랜잭션 사용 시 주의사항

  • 트랜잭션 범위는 최대한 짧게
  • 대량 데이터 트랜잭션은 성능 저하 및 락 문제 유발 가능
  • 서비스 계층에서 비즈니스 단위로 선언

참고

  • @Transactional의 기본 전파 속성: REQUIRED
  • 롤백 조건: 기본적으로 RuntimeException, Error에서만 롤백됨
    • @Transactional(rollbackFor = [Exception::class]) 등으로 조정 가능
  • 프록시 기반이므로 내부 메서드 호출 시 주의 필요
728x90