spring 으로 웹을 구현했을 때 서비스 단에 트랜잭션을 보장하기위해 @Transactional 을 사용했었는데 그 동작에 대해서는 궁금해하지않았던 것 같아 정리를 해보려한다.
트랜잭션 ( Transaction ) 이란
트랜잭션은 데이터베이스 용어로, 하나 이상의 관련 작업을 그룹화하여 일련의 작업들이 모두 성공적으로 완료되거나 하나라도 실패할 경우 모든 작업들이 취소되도록 하는 것을 의미한다.
트랜잭션의 특성 ( ACID )
- 원자성(Atomicity) : 트랜잭션 내의 모든 작업들이 완전히 수행되거나 전혀 수행되지 않아야 한다.
- 일관성(Consistency) : 트랜잭션 실행 전후로 데이터베이스는 항상 일관된 상태를 유지해야 한다. 
- 독립성(Isolation) : 동시에 여러 트랜잭션이 실행될 때, 각 트랜잭션은 서로의 작업 내용을 볼 수 없어야 한다. 
- 지속성(Durability) : 트랜잭션이 성공적으로 완료된 후, 그 결과는 영구적으로 저장되어야 한다.
예시를 통해 특성을 살펴보면 계좌이체를 생각할 수 있다.
시나리오 : userA : 10000 원 , userB : 10000 원 일 때 userA 가 userB 에게 2000원을 입금한다.
1. userA 의 금액에서 2000원을 차감하고 userB의 금액에서 2000원을 더해야하는데 여기서 userA의 금액을 차감한 상태에서 트랜잭션이 종료된다면 userA 는 8000원, userB 는 10000 원으로 원자성 보장 X.
2. 그렇다면 위 상황에서 롤백이 되어 이전 상태로 돌아가야하는데, 롤백된 데이터와 이전 상태가 다르다면 일관성 보장 X.
3. 혹은 userB의 금액이 업데이트되지 않은 상태에서 userC 가 userB에게 3000원을 이체하려고 한다면 userB 의 금액은 10000+3000 = 13000 이 되어 독립성 보장 X.
4. 트랜잭션 이후 시스템에 장애가 발생하여도 커밋된 데이터는 보존되어야하는데 데이터가 보호되지 않는다면 지속성 보장 X.
데이터베이스 연결 구조와 DB 세션
트랜잭션이 시작하기까지 아래의 과정을 갖는다.
1. 사용자는 WAS 나 DB 접근 툴 등의 클라이언트를 통해 서버에 연결을 요청하여 커넥션을 맺는다.
2. 데이터베이스 서버 내부에 세션을 만들고 해당 커넥션을 통한 모든 요청을 실행한다. ( 커넥션 당 세션 1개 )
3. 개발자가 클라이언트를 통해 SQL 을 전달하면 현재 커넥션에 연결된 세션이 SQL 을 실행한다.
4. 세션은 트랜잭션을 시작하고 커밋 또는 롤백을 통해 종료한다.
트랜잭션 ( Transaction ) 구현 및 적용
트랜잭션을 보장하기위해 우리는 서비스 단에서 메서드나 클래스에 @Transactional 을 적용했을 것이다.
@Transactional
public class MemberServiceImpl implements MemberService{
    ..
}
그래서 이 @Transactional 은 무엇이고 어떻게 동작하는가?
@Transactional은 스프링 프레임워크에서 제공하는 어노테이션으로, 선언적 트랜잭션 관리를 가능하게 한다. 이 어노테이션을 메서드나 클래스에 붙이면 해당 메서드 내의 작업들이 하나의 트랜잭션으로 묶인다.
이는 AOP(Aspect-Oriented Programming) 프록시를 기반으로 동작하고 클래스에 @Transactional이 추가되면 스프링은 런타임에 해당 클래스의 프록시를 생성한다.
프록시 도입 전과 도입 후 ( 강의 참고 )
- 프록시 도입 전 서비스 계층의 트랜잭션 코드
    public void accountTransfer(String memberId, int money) throws SQLException {
        // 트랜잭션 시작
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            // 비즈니스 로직
            bizLogic(memberId, money);
            transactionManager.commit(status); // 성공시 커밋
        } catch (Exception e) {
            transactionManager.rollback(status); // 실패시 롤백
            throw new IllegalStateException(e);
        }
    }프록시 도입 전에는 서비스 단에서 트랜잭션을 직접 시작하고 종료하는 것을 볼 수 있다.
- AOP 프록시 도입 후 서비스 계층의 트랜잭션 코드
    @Transactional
    public void accountTransfer(String memberId, int money) throws SQLException {
        bizLogic(memberId, money);
    }트랜잭션 AOP 을 적용함으로서 트랜잭션을 처리하는 로직이 제거되어 순수한 비즈니스 로직만 남아있음을 볼 수 있다.
트랜잭션 프록시는 트랜잭션을 시작하고 실제 서비스를 호출하여 트랜잭션 처리 로직을 실행한다.
정리
가볍게 이해하고 사용하였던 애노테이션인데 그 안에서 이렇게 동작한다는 것은 이제서야 알았다. 위 내용을 공부하며 ThreadLocal 이 언급되었는데 다음 번에 정리해봐야겠다.
참고
https://docs.spring.io/spring-framework/reference/data-access/transaction.html
김영한, 스프링 DB 1편 - 데이터 접근 핵심 원리, https://inf.run/AomUA
'Web' 카테고리의 다른 글
| [Spring] Spring 3.x + Swagger 설정 (0) | 2023.11.19 | 
|---|---|
| [System Design] URL 단축기 설계 -1 (0) | 2023.11.05 | 
| [JPA] 지연로딩과 즉시로딩 (0) | 2023.10.08 | 
| [JPA] 1:N 단방향 매핑과 N:1 양방향 매핑 (0) | 2023.10.01 | 
| [Spring] CORS Configuration with Spring security (0) | 2023.09.17 | 
