1. 시작하게 된 계기 및 다짐 😮
  • 이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.

    • 그 날 배웠던 것을 길지 않아도 좋으니 정리하며 복습하는 습관 기르기
    • 주말에 다음주에 배울 내용들을 예습
    • 코딩 문제와 java코드들은 꾸준히 학습
    • 자료구조를 이용한 알고리즘 문제 해결 학습
  1. 학습 목표 😮
목표결과
트랜잭션(Transaction)이 무엇인지 이해O
Spring에서 지원하는 트랜잭션 방식을 이해O
트랜잭션이 적용되지 않은 애플리케이션에서 어떤 문제점이 발생할 수 있는지 이해O
  1. 정리 😮

트랜잭션(Transaction) 이란?


1. 트랜잭션(Transaction)이란?

  • 여러개의 작업들을 하나의 그룹으로 묶어서 처리하는 처리 단위
  • 즉, 하나의 그룹(주문->결제->스탬프적립)의 작업이 모두 성공하거나 모두 실패하여야 한다는 의미인다.
  • 데이터 무결성(일관성)을 보장하는 핵심적인 역할이다.

2. ACID 원칙
1. 원자성(Atomicity)

  • 트랜잭션에서 더이상 쪼갤수 없는 작업(그룹)을 의미 (Ex. 위의 그룹과정)
  1. 일관성(Consistency)
  • 트랜잭션이 에러없이 성공될 경우, 비지니스 로직에서 의도하는 대로 일관성 있게 저장되거나 변경되어야 함
  • 작업 성공에 따른 결과가 일관적이여야함
  1. 격리성(Isolation)
  • 여러개의 트랜잭션이 실행 될 경우, 각각 독립적으로 실행되며 서로 영향을 주지 말아야함
  • 각 다른 트랜잭션(Ex. 음악듣기, 문서작업)에 영향을 주지 말아야함
  1. 지속성(Durability)
  • 트랜잭션이 완료되면 그 결과는 지속되어야 한다.
  • 트랜잭션에 따른 결과가 물리적인 저장소에 지속적으로 유지되어야 한다는 의미.

3. 커밋(Commit) 과 롤백(Rollback)

  1. 커밋

    • 모든 작업을 최종적으로 DB에 반영하는 명령어로, commit명령시 변경된 내용이 DB에 영구 저장됨
    • commit 명령이 없을시, 작업결과가 DB에 반영 X
    • commit 명령시 하나의 트랜잭션 작업이 종료되는것
  2. 롤백

    • 작업 중 문제가 발생했을 때, 트랜잭션 내에서 수행된 작업 취소
    • 트랜잭션 시작 이전의 상태로

4. Commit 호출과정

  1. TransactionImpl
    ★ 트랜잭션 제어를 위한 트랜잭션 드라이버 구현체 획득

    • EntityTransaction 인터페이스 구현클래스로, tx.commit() 호출시 호출
    • 트랜잭션 제어를 위한 트랜잭션 드라이버 구현체인 TransactionDriverControlImpl
      를 얻은 후에 구현 메서드인 commit()을 다시 호출
      [Ex. internalGetTransactionDriverControl().commit() == TransactionDriver.commit ]
  2. JdbcResourceLocalTransactionCoordinatorImpl > TransactionDriverControlImpl
    ★ JDBC Connection 액세스 방법을 제공하는 객체 획득

    • 내부의 TransactionDriverControlImpl클래스에서 JDBC Connection의 액세스 방법을 제공하는
      JdbcResourceTransaction의 구현체인 AbstractLogicalConnectionImplementor의
      commit()을 다시 호출한다.
      [Ex. jdbcResourceTransaction.commit() == AbstractLogicalConnectionImplementor.commit()]
  3. AbstractLogicalConnectionImplementor

    • 실제 JDBC Connection(JdbcConnection[Connect인터페이스 상속])을 얻은 후, 이 connection 객체의 commit()을 다시 호출
    • 여기까지가 Hibernate ORM에서의 영역
    • 이후, JDBC API의 구현체인 JdbcConnection 영역으로 이동
      [Ex. getConnectionForTransactionManagement().commit() == JdbcConnection.commit()]
  4. JdbcConnection

    • [H2사용] JDBC API의 구현체인 H2의 영역이다.
    • 아래 코드는 메서드를 호출하는 H2에서 지원하는 Command 클래스에서 이루어진다.
    • commit은 CommandInterface 변수이다.

    1). commit = prepareCommand("COMMIT", commit);

    • DB에서 commit명령을 준비한 후,

    2). commit.executeUpdate(null);

    • 해당 명령을 실행한다.
  5. Command

    • executeUpdate 실행시,
    • 3),4)는 commitIfNonTransactional 메서드 내에서 실행됨

    1). commitIfNonTransactional();

    • DB에 commit 명령을 전달하기 위해 메서드 호출

    2). session.rollback();

    • commitIfNonTransactional 수행 중 예외 발생시 수행

    3). boolean autoCommit = session.getAutoCommit();

    • H2에서 auto commit 여부를 체크한 후,

    4). session.commit(true);

    • DB 세션에 해당하는 Session 객체를 통해 commit 명령을 수행한다.
  6. SessionLocal 클래스
    1). transaction.commit();

    • 트랜잭션에 대한 commit이 최종적으로 수행됨

Extra. JDBC,JPA 이란

  1. JPA
  • ORM(객체-DB데이터 매핑)을 사용하기 위한 표준 인터페이스이다.
  • 정해 놓은 표준 스펙(사양 또는 명세)이다.
  • 구현체로 Hibernate ORM를 사용
  • 내부적으로 JDBC를 사용
  1. JDBC
  • DB에 접근할 수 있도록 Java에서 제공하는 API이다.
  • 모든 Java의 Data Access 기술의 근간이다.
  • 구현체인 H2를 사용
  1. Sesson 이란
  • 세션은 클라이언트 별로 각각의 상태 정보를 서버에서 저장하는 기술



선언형 방식의 트랜잭션 적용


0. @애너테이션

  1. @Transcational
    • (readOnly = true) : 읽기 전용 트랜잭션으로, commit 시에도 flush가 발생하지 않는다.
      즉, 변경 감지를 위한 snapshot을 생성하지 않고 영속성 컨텍스트 -> DB 작업 이 없음
    • (propagation = Propagation.REQUIRED) : 메서드 실행시, 현재 진행 중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 존재하지 않으면 새 트랜잭션을 생성하도록 해준다.
      ★ Test케이스에 작성시 실행 후 롤백을 해줌

1. Spring Boot에서의 트랜잭션 설정

  1. 애너테이션 방식의 트랜잭션

    • @Transactional애너테이션을 이용하여 클래스/메서드/
    • 기본적으로, PlatformTransactionManager인터페이스에 의해 관리되며,
      구현 클래스인 JpaTranscationManager를 사용한다.

    1).클래스 레벨에 적용

    • @Transactional 애터에이션이 붙은 모든 메서드들이 트랜잭션 적용이 된다.
    • RuntimeException제외인 체크예외들 같은 경우 @Transactional만으로는 rollback이 되지않음
      따라서, try~catch 문으로 예외를 잡거나
      [@Transactional(rollbackFor = {SQLException.class, DataFormatException.class})]
      와 같이 체크 예외를 직접 지정해줌으로써 작동하게 할 수 있음

    2). 메서드 레벨에 적용

    • 클래스 레벨에 @Transactional을 추가한것에 추가적으로 메서드에 @Transactional(readOnly =true) 를 추가
      하면 읽기 전용 트랜잭션이 적용된다.
      이 경우, commit을 해도 영속성 컨텍스트가 flush 되지 않고 변경 감지를 위한 snapshot 생성도 따로 하지않음( 1차 캐시에는 저장되어, 같은 조회시 이를 사용, SELECT문만 사용가능)

    • 일부 메서드에 @Transactional이 되어있지 않으면, 클래스 레벨 @Transactional적용


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

  1. 트랜잭션 전파(Transcation Propagation)

    • 트랜잭션의 경계에서 진행 중인 트랜잭션이 존재할 때 또는 존재하지 않을때 어떻게 동작할 것인지 결정

      1). Propagation.REQUIRED
      - (Default값)으로, 진행 중인 트랜잭션이 없을시 새로시작하고, 있으면 해당 트랜잭션에 참여

      2). Propagation.REQUIRES_NEW
      - 무조건 새로 트랜잭션을 시작하고, 기존의 트랜잭션은 새로 시작된 트랜잭션이 종료시까지 중지

      3). Propagation.MANDATORY
      - 진행중인 트랜잭션이 없으면 새로 싲가하지만, 있다면 예외를 발생

      4). Propagation.NOT_SUPPORTED
      - 트랜잭션을 필요로 하지 않음, 진행 중인 트랜잭션이 있으면 메서드 종료까지 트랜잭션이 중지

      5). Propagation.NEVER
      - 트랜잭션이 필요하지 않음, 진행 중인 트랜잭션이 있을시 예외 발생


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

    • ACID원칙에서, 이 격리성을 조절할 수 있는 옵션
    • 일반적으로 DB나 Data Source에 설정된 격리 레벨을 따르도록 권장되며, 이해만 하고 넘어가면됨

    1). Isolation.DEFAULT

    • READ_COMMITTED

    2). Isolation.READ_UNCOMMITTED

    • 다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것을 허용

    3). Isolation.READ_COMMITTED

    • 다른 트랜잭션에 의해 커밋된 데이터를 읽는 것을 허용

    4). Isolation.REPEATABLE_READ

    • 트랜잭션 내에서 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되도록
    • 트랜잭션 범위 내에서, 다른 사용자의 데이터 수정을 불가능하게

    5). Isolation.SERIALIZABLE

    • 동일한 데이터에 대해 동시에 두 개 이상의 트랜잭션이 수행되지 못하도록 한다.

3. AOP 방식의 트랜잭션 적용

  1. AOP 방식의 트랜잭션을 통해, 비지니스 로직에 @Transactional 애너테이션 조차도 적용하지 않는 방법

[예제 Code]

@Configuration
public class TxConfig {
    private final TransactionManager transactionManager;

		// (2)
    public TxConfig(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @Bean
    public TransactionInterceptor txAdvice() {
        NameMatchTransactionAttributeSource txAttributeSource =
                                    new NameMatchTransactionAttributeSource();

				// (3)
        RuleBasedTransactionAttribute txAttribute =
                                        new RuleBasedTransactionAttribute();
        txAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

				// (4)
        RuleBasedTransactionAttribute txFindAttribute =
                                        new RuleBasedTransactionAttribute();
        txFindAttribute.setPropagationBehavior(
                                        TransactionDefinition.PROPAGATION_REQUIRED);
        txFindAttribute.setReadOnly(true);

				// (5)
        Map<String, TransactionAttribute> txMethods = new HashMap<>();
        txMethods.put("find*", txFindAttribute);
        txMethods.put("*", txAttribute);

				// (6)
        txAttributeSource.setNameMap(txMethods);

				// (7)
        return new TransactionInterceptor(transactionManager, txAttributeSource);
    }

    @Bean
    public Advisor txAdvisor() {
				// (8)
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* com.codestates.coffee.service." +
                "CoffeeService.*(..))");

        return new DefaultPointcutAdvisor(pointcut, txAdvice());  // (9)
    }
}

(1) AOP방식으로 트랜적션 적용을 위해 @Configuration 애너테이션을 추가하며 Configuration 클래스를 정의
(2) 애플리케이션에 트랜잭선 적용을 위해 TransactionManager 객체를 받음(DI)
(3) 트랜잭션 어드바이스용 TransactionInterceptor 빈을 등록하고 이를 이용해 트랜잭션 경계를 설정 및 트랜잭션 적용
- (3)을 이용하여 조회 메서드를 제외한 공통 트랜잭션 애트리뷰트적용
- (4)을 이용하여 조회 메서드에 적용하기 위한 트랜잭션 적용
(5) 트랜잭션을 적용할 메서드에 트랜잭션 애트리뷰트 매핑하는 과정으로, 각
- Key : 메서드 이름, value : 적용할 애트리뷰트 를 추가해준다.
(6) map객체를 트랜잭션 애트리뷰트 Source에 넣음
txAttributeSource.setNameMap(txMethods) 해당 어드바이스용 TransactionInterceptor 생성자 파라미터로 객체 생성 및 리턴

(8) 포인트 컷 지정하는 과정으로, 위에서 만든 TransactionInterceptor 객체를 타켓 클래스에 적용하기 위한 포인트 컷을 지정

(9) 마지막으로 (9)와 같이 DefaultPointcutAdvisor의 생성자 파라미터로 포인트컷과 어드바이스를 전달


Extra. 이벤트 발생(EventListner)

  1. @애너테이션

    1). @EnableAsync

    • 해당 클래스를 비동기화가 가능하게 만듬 [클래스 레벨]

    2). @Async

    • 1)이 있는 클래스 내 메서드에 붙이는 애너테이션으로, 해당 메서드를 비동기적으로 실행

    3). @EventLinster

    • 이벤트 발생시에 어떤 발생한 custom이벤트에 따른 처리를 하기위한 로직이다.
      • 해당 이벤트 발생시, 그 이벤트를 파라미터로 잡아 처리

    4). @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)

    • 트랜잭션이 성공적으로 commit이 되기 전/후/마무리 되었을 때를 기준으로 실행

      • BEFORE_COMMIT : 커밋 이전에 실행
      • AFTER_COMMIT : default
      • AFTER_ROLLBACK : 롤백 직후 수행
      • AFTER_COMPLETION : 트랙재션이 완료 된 뒤 수행한다.
  2. ApplicationEventPublisher 클래스

    • 해당 클래스를 이용하여 멤버변수를 만들어 이를 이를 통해 등록해놓은 이벤트를 발생시킬 수 있다.
      [Ex. publisher.publishEvent(new MemberRegistrationApplicationEvent(this, savedMember));]
    • 여기서 this는 이 메서드를 가지고 있는 클래스를 의미한다.
  3. MemberRegistrationApplicationEvent 클래스

    • ApplicationEvent 클래스를 상속받은 custom클래스로 Member클래스를 내부변수로 가지고있음
  4. MemberRegistrationEventListener 클래스

    • Custom 클래스
    • @EventLister를 가지고있는 클래스로, 이벤트 발생시 발생한 이벤트를 받아서 처리하는 역할
    • 위에서 custom한 MemberRegistrationApplicationEvent를 파라미터로 받는 메서드를 통해
      해당 클래스의 멤버변수에 접근해 이벤트를 처리한다.



  1. 피드백 😮
  • 트랜잭션을 이용하여 하나의 작업이 전부 성공or실패에 따라 전체를 성공시킬지 취소시킬지 결정하는 방식을 학습하였다.
    이는, 데이터의 일관성을 위해 사용한다.

  • 특정 상황이 발생하였을 때 동작시키는 EventListener를 이용하여 어떤 상황이 발생하였을때 동작하는 방식을 학습하였다.

  1. 앞으로 해야 될 것 😮
  • 매일 꾸준히 할 것
    • 꾸준히 velog 작성
    • Java 언어 및 Algorithm 공부(Coding-Test)
    • 틈틈히 운동 하기

  • 내일 해야 할 것
    • Spring Testing
profile
Will be great Backend-developer

0개의 댓글