본문 바로가기
Back-End/Spring

트랜잭션

by 달의 조각 2022. 9. 5.

 

트랜잭션이란 여러 개의 작업들을 하나의 그룹으로 묶어서 처리하는 단위이다.

 

ACID 원칙

  • 원자성(Atomicity): 하나의 그룹 단위는 모두 성공하거나, 모두 실패해야 한다.
  • 일관성(Consistency): 트랜잭션이 성공적으로 종료되면, 비즈니스 로직의 의도대로 일관성 있게 처리돼야 한다.
  • 격리성(Isolation): 여러 개의 트랜잭션이 실행될 때, 서로 영향을 주지 않고 독립적으로 실행되어야 한다.
  • 지속성(Durability): 트랜잭션이 완료되면 DB가 종료되어도 데이터는 물리 저장소에 저장되어 결과가 지속되어야 한다.

 

커밋과 롤백

  • 커밋: 모든 작업을 최종적으로 DB에 반영한다. 이 명령을 수행하면 하나의 트랜잭션 과정이 종료된다.
  • 롤백: 작업 중 문제가 생겼을 때, 트랜잭션 내 수행된 작업을 취소한다.

JPA API로 commit을 수행하기 위해 여러 클래스를 거친다. JPA 구현체인 하이버네이트에서 JDBC API의 구현체인 H2까지 이어진다. JPA 기술을 사용한 데이터베이스와의 인터랙션은 내부적으로는 JDBC API를 통해서 이루어진다.

 


 

트랜잭션: 로컬 트랜잭션, 분산 트랜잭션
Spring: 선언형 방식, 프로그래밍 코드 베이스 방식

 

Spring에서의 적용

선언형 방식

 

트랜잭션은 애플리케이션의 부가 기능이기 때문에 AOP 적용 대상이다.

 

🌿 비즈니스 로직에 @Transational 추가

 

클래스 레벨에 적용

메서드에 일괄 적용된다.

@Service
@Transactional
public class MemberService {
	...
}

체크 예외(Exception, SQLException, DataFormatException 등)는 이 방법으로 rollback이 되지 않는다.
별도의 예외 전략이 필요하지 않을 경우 아래와 같이 직접 지정해 주거나, 언체크 예외로 감싼다.

@Transactional(rollbackFor = {SQLException.class, DataFormatException.class})

 

메서드 레벨에 적용 (+ 클래스 레벨)

클래스 + 메서드 레벨에 적용하면 메서드 레벨의 애너테이션이 적용된다. 조회 메서드는 readonly를 true로 설정해서 JPA가 자체적으로 성능 최적화 과정을 거치도록 하는 게 좋다.

  • 내부적으로 영속성 컨텍스트를 flush 하지 않는다.
  • 변경 감지를 위한 스냅샷 생성을 하지 않는다.
@Service
@Transactional
public class MemberService {

	...

    @Transactional(readOnly = true) //읽기 전용 트랜잭션
    public Member findMember(long memberId) {
        return findVerifiedMember(memberId);
    }
    ...
}
Creating new transaction with name [com.codestates.member.service.MemberService.findMember]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly

 

트랜잭션 전파

트랜잭션의 경계에서 진행 중인 트랜잭션이 존재할 때, 존재하지 않을 때 어떻게 동작할 것인지 결정한다. 서로 다른 Service에서 이뤄지는 같은 분류의 작업이 있을 때, 이는 하나의 트랜잭션에 존재하도록 해야 한다.

//진행 중인 트랜잭션이 없으면 새로 시작하고, 진행 중인 트랜잭션이 있으면 해당 트랜잭션에 참여
/* 적용된 메서드를 다른 Service 클래스에서 호출했을 때,
해당 클래스에 트랜잭션이 진행되고 있으면 그곳에 참여한다. */
@Transactional(propagation = Propagation.REQUIRED)

//무조건 새로운 트랜잭션 시작, 진행 중인 트랜잭션은 새로운 트랜잭션이 종료될 때까지 중지
@Transactional(propagation = Propagation.REQUIRES_NEW)

//진행 중인 트랜잭션이 없으면 예외 발생
@Transactional(propagation = Propagation.MANDATORY)

//트랜잭션을 필요로 하지 않는다 : 진행 중인 게 있으면 메서드 종료 시점까지 중지
@Transactional(propagation = Propagation.*NOT_SUPPORTED*)

//트랜잭션을 필요로 하지 않는다 : 진행 중인 게 있으면 예외 발생
@Transactional(propagation = Propagation.*NEVER*)

 

트랜잭션 격리 레벨

@Transactional(isolation = Isolation.DEFAULT)

//다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것을 허용
@Transactional(isolation = Isolation.READ_UNCOMMITTED)

//다른 트랜잭션에 의해 커밋된 데이터를 읽는 것을 허용
@Transactional(isolation = Isolation.READ_COMMITTED)

//트랜잭션 내에서 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되도록 한다
@Transactional(isolation = Isolation.REPEATABLE_READ)

//동일한 데이터에 대해서 동시에 두 개 이상의 트랜잭션이 수행되지 못하도록 한다
@Transactional(isolation = Isolation.SERIALIZABLE)

 

🌿 AOP 방식을 이용하여 비즈니스 로직에서는 감추는 방법

  1. Configuration 클래스 정의: @Configuration
  2. TransactionManager DI
  3. 트랜잭션 어드바이스용 TransactionInterceptor 빈 등록
  4. Advisor 빈 등록

 

댓글