트랜잭션은 원자성 일관성 격리성 지속성 을 다 보장해야함

  • 원자성 : 트랜잭션 내에서 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공하거나 실패해야함

  • 일관성 : 모든 트랜잭션은 일관성 있는 데이터베이스 상태로 유지해야함

  • 격리성 : 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야함
    -> 격리성은 동시성과 관련된 성능 이슈로 인해 트랜잭션 격리 수준을 선택해야 한다.
    트랜잭션 격리 수준
    READ UNCOMMITED (커밋되지 않은 읽기)
    READ COMMITTED (커밋된 읽기)
    REPEATABLE READ (반복 가능한 읽기)
    SERIALIZABLE (직렬화 가능)
    일반적으로는 READ COMMITTED 를 많이 사용

  • 지속성 : 트랜잭션을 성공적으로 끝내면 결과가 항상 기록되어야함 (내용을 복구하기 위해서)


설정

✅ 데이터베이스의 연결구조와 DB 세션

  1. 사용자는 웹 애플리케이션 서버 (WAS)와 같은 클라이언트를 사용해서 데이터베이스 서버에 접근
  2. 클라이언트는 데이터베이스 서버에 연결을 요청하고 커넥션 을 맺음
  3. 데이터베이스 서버는 내부에 세션을 만듬
  4. 세션은 트랜잭션을 시작하고, COMMIT 또는 ROLLBACK 을 함
  5. 사용자가 커넥션을 닫거나 DBA 세션을 강제로 종료하면 세션은 종료됨
  • 수동 커밋 vs 자동 커밋
    트랜잭션을 사용하려면 자동 커밋수동 커밋 을 이해해야 한다
    자동 커밋 으로 설정하면 쿼리 실행 직후에 따로 COMMIT 이나 ROLLBACK 명령어를 호출하지 않아도 되지만, 원하는 트랜잭션 기능을 제대로 사용할 수 없다.
    수동 커밋 으로 설정하면 쿼리 직후에 꼭 COMMIT 이나 ROLLBACK 을 호출해야한다.
    수동 커밋 모드로 전환하는 것을 트랜잭션을 시작한다라고 표현할 수 있다.

수동 커밋 으로 했을 경우 세션 1에서 쿼리 작업을 진행 후 COMMIT 명령어를 호출하지 않으면
세션 1에서는 쿼리 작업의 결과를 볼 수 있지만, 세션 2에서는 아직 반영이 되지 않았기 때문에 확인할 수 없다.

자동커밋 설정 : set autocommit true;
수동커밋 설정 : set autocommit false;


✅ DB 락

DB 락 : 세션 1과 세션 2가 동시에 같은 데이터를 수정하면 원자성 의 문제점이 발생한다. 이러한 문제점을 막기 위해서 세션이 트랜잭션을 시작하고 데이터를 수정할 때 COMMIT 이나 ROLLBACK 을 하지 않았으면, 다른 세션에서 해당 데이터를 수정할 수 없도록 막는 것을 DB 락 이라고 한다.

세션1에서 COMMIT 이나 ROLLBACK 을 수행하면, 세션 2에서는 을 얻으면서 쿼리 진행이 가능하다

락 타임아웃 : 다른 세션에서 을 얻을 때 까지 기다리는데, 그 락을 타임아웃으로 설정해서
시간이 넘어도 을 얻지 못하면 락 타임아웃 오류가 발생


✅ 트랜잭션 매니저

트랜잭션은 비즈니스 로직이 있는 서비스 계층 에서 시작해야 한다.
DB 트랜잭션을 사용하려면 트랜잭션을 사용하는 동안 같은 커넥션 을 유지해야 한다.

트랜잭션 매니저 : 데이터베이스 시스템에서 트랜잭션을 관리하는 중요한 구성 요소
-> Oracle, MySQL, PostgreSQL 등에서 트랜잭션 매니저가 내장되어 있음
-> 트랜잭션 매니저 는 크게 트랜잭션 추상화리소스 동기화 의 역할을 담당한다

  • 트랜잭션 추상화 : 데이터 접근 기술에 따라서 (JPA, JDBC 등등) 트랜잭션 구현체를 만들어두었기 때문에 가져다 사용하기만 하면 된다

  • 리소스 동기화 : 커넥션 을 파라미터로 전달 할 필요 없이 트랜잭션 동기화 매니저 를 통해서 커넥션을 보관해서 커넥션이 필요할 때 갖다 씀 (커넥션 유지가 쉬워짐)


✅ 트랜잭션 템플릿

서비스에서 트랜잭션을 시작하면 try catch finally 포함한 commit rollback 코드가 반복됨
달라지는 부분은 서비스 비즈니스 로직만 달라짐
이런 반복되는 부분을 템플릿 콜백 패턴 을 활용하면 해결 가능함
-> 스프링에서 제공하는 TransactionTemplate 클래스
-> 언체크 예외가 발생하면 rollback 처리, 체크 예외의 경우 commit 처리

트랜잭션 템플릿 덕분에 반복되는 코드는 제거 할 수 있다
하지만 서비스 로직 뿐 아니라 트랜잭션을 처리하는 기술 로직도 포함되어 있다.

//ex)
  txTemplate.executeWithoutResult((status) -> { // 트랜잭션 처리 기술
 try {
		bizLogic(fromId, toId, money);  //비즈니스 로직
      } 
catch (SQLException e) {
		 throw new IllegalStateException(e);
      }
  });

이러한 문제점을 해결하기 위해 트랜잭션 AOP 를 사용한다


✅ 트랜잭션 AOP

트랙재션 처리가 필요한 곳에 @Transactional 어노테이션을 붙여주면 됨
이러한 방법을 선언적 트랜잭션 관리 라고 불린다.
위에서 설명한 트랜잭션 매니저 또는 트랜잭션 템플릿 등을 사용해서 트랜잭션 관련 코드를 직접 작성하는 것을 프로그래밍 방식의 트랜잭션 관리 라고 한다.
대부분 실무에서는 선언적 트랜잭션 관리 를 많이 사용
스프링 트랜잭션 AOP 는 이 어노테이션을 인식해서 트랜잭션 프록시 를 적용시켜줌

프록시 : 다른 객체에 대한 대리자 역할을 하는 디자인 패턴
프록시를 사용하면 트랜잭션을 처리하는 객체와 비즈니스 로직을 처리하는 서비스 객체를 분리 가능하다
즉 서비스 계층에서는 순수한 비즈니스 로직만 남길 수 있음


✅ 스프링 부트의 자동 리소스 등록

스프링 부트가 등장하면서
DataSource트랜잭션 매니저를 직접 빈으로 등록할 필요 없이 자동 등록이 가능해짐

기존에는 다음과 같이 스프링 빈 으로 직접 등록했었음

	@Bean
	DataSource dataSource() {
		return new DriverManagerDataSource(URL, USERNAME, PASSWORD);
	}

	@Bean
	PlatformTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dataSource());
	}

하지만 요즘 스프링부트에서는 application.properties 에 있는 속성을 이용해서 DataSource 생성

 spring.datasource.url=jdbc:h2:tcp://localhost/~/test
 spring.datasource.username=sa
 spring.datasource.password
profile
우측 상단 햇님모양 클릭하셔서 무조건 야간모드로 봐주세요!!

0개의 댓글