DB

[DB] 스프링부트 트랜잭션 @Transactional

somuxsomu 2024. 2. 21. 15:25

Spring Boot 의 트랜잭션 : @Transactional 

 

3탄에서는 스트링부트에서의 트랜잭션에 대해 알아보려고 한다.

 

 

@Transactional 이란?

Spring Boot에서 @Transactional은 트랜잭션 처리를 하기 위해 사용된다.

 

메소드 또는 클래스 레벨에서 사용할 수 있으며,

메소드 또는 클래스 내에서 실행되는 모든 데이터베이스 작업이 하나의 트랜잭션 묶여서 처리가 되며,

트랜잭션이 적용된 메소드 또는 클래스에서

작업 실행 중 예외가 발생하면

자동으로 롤백(Rollback)하여 이전 상태로 되돌려준다.

 

1. 트랜잭션 시작
2. 데이터베이스 연결
3. 요청 실행
4. 예외 발생 여부 확인
5-1. 성공적으로 실행되면 변경 내용을 영구적으로 저장하고, 트랜잭션 종료 커밋(Commit)
5-2. 에외 발생하면 변경 내용을 롤백하고, 트랜잭션 종료 롤백(Rollback)

 

 


트랜잭션 경계 설정

트랜잭션 경계는

트랜잭션의 시작과 종료 지점을 의미한다.

 

@Transactional 어노테이션을 사용하여 메서드나 클래스에 경계를 설정하면,

해당 영역에서 자동으로 트랜잭션을 시작하고 종료할 수 있다.

 

예를들어,

메소드에 @Transactional을 적용하면, 해당 메소드가 트랜잭션 경계 내에서 실행된다.

메소드 시작 시 트랜잭션을 시작하고, 메소드내 작업 완료 시 커밋 또는 롤백을 수행한다.

 


속성 설정

@Transactional의 속성을 사용하여

트랜잭션 동작을 세부적으로 제어할 수 있다.

 

1. readOnly

- 트랜잭션의 읽기 전용 여부 설정

- 데이터 수정 작업 없이 조회 작업만 수행하는 경우 사용하여 성능을 향상시킬수 있다.

- 기본값 : false

- readOnly를 true로 설정하면 트랜잭션이 읽기 전용으로 간주된다.

해당 트랜잭션 내에서 데이터를 수정하는 작업이 없음을 나타낸다.

- @Transactional(readOnly = true)

 

 

 

2. isolation

- 트랜잭션의 격리 레벨을 설정

- 격리 레벨은 동시에 여러 트랜잭션이 실행될 때 트랜잭션 간의 데이터 접근을 제어하는 데 사용된다.

- 기본값 : DEFAULT

- 여러개의 트랜잭션이 발생하는 경우,

생길 수 있는 동시성 문제를 해결 할 수 있는 isolation속성에는

다음과 같은 값을 설정할 수 있다.

 

1. DEFAULT
- 기본 격리 레벨
- DBMS의 isolation Level을 따른다. ( MySQL의 격리레벨은 REPEATABLE_READ이다.)

2. READ_UNCOMMITTED (커밋되지 않는 읽기, level 0)
- 트랜잭션 처리중이거나 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용.
- 동시성 부작용 Dirty Read 발생 → 다른 트랜잭션에서 처리하는 작업이 완료되지 않았는데도 다른 트랜잭션에서 읽을 수 있는 현상

3. READ_COMMITTED (커밋된 읽기, level 1) 
- 트랜잭션에서 커밋된 데이터만 읽는 것을 허용.
- Dirty Read 방지
- A라는 데이터가 B로 변경되는 동안 다른 사용자는 해당 데이터에 접근할 수 없다.

4. REPEATABLE_READ (반복 가능한 읽기, level 2)
- 읽은 데이터를 다시 읽을 때 같은 값이 나오는 것을 보장한다.
- 트랜잭션이 완료될 때까지 SELECT문을 사용하는 모든 데이터에 Shared Lock 처리한다. 따라서, 다른 사용자는 해당 영역에 대한 데이터 수정이 불가능하다.
- 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지, 후행 트랜잭션이 갱신하거나 삭제가 불가능 하기 때문에 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴한다.

5. SERIALIZABLE (직렬화 가능, level 3) 
- 동시에 실행 중인 다른 트랜잭션에서 접근하지 못하도록 하며, 완벽한 읽기 일관성 모드 제공한다.
- 동시성 부작용을 모두 방지한다. 동시 호출을 순차적으로 실행하도록 제한하는 설정이므로, 성능 저하의 우려가 있다.
트랜잭션이 완료될 때까지 SELECT문을 사용하는 모든 데이터에 Shared Lock이 걸리므로 다른 사용자는 그 영역에 해당하는 데이터에 대한 수정 및 입력이 불가능하다.
- 적절한 격리 수준은 동시성과 데이터 일관성 사이의 균형을 유지하기 위해 선택되어야 한다. 필요에 따라 격리 수준을 설정하여 트랜잭션의 동작을 제어할 수 있다.

 

 

- @Transactional(isolation = Isolation.READ_COMMITTED)

//커밋된 데이터만 다른 트랜잭션에서 조회할 수 있도록 보장 
@Transactional(isolation = Isolation.READ_COMMITTED) 
public void updateAccountBalance(String accountId, double amount) { 
	// 계좌 잔액 업데이트 작업 수행 
}

 

 

3. propagation

- 트랜잭션 동작 도중 다른 트랜잭션을 호출할 때, 어떻게 할 것인지 설정

- 기본값 : REQUIRED

- REQUIRED로 설정하면 기존 트랜잭션이 있는 경우 해당 트랜잭션에 참여하고, 없는 경우 새로운 트랜잭션을 생성한다.

- REQUIRES_NEW, NESTED, SUPPORTS, NOT_SUPPORTED, NEVER 등이 있다.

- @Transactional(propagation = Propagation.REQUIRED)

 

 

4. timeout

- 트랜잭션의 제한 시간을 설정

- 시간은 초 단위로 설정하며, 설정한 시간을 초과할 경우 트랜잭션이 롤백된다다.

응용 프로그램에 트랜잭션 타임아웃 정책이 있을 경우 유용하게 사용할 수 있다.

- 기본값: -1(무제한)

- @Transactional(timeout = 60)

 


 

 

참고해서 개발할 때 적용해보세요 !

클래스 단위로 트랜잭션을 설정하는것보다는

필요한 메소드 단위에서 설정하는게 더 좋은것 같습니다