트랜잭션(Transaction)이란
: DB 상태를 변환 시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위입니다.
쪼갤 수 없는 여러 작접들을 논리적으로 최소 단위로 묶은 것입니다.
트랜잭션의 예시 상황
많은 블로그에서 보셨던 방식으로 접근해 보겠습니다 !
온라인 쇼핑몰을 결제할 때, 두가지 작업을 거쳐야합니다.
첫 번째 판매처에 돈을 송금하기, 두 번째 판매처에서 고객이 보낸 돈을 받기 두 작업을 한 트랜잭션이라고 할 경우 판매처에 돈을 보냈는데 판매처에서 고객이 보낸 돈을 받지 못하는 경우나, 고객이 돈을 보내지 않았는데 판매처에서 돈을 받는 경우가 생기면 안된다.
모든 작업이 성공적으로 완료되어야 작업 결과를 적용(commit)하고, 트랜잭션에 속한 모든 작업 중에(1번이나 2번 둘 중) 하나라도 오류가 발생하는 경우에는 작업을 실행하기 전의 상태로 완벽하게 돌아가야 하는 것(rollback)이 트랜잭션의 개념이다.
트랜잭션의 특징(Transaction ACID)
"A"tomicity(원자성)
: 트랜잭션의 연산은 DB에 모두 반영되거나, 모두 반영되지 않아야합니다. 하나라도 실패한다면, 앞서 성공한 것들을 복구시켜야합니다.
"C"onsistency(일관성)
: 트랜잭션의 작업 처리 결과는 항상 일관성 있어야 합니다. = 모든 트랜잭션은 일관성 있는 DB 상태를 유지합니다.(ex: DB의 무결성 제약 조건 항상 만족)
"I"solation(격리성)
: 어떤 트랜잭션도 다른 트랜잭션의 작업에 끼어들거나 서로 영향을 줄 수 없습니다. (ex: 동시에 같은 데이터 수정 X)
"D"urability(지속성)
: 트랜잭션이 완료된다면, 결과는 영구적이어야합니다.
* 격리성을 완벽하게 보장하려면 트랜잭션을 순서를 정해서 하나씩 실행해야하는데, 성능이 너무 나빠지기 때문에 트랜잭션 격리 수준이 존재합니다.
트랜잭션 격리 수준(lsolation Level)
비즈니스 로직과 트랜잭션 처리 로직이 동시에 존재한다면 코드의 중복이 발생할 수도 있고, 비즈니스 로직에만 집중된 코드르 작성하기 어렵습니다. 이렇게 비즈니스 로직에만 집중하기 위해 Spring은 크게 2가지 트랜잭션 기술을 지원합니다.
1. TransactionTemplate(TransactionManager을 주입받아 사용)
데이터 접근 기술에는 JdbcTemplate, JPA등 여러가지가 존재하는데 이 기술마다 트랜잭션을 사용하는 방법도 달라진다. 따라서 Spring에서는 이것을 고려해 트랜잭션에 대한 추상화를 지원한다. PlatformTransactionManager 인터페이스를 이용하면 된다. 해당 인터페이스는 총 3가지 메서드를 제공합니다.
- getTransaction(): 현재 TransactionStatus를 return
- commit(): 변경 내역 commit
- rollback(): 변경 내역 rollback
-> TransactionManager 클래스를 이용해 개발자가 트랜잭션 시작/종료 지점을 명시적으로 결정 가능.
트랜잭션은 로직을 수행하고 모든 로직이 성공적으로 수행되었을 경우에는 모든 결과를 DB에 일괄적으로 commit하고, 하나라도 실패한다면 모든 작업을 원상복구(rollback)시킵니다.
// 프로그래밍적 트랜잭션 관리
public void sendMoney(Long senderId, Long receiverId, Long value) {
Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
try {
// 한번에 처리되어야 하는 로직
1. 고객이 판매처에 돈 보내기
2. 판매처에서 고객이 보낸 돈 받기
connection.commit();
} catch(SQLException e) {
connection.rollback();
throw new RemittanceException();
}
}
- Connection 클래스: Java에서 을 나타내는 클래스. JDBC(Java Database Connectivity) API의 일부로 제공되며, 데이터베이스와의 통신을 위한 핵심 역할을 수행.
- 데이터베이스와의 연결
- try문에서 트랜잭션에 속한 여러 작업들 모두가 문제없이 처리 완료되면, commit.
- 하나의 작업에서라도 오류가 발생한다면 catch문에서 rollback.
- 보통 코드를 작성할 때 @Transactional 어노테이션을 이용하여 트랜잭션 처리를 해주는데, 테스트를 하는 경우에 @Transactional 어노테이션을 이용해 Test를 해야하는 메서드의 Transaction은 무시가 되기 때문에 100% 테스트를 통과했다고 하긴 어렵다. 요약해서 말하자면 테스트를 할 때 어노테이션을 이용하면 트랜잭션에 관련된 테스트는 되지 않는다는 것입니다.
2. @Transactional 어노테이션-AOP를 보장해준다 !
DB와 관련된 트랜잭션이 필요한 클래스 혹은 메서드에 @Transactional 어노테이션을 달아주기만 하면된다.
(+ 클래스에 달면 해당 클래스 및 하위 클래스까지 적용됨.)
@Transactional은 클래스와 메서드 모두 적용 가능하다. @Transactional 이 선언된 클래스 안의 메서드에 또 @Transactional가 선언되었다면, 메서드 레벨의 @Transactional 선언을 우선 적용. (Spring은 기본적으로 더 좁은 범위를 우선시 해줌)
@Transactional
public void buy(Long money) {
userDao.loseMoney(money);
sellerDao.gainMoney(money);
}
- loseMoney 메서드나 gainMoney 메서드 중 하나라도 실패한다면 전체 작업을 취소.
- 위의 메서드 모두 성공한다면 DB에 해당 변경 내역 반영.
@Transactioal 동작 구조
스프링의 트랜잭션 AOP는 @Transactional을 인식하여 트랜잭션 프록시를 적용.
프록시는 '대리인'이라는 뜻으로 Aspect와 @Transactional을 적용한 클래스 / 메서드인 Target을 연결해주는 역할
1. 클라이언트가 API 호출
2. 프록시 실행
3. 트랜잭션 코드 실행
4. 비즈니스 로직 실행
5. 트랜잭션 코드 실행(commit / rollback)
*스프링에서 AOP를 구현하는 방식은 JDK Dynamic Proxy와 CGLIB가 있습니다. 두 방법은 어떤 기술을 통해 Proxy객체를 생성하느냐에 차이가 있는데, 더 자세한 정보는 여기에 있으니 참고하면 좋습니다 :)
'IT이론 > 데이터베이스' 카테고리의 다른 글
[CS] 데이터 모델링이란 ? (0) | 2024.03.01 |
---|---|
[CS] DataBase 정규화(Normaliztion)란 ? (0) | 2024.02.29 |
개발의 모든 것 !
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!