Backend/SpringBoot 이론 부분

SpringBoot 에서 트랜잭션

Dean83 2025. 10. 1. 11:44

보통 서비스에 @Transactional 어노테이션을 이용하여 DB 에서 데이터를 가져오든, 변경하든 한다. 

비즈니스 로직상, 하나의 기능을 구현함에 있어 DB 접근을 하여 처리하는 단위를 트랜잭션이라고 한다. 

즉, 여러 DB 조작을 한 비즈니스 로직안 에서 수행할때 사용한다. 반대로 말하면, 단건일 경우는 @Transactional을 쓰지 않아도 된다.

@Transactional 어노테이션이 적용된 빈은, 프록시(https://dean83.tistory.com/342)를 생성하여 트랜잭션을 관리한다.

  • 클래스 어노테이션으로 붙이는 경우
    • 모든 public 메소드에 반영된다.
  • 메소드 어노테이션으로 붙이는 경우
    • 해당 메소드전용으로 트랜잭션이 반영된다.
  • 클래스 어노테이션 < 메소드 어노테이션 이므로, 클래스 어노테이션에서 이용하고, 특정 메소드 전용 트랜잭션을 정의할 수 있다.

 

옵션값

  • @Transactional( 옵션) 을 통해 옵션 설정이 가능하다
    • propagation
      • 이미 실행중인 트랜잭션이 있을 경우, 새로운 트랜잭션은 어떻게 처리할지 에 대한 옵션
        • REQUIRED (기본): 기존 트랜잭션 있으면 합류, 없으면 새로 시작
        • REQUIRES_NEW: 무조건 새로운 트랜잭션 시작 (기존 트랜잭션은 일시 중단)
          • 하나의 트랜잭션 중, 로깅을 별도로 할때, 알람 전송 등 단독으로 쓸때 사용. 단, try catch를 통해 실패시 원래 트랜잭션에 영향이 가지 않도록 해야 함.
        • SUPPORTS: 트랜잭션 있으면 합류, 없으면 그냥 트랜잭션 없이 실행
        • MANDATORY: 무조건 기존 트랜잭션 필요 (없으면 예외 발생)
        • NOT_SUPPORTED: 트랜잭션 없이 실행 (기존 트랜잭션은 중단됨)
        • NEVER: 트랜잭션 있으면 예외 발생
        • NESTED: 기존 트랜잭션 안에서 “중첩 트랜잭션” 시작 (DB savepoint 이용)
    • isolation
      • 여러 트랜잭션이 동시 실행될때 DB에서 데이터를 어떻게 격리할지 설정하는 옵션 
        • DEFAULT : DB 기본 격리 수준 사용 (기본값)
        • READ_UNCOMMITTED : 커밋되지 않은 데이터도 읽음 (Dirty Read 허용) → 위험
        • READ_COMMITTED : 커밋된 데이터만 읽음 (일반적인 DB에서 설정된 기본값)
        • REPEATABLE_READ : 같은 트랜잭션 안에서는 같은 결과를 보장 (Non-repeatable read 방지)
        • SERIALIZABLE : 가장 엄격, 완전 직렬화 (성능 저하 가능)
          • 금융거래, 결제처리, 티케팅 등의 경우에 사용
    • timeout
      • 기본값은 -1로 무제한 이다. 타임아웃 되면 롤백 한다.
    • readonly
      • true로 설정시, 읽기전용으로 최적화 된다.

 

코드 블록 내에서 트랜잭션을 관리하고 싶을 경우, 코딩을 통해 제어할 수 있다. 이 경우, 커밋, 롤백 등을 별도로 호출해야 하는데, 잘 쓰지는 않는다. 

 

롤백 규칙

기본적으로 트랜잭션 도중 오류가 발생 할 경우,  RuntimeException 및 이를 상속하는 Exception이 발생할 경우 롤백이 되며, 상위 부모 개념인 Exception 중 RuntimeException과 관련없는 오류 발생시에는 롤백이 되지 않는다. 즉, 커스텀 Exception 생성시 RuntimeException 을 상속하지 않은 경우는 롤백되지 않는다. 쉽게말해 Checked Exception은 롤백 안함, Unchecked Exception은 롤백 한다. (https://dean83.tistory.com/346)

첨부파일을 클라이언트로  부터 받았고, 해당 파일을 S3에 올리다가 오류가 발생하는 등의 경우에 롤백을 위해 쓸 수 있을거 같다. 

 

@Transaction의 옵션으로, rollBackFor 과 noRollBackFor 를 이용해서 커스텀하게 설정 할 수 있다. 

  • rollBackFor
    • 롤백이 되지 않는 exception도 롤백 하고 싶을때 설정
  • noRollbackFor
    • 롤백이 되는 exception을 롤백 안하고 싶을때 설정
  @Transactional(
        rollbackFor = 클래스명.class,
        noRollbackFor = 클래스명.class
    )

 

내부 메서드를 호출 할 경우 발생하는 문제

  • 내부 메서드 호출 문제 (self invocation) 예를 보면
@Service
public class TestService
{
	....
    //transactional 메소드가 아님
    public Entity create(...)
    {
    	...비스니스 로직 수행
        return this.createToDB(...);
    }
    
    @Transactional
    public Entity createToDB(...)
    {
    	...
    }
    
    ...
}
  • transactional 이 아닌 메서드에서 몇몇 비즈니스 로직 처리 -> transactional 이 있는 내부 메서드 호출을 통해 DB 접근 하려는 경우 발생한다.
    • create 메소드는 트랜잭션이 아니므로 프록시를 이용하지 않는다.
    • create 내부에서 createDB를 호출 하더라도, 이미 프록시가 아니기 때문에 트랜잭션이 동작하지 않는다. 
  • 따라서, 이 경우를 방지 하기 위해서 Service 클래스를 분리하여, 트랜잭션을 이용하는 메서드, 그렇지 않은 메서드로 나눠야 한다.
    • 이 경우, 트랜잭션을 이용하지 않는 서비스에서 트랜잭션을 이용 하는 Service 주입시 프록시가 주입되어 트랜잭션이 동작하게 된다.
  • ** 기타 방법으로는, 자기 자신을 인젝션 받는 방법 (단, lazy 인젝션을 받아야 함), 또는 AopContext.currentProxy 를 이용하여 프록시에 있는 메소드를 호출 하는 방법이 있으나, 둘 다 권장되지는 않는다. (한마디로 억지로 프록시를 이용하여 동작하는 방법)

 

 

'Backend > SpringBoot 이론 부분' 카테고리의 다른 글

Filter 및 SecurityFilterChain  (0) 2025.12.15
Spring boot의 요청 처리 흐름  (0) 2025.12.15
Entity 심화  (0) 2025.09.30
Proxy  (0) 2025.09.30
Spring Data JPA  (0) 2025.09.29