본문 바로가기
Backend boot camp/Session3

[Spring MVC] 트랜잭션

by orioncsy 2022. 11. 7.

Transaction

개념과 사용 목적

개념

  • 물리적으로는 여러 개의 작업이지만 논리적으로는 하나의 작업
  • 모든 하위 작업이 성공하거나 실패하는 처리

목적

  • 데이터의 무결성을 보장하는 핵심적인 역할

ACID 원칙

Atomicity

  • 작업을 더 이상 쪼갤 수 없는 특징
  • 모든 작업이 성공하거나 실패하는 특징

Consistency

  • 일관성 있게 저장되거나 변경되는 것

Isolation

  • 각각의 트랜잭션은 독립적으로 실행
  • 다른 트랜잭션에 영향을 주지 않는 것

Durability

  • 결과가 지속되는 것
  • 데이터가 물리적인 장소에 지속적으로 유지

Transaction Commit & Rollback

Commit

  • 모든 작업을 최종적으로 반영하는 명령어
  • 변경 내용이 DB에 영구 저장
  • 하나의 트랜잭션 종료

Rollback

  • 작업 중 문제가 생겼을 때 수행된 작업들 취소
  • 트랜잭션 시작 이전 상태로 복귀

Commit & Rollback

  • Commit을 실행하지 않으면 쿼리가 실행되어도 테이블에 반영되지 않는다.
  • Rollback을 실행하면 이전 상태로 돌아간다.
  • 프로그래밍 상에 직접 명령을 할 수 없고 JPA에서 EntityManager 객체에서 Transaction 객체를 얻어 commit()을 호출해 반영 가능

JPA .commit() 동작 원리

  • JPA API를 통해 EntityTransaction 객체를 얻고 commit() 메서드를 호출했을 때 동작
  1. TransactionImpl
    • EntityTransaction 인터페이스의 구현체인 TransactionImpl에서 commit 호출
    • 그 안에서 TransactionDriverControlImpl와 같은 로컬 트랜잭션 드라이버 구현 객체를 얻어서 commit 다시 호출
  2. JdbcResourceLocalTransactionCoordinatorImpl > TransactionDriverControlImpl
    • JdbcResourceTransaction의 구현 객체인 AbstractLogicalConnectionImplementor의 commit()을 호출
  3. AbstractLogicalConnectionImplementor
    • 물리적인 JDBC connection을 얻고 객체의 commit()을 호출
    • Hibernate ORM 영역에서 처리되었다.
  4. JdbcConnection
    • JDBC API 구현체인 H2 영역
    • commit 명령을 준비하고 실행하는 메서드를 호출
    • 메서드 호출은 Command 클래스에서 진행
  5. Command
    • commitIfNonTransactional() 메서드를 호출
      • 메서드 내부에서 autoCommit 여부를 체크해서 데이터 베이스 세션의 commit 수행
    • 수행 과정에서 error가 발생하면 rollback 수행
  6. SessionLocal
    • 마지막으로 SessionLocal 클래스에서 트랜잭션 commit 수행

Spring Framwork Transaction 처리

선언형 트랜젝션 방식

Spring Boot에서의 트랜잭션 설정

  • Spring boot를 사용하지 않는다면 configuration을 작성해야 한다.
    • Datasource가 데이터베이스 커넥션 정보를 포함하고 있다.
    • Spring에서 트랜잭션은 PlatformTransactionManager에 의해 관리
    • PlatformTransactionManager 인터페이스를 구현한 JpaTransactionManager를 사용한다.

애너테이션 적용 방식

  • @Transactional이라는 애너테이션을 필요한 영역에 추가한다.
  • Service 클래스에 추가를 하면 Repository의 기능을 이요하는 모든 메서드에 트랜잭션 작용
  • 트랜잭션 적용 확인을 위해 JPA 로그 레벨을 설정(application.yml)
    • member를 추가했을 때 로그를 확인해보면
      • 새로운 트랜잭션 생성
      • 트랜잭션에서 commit 발생
      • 트랜잭션 종료
      • EntityManager 종료
    • member를 생성하는 로직에 RuntimeException을 의도적으로 발생시키면
      • 새로운 트랜잭션 생성
      • Rolling back JPA transaction on EntityManager
        • 위와 같이 rollback이 생성되는 것 확인 가능
  • logging: level: org: springframework: orm: jpa: DEBUG
  • checked Exception은 rollback이 적용이 되지 않는다.
    • @Transactional(rollbackFor={…})
      • 위와 같이 직접 예외를 지정하거나 unchecked exception으로 감싸서 rollback 작동
  • 메서드 레벨에 @Transactional 적용
    • @Transactional(readOnly=true)를 적용하면
      • 해당 메서드는 commit을 실행하기는 하지만 영속성 콘텍스트를 flush하지 않는다.
      • 읽기 전용은 변경 감지를 위한 스냅샷도 진행하지 않는다.
      • 불필요한 동작 최소화
      • PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
        • 트랜잭션 생성할 대 위와 같이 readOnly로 설정된다.

여러 작업이 하나의 트랜잭션으로 묶이는 경우

  • 커피 주문을 하여 고객 스탬프가 적립되는 상황
    • orderService에서 트랜잭션이 실행되고 memberService에서 update 하면서 트랜잭션 실행
    • 독립적으로 실행될 경우 ACID에서 원자성이 실행이 안된다.
    • createOrder() 메서드에 스탬프를 업데이트하는 update 메서드를 실행하여 메서드들이 하나의 트랜잭션 경계 내에 있도록 하여 묶는다.
  • 실행 예시
    • orderService에 @Transactional을 추가하고 createOrder 메서드 안에 반환 직전에 RuntimeException을 의도적으로 발생
    • memberService에서 updateMember에 @Transactional(propagation=Propagation.REQUIRED)를 추가한다.
      • 이것은 현재 진행 중인 트랜잭션이 존재하면 그 트랜잭션을 사용하고 존재하지 않으면 새로 생성하여 진행한다.
    • 실행 결과
      • rollback 로그 확인 가능
      • H2 웹 콘솔에서 order 정보와 stamp정보가 반영되지 않은 것 확인 가능

트랜잭션 전파

  • 트랜잭션의 경계에서 진행 중인 트랜잭션이 존재 유무에 따라 작동 방식 설정
    • Propagation.REQUIRED
      • propagation의 디폴트 값으로 진행 중인 트랜잭션 없으면 생성, 있으면 해당 트랜잭션 참여
    • Propagation.REQUIRES_NEW
      • 진행 중인 것과 상관없이 새로운 트랜잭션 시작, 기존 진행 중인 트랜젝션은 새로 시작된 트랜잭션 종료 시까지 일시 중지
    • Propagation.MANDATORY
      • 진행 중인 트랜잭션이 없으면 예외를 발생
    • Propagation.NOT_SUPPORTED
      • 트랜잭션이 필요 없음을 의미, 진행 중인 트랜잭션이 있다면 일시 중지하고 메서드 수행하고 종료되면 다시 재개
    • Propagation.NEVER
      • 진행 중인 트랜잭션이 존재할 경우 예외 발생

트랜잭션 격리 레벨(Isolation Level)

  • ACID의 독립성을 지키기 위해 제공하는 애트리뷰트
    • Isolation.DEFAULT
      • 데이터베이스 제공 기본값
    • Isolation.READ_UNCOMMITTED
      • 다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것 허용
    • Isolation.READ_COMMITTED
      • 다른 트랜잭션에서 커밋된 데이터를 읽는 것 허용
    • Isolation.REPEATABLE_READ
      • 트랜잭션 내 한 번 조회한 데이터를 반복 조회해도 같은 데이터 조회
    • Isolation.SERIALIZABLE
      • 동일한 데이터 동시에 두 개 이상 트랜잭션 수행 불가

AOP 적용 방식

  • AOP 트랜잭션 적용을 위한 Configuration 클래스 정의
    • @Configuration을 추가하여 클래스 정의
  • TransactionManager DI
    • TransactionManager 객체를 DI로 받는다.
  • @Bean을 추가한 TransactionInterceptor를 반환하는 txAdvice()를 생성
    • NameMatchTransactionAttributeSource 객체로 txAttributeSource를 생성
    • RuleBasedTransactionAttribute 객체로 txAttribute와 txFindAttribute 생성
      • 각각 setPropagationBehavior로 TransactionDefinition.PROPAGATION_REQUIRED 설정
      • txFindAttribute는 setReadOnly(true) 설정
    • Map <String, TransactionAttribute>로 txMethods 생성
      • .put(”find*”, txFindAttribute) 와 .put(”*”, txAttribute)로 추가
    • txAttributeSource에 .setNameMap(txMethods)로 설정
    • 마지막으로 TransactionInterceptor(transactionManager, txAttributeSource)로 생성해서 반환
  • @Bean을 추가한 Advisor를 반환하는 txAdvisor() 생성
    • AspectJExpressionPointcut 객체 pointcut을 생성
    • pointcut에 .setExpression(…) 으로 매개변수에 적용할 경로 작성
    • 마지막으로 DefDefaultPointcutAdvisor(pointcut, txAdvice())를 return 한다.

'Backend boot camp > Session3' 카테고리의 다른 글

[Spring MVC] API문서화  (0) 2022.11.11
[Spring MVC] Testing  (0) 2022.11.11
[Spring MVC] JPA 데이터 액세스 계층  (0) 2022.11.07
[Spring MVC] JDBC DB Access Layer  (0) 2022.10.29
[Spring MVC] Exception Handle  (0) 2022.10.29