[SPRING] Transaction 란❓

수경·2025년 3월 24일

SpringFrameWork

목록 보기
12/24
post-thumbnail

Transaction 란❓

트랜잭션이란 "하나의 작업 단위를 의미하며, 이 단위 내에서 실행되는 모든 작업이 성공해야 최종적으로 반영되고, 하나라도 실패하면 모든 작업이 취소(rollback)되는 메커니즘"을 말한다.

Transaction 의 기본원칙 (ACID)

1. Atomicity(원자성)

  • 트랜잭션 내의 모든 작업이 하나의 단위로 실행되어야 한다.

  • 일부만 실행되거나 실패할 경우 전체를 롤백해야 한다.

2. Consistency(일관성)

  • 트랜잭션이 성공적으로 완료되면 데이터는 항상 일관성 있는 상태를 유지해야 한다.

  • 비즈니스 규칙과 데이터 무결성을 보장해야 한다.

3. Isolation(격리성)

  • 여러 트랜잭션이 동시에 실행될 때 서로 간섭하지 않아야 한다.

  • 격리 수준을 설정하여 데이터 정합성을 유지할 수 있다.

4. Durability(지속성)

  • 트랜잭션이 성공적으로 완료되면, 변경된 데이터는 영구적으로 저장되어야 한다.

  • 시스템 장애가 발생해도 데이터는 손실되지 않아야 한다.

Transaction 관리 방식

스프링에서는 트랜잭션을 관리하는 방식으로 선언적 트랜잭션(Declarative Transaction)과 프로그래매틱 트랜잭션(Programmatic Transaction) 두 가지 방법을 제공한다.

선언적 트랜잭션

가장 많이 사용되는 방식으로, 어노테이션(@Transactional)을 이용해 트랜잭션을 선언하면 스프링이 자동으로 트랜잭션을 관리해준다.

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);  // 데이터베이스에 저장
    }
}
  • @Transactional 을 메서드에 선언하면 해당 메서드가 실행될 때 트랜잭션이 시작되고, 성공적으로 끝나면 자동으로 커밋(commit) 된다.

  • 만약 예외가 발생하면 롤백 (rollback) 된다.

클래스 레벨 적용

@Service
@Transactional
public class UserService {
    // 이 클래스의 모든 메서드에 트랜잭션이 적용됨
}

클래스에 @Transactional 을 선언하면, 해당 클래스의 모든 메서드에 트랜잭션이 적용된다.

프로그래매틱 트랜잭션

개발자가 직접 트랜잭션의 시작(BEGIN), 커밋(COMMIT), 롤백(ROLLBACK)을 코드로 제어하는 방식이다.
TransactionTemplate 또는 PlatformTransactionManager 를 사용하여 구현할 수 있다.
1. TransactionTemplate 사용

@Service
public class AccountService {
    @Autowired
    private TransactionTemplate transactionTemplate; // 트랜잭션 템플릿 주입

    @Autowired
    private AccountDAO dao;

    public void transferMoney(int amount, String fromAcc, String toAcc) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    dao.withdraw(fromAcc, amount);  // 출금
                    dao.deposit(toAcc, amount);     // 입금
                } catch (Exception e) {
                    status.setRollbackOnly();  // 예외 발생 시 롤백
                }
            }
        });
    }
}

transactionTemplate.execute() 내부에서 트랜잭션이 실행된다.
✅ 예외가 발생하면 status.setRollbackOnly() 를 호출하여 롤백한다.

2. PlatformTransactionManager 사용

@Service
public class AccountService {
    @Autowired
    private PlatformTransactionManager transactionManager; // 트랜잭션 매니저 주입

    @Autowired
    private AccountDAO dao;

    public void transferMoney(int amount, String fromAcc, String toAcc) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def); // 트랜잭션 시작

        try {
            dao.withdraw(fromAcc, amount);  // 출금
            dao.deposit(toAcc, amount);     // 입금
            transactionManager.commit(status);  // 정상 실행되면 커밋
        } catch (Exception e) {
            transactionManager.rollback(status);  // 예외 발생 시 롤백
        }
    }
}

✅ 트랜잭션을 직접 시작하고(getTransaction) 성공 시 commit(), 실패 시 rollback() 호출한다.
TransactionTemplate 보다 코드가 더 복잡하다.

Transaction이 필요한 이유

트랜잭션은 데이터 무결성과 신뢰성을 보장하는 필수적인 기능이다

1. 데이터 일관성(Consistency) 유지

  • 여러 개의 SQL 연산이 하나의 작업 단위로 실행될 때 모두 성공해야만 변경을 확정(Commit) 하고, 하나라도 실패하면 모든 변경을 취소(Rollback) 해야 한다.

예를 들어, 은행 계좌 이체에서 돈이 출금되었는데 입금이 안 되면 문제가 발생한다.

2. 무결성(Integrity) 보장

  • 데이터가 논리적으로 올바르게 유지되도록 보장한다.

예를 들어, 좌석 예매 시스템에서 같은 좌석이 여러 사람에게 동시에 예약되지 않도록 방지한다.

3. 시스템 장애 대비

  • 실행 도중 오류, 네트워크 장애, 서버 다운 등의 상황에서도 데이터 손상을 방지할 수 있다.

예를 들어, 온라인 쇼핑몰에서 결제 중 서버가 다운되었을 때 결제가 실패해야 한다.

4. 동시성 문제 해결

  • 여러 사용자가 동시에 같은 데이터를 수정할 때 충돌을 방지한다.

예를 들어, 주식 거래 시스템에서 동일한 주식을 동시에 여러 사람이 매수할 때 문제를 방지한다.

🔹 트랜잭션 예시

💡 1. 은행 계좌 이체

⚠️ 트랜잭션이 없을 경우 발생할 문제

UPDATE bank_account SET balance = balance - 500 WHERE accNo = '100-100-1001';  -- A 계좌에서 500원 출금
-- (서버 다운, 네트워크 문제 발생)
UPDATE bank_account SET balance = balance + 500 WHERE accNo = '100-100-1002';  -- B 계좌에 500원 입금 (실행되지 않음!)
  • A 계좌에서 돈은 빠져나갔는데, B 계좌에는 돈이 입금되지 않는 문제가 발생한다!

✅ 트랜잭션을 사용할 경우 (올바른 방법)

BEGIN TRANSACTION;

UPDATE bank_account SET balance = balance - 500 WHERE accNo = '100-100-1001';  -- A 계좌에서 500원 출금
UPDATE bank_account SET balance = balance + 500 WHERE accNo = '100-100-1002';  -- B 계좌에 500원 입금

COMMIT;
  • 모든 SQL이 성공하면 COMMIT

  • 중간에 실패하면 ROLLBACK 하여 원상 복구

💡 2. 온라인 쇼핑몰 결제

⚠️ 트랜잭션이 없을 경우

  1. 사용자가 상품을 장바구니에 추가

  2. 결제 요청을 보낸다.

  3. 결제 완료되기 전에 서버 장애 발생
    → 결제는 성공했는데 주문이 등록되지 않는다! (사용자는 돈을 냈지만 주문이 사라짐)

✅ 트랜잭션 적용

@Transactional
public void processOrder(OrderDTO order) {
    paymentService.pay(order);  // 결제 처리
    orderService.createOrder(order);  // 주문 정보 저장
    inventoryService.updateStock(order);  // 재고 감소
}
  • 모든 과정이 성공해야만 주문이 완료됨

  • 중간에 실패하면 모든 작업 취소(ROLLBACK)

💡 3. 좌석 예매 시스템

⚠️ 트랜잭션이 없을 경우

  1. A 사용자가 좌석 예약 요청 (seat_no = 10)

  2. 동시에 B 사용자도 같은 좌석 예약 요청 (seat_no = 10)

  3. 동시에 요청이 처리되면 좌석이 중복 예약된다.

✅ 트랜잭션 적용

BEGIN TRANSACTION;

UPDATE seat_table SET status = 'reserved' WHERE seat_no = 10;

COMMIT;
  • seat_no = 10이 예약 처리되면 다른 사용자는 예약 불가

  • 트랜잭션이 없으면 동시성 문제 발생 가능

profile
개발 공부중•••

0개의 댓글