코딩마을방범대

[Java] @Transactional 에 대해서 (& 프록시객체) 본문

💡 백엔드/Java

[Java] @Transactional 에 대해서 (& 프록시객체)

신짱구 5세 2023. 8. 4. 11:48
728x90

 

 

@Configuration, XML 기반으로도 트랜잭션을 구성할 수 있지만 이번엔 어노테이션을 이용한 방법으로 알아볼 것이다.

어노테이션 방식은 선언적 트랜잭션이라고 불리고, 선언 시 트랜잭션 기능이 적용된 프록시 객체가 생성된다.

 

 

프록시 객체란 앞서 포스팅했던 '프록시(Proxy)란' 과 비슷한 개념을 갖고 있다.

객체와 DB 사이의 중간 다리 역할을 해주는 또 다른 객체라고 볼 수 있다.

 

만약 사용자가 객체의 get을 이용해 데이터를 조회했을 때, 프록시 객체의 Entity Target 값이 비어있다면

DB를 조회해 Entity 생성 후 프록시 객체의 타겟 값으로 저장한다.

 

이 프록시 객체의 타겟값을 이용해 데이터 조회에 대한 응답을 리턴한다.

(JPA에서 지연로딩을 적용하면 프록시 객체가 생성된다.)

 

 

 

 


 

 

 

 

 

 

 

트랜잭션(Transaction)이란

데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위
( SELECT, INSERT, UPDATE, DELETE )

 

 

트랜잭션의 목적

  • 오류로부터 복구를 허용하고 데이터베이스를 일관성있게 유지하는 안정적인 작업 단위를 제공한다.
  • 동시 접근하는 여러 프로그램 간 격리를 제공한다.

 

 

 

 


 

 

 

Transaction의 ACID

 

 

원자성(Atomicity)

  • 한 트랜잭션 내에서 실행한 작업들은 하나의 단위로 처리
  • 모두 성공하거나 모두 실패 (중간 단위까지 성공하고 실패하는 경우를 방지)

 

일관성(Consistency)

  • 일관성있는 데이터베이스 상태를 유지
    ( 트랜잭션이 성공적으로 완료하면 언제나 동일한 데이터베이스 상태로 유지하는 것을 의미 )

 

독립성(Isolation)

  • 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리
    ( 트랜잭션 밖에 있는 어떤 연산도 중간 단계의 데이터를 볼 수 없음을 의미 )

 

영속성(Durability)

  • 성공적으로 수행된 트래잭션은 영원히 반영(기록)되어야 함을 의미
    ( 데이터베이스 내의 데이터는 트랜잭션의 결과로만 변경되어야 하며, 외부 영향에 의해 변경될 수 없어야 한다. )

 

 

 

 

 

 


 

 

 

 

 

 

@Transactional 어노테이션 사용법

 

트랜잭션은 PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 정상 여부에 따라 Commit/Rollback을 수행한다.

간단하게, 트랜잭션 처리를 JDK Dynamic Proxy 객체에게 대신 위임하여 AOP로 동작하게 된다.

JDK Dynamic Proxy
Interface를 기반으로 Proxy를 생성해주는 방식이다.
직접 생성 시 Java.lang.reflect.Proxy 클래스의 newProxyInstance() 메소드를 이용해 프록시 객체를 생성한다.

 

 

※ 어노테이션을 통해 트랜잭션을 설정해줄 때 아래와 같은 옵션을 설정할 수 있다.

옵션 설명
isolation 트랜잭션에서 일관성없는 데이터 허용 수준을 설정 (격리 수준)
propagation 동작 도중 다른 트랜잭션을 호출할 때, 어떻게 할 것인지 지정하는 옵션 (전파 옵션)
noRollbackFor 특정 예외 발생 시 rollback이 동작하지 않도록 설정
rollbackFor 특정 예외 발생 시 rollback이 동작하도록 설정
timeout 지정한 시간 내에 메소드 수행이 완료되지 않으면 rollback이 동작하도록 설정
readOnly 트랜잭션을 읽기 전용으로 설정

 

 

 

 


 

 

 

isolation

 

일관성 없는 데이터의 부작용

Dirty read 동시 트랜잭션의 커밋되지 않은 변경 내용을 조회하는 상황 (데이터 불일치)
Nonrepeatable read 동시 트랜잭션이 동일한 행을 업데이트하고 커밋하는 경우, 행을 다시 조회할 때 다른 값을 얻는 상황
Phantom read 다른 트랜잭션이 특정 범위의 행을 추가/제거할 경우, 커밋 전/후 조회 결과가 다른 상황
@Transactional(isolation = Isolation.XXX)
public void example(String message) {
	// ...
}

 

※ isolation을 이용해 설정할 수 있는 리스트

DEFAULT - DBMS의 기본 격리 수준 적용
READ_UNCOMMITED (level 0) - 트랜잭션의 동시 액세스 허용 
- 세 가지 동시성 부작용이 모두 발생 (Dirty read, Nonrepeatable read, Phantom read)
- Postgres는 미지원(대신 READ_COMMITED 로 폴백), Oracle은 지원하거나 허용하지 않음
READ_COMMITED (level 1) - Dirty read 방지
- 나머지 부작용은 여전히 발생할 수 있음 (Nonrepeatable read, Phantom read)
- Postgres, SQL Server 및 Oracle의 기본 수준
REPEATEABLE_READ (level 2) - Dirty read, Nonrepeatable read 방지
- 업데이트 손실을 방지하기 위해 필요한 가장 낮은 수준 (동시 액세스를 허용하지 않음)
- Phantom read 부작용은 여전히 발생
- MySQL의 기본 수준, Oracle은 미지원
SERIALIZABLE (level 3) - 가장 높은 격리 수준이지만, 동시 호출을 순차적으로 실행하므로 성능 저하의 우려
- 모든 부작용을 방지

 

 

 

 


 

 

 

propagation

 

@Transactional(propagation = Propagation.XXX)
public void example(String user) { 
	// ... 
}

 

※ propagation을 이용해 설정할 수 있는 리스트

REQUIRED (default) - 활성 트랜잭션이 있는지 확인하고, 아무것도 없으면 새 트랜잭션을 생성
SUPPORTS - 활성 트랜잭션이 있는지 확인하고, 있으면 기존 트랜잭션 사용, 없으면 트랜잭션 없이 실행
MANDATORY - 활성 트랜잭션이 있으면 사용하고, 없으면 예외 발생 
- 독립적으로 트랜잭션을 진행하면 안 되는 경우 사용
NEVER - 활성 트랜잭션이 있으면 예외 발생 
- 트랜잭션을 사용하지 않도록 제어할 경우
NOT_SUPPORTED - 현재 트랜잭션이 존재하면 트랜잭션을 일시 중단한 다음 트랜잭션 없이 비즈니스 로직 실행
REQUIRES_NEW - 현재 트랜잭션이 존재하는 경우, 현재 트랜잭션을 일시 중단하고 새 트랜잭션을 생성
NESTED - 트랜잭션이 존재하는지 확인하고 존재하는 경우 저장점을 표시
- 비즈니스 로직 실행에서 예외가 발생하면 트랜잭션이 이 저장 지점으로 롤백
- 활성 트랜잭션이 없으면 REQUIRED 처럼 작동

 

 

 

 


 

 

 

rollbackFor, noRollbackFor

 

기본적으로 따로 설정을 하지 않을 경우 런타임 예외가 발생하면 롤백을 수행한다.

예외가 발생하지 않거나 체크 예외 발생 시 커밋을 수행하며, rollbackFor 옵션으로 기본 동작방식 변경이 가능하다.

 

 

rollbackFor

특정 예외가 발생 시 강제로 Rollback

@Transactional(rollbackFor=Exception.class)

 

noRollbackFor

특정 예외의 발생 시 Rollback 제외

 @Transactional(noRollbackFor=Exception.class)

 

 

 

 


 

 

 

timeout

 

  • 지정한 시간 내에 해당 메소드 실행이 완료되지 않은 경우 rollback 수행 (단위: second)
  • -1일 경우 no timeout (default : -1)
@Transactional(timeout=10)

 

 

 

 

 


 

 

 

readOnly

 

  • 트랜잭션을 읽기 전용으로 설정 
  • 성능을 최적화하기 위해 사용하거나, 특정 트랜잭션 작업 안에서 쓰기 작업이 일어나는 것을 의도적으로 방지하기 위해 사용 
  • readOnly = true인 경우 INSERT, UPDATE, DELETE 작업 진행 시 실행 시 예외 발생 (default : false)
@Transactional(readOnly = true)

 

 

 

 

 

 


참고사이트

[Spring] @Transactional 잘 사용해보기

[JPA] 프록시(Proxy)

 

 

728x90