본문 바로가기

서버 심화 스터디

트랜잭션

 <트랜잭션이란?>

DB 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위

또는 한꺼번에 모두 수행되어야 할 일련의 연산들

<트랜잭션 예시>

  • 상황: 온라인 쇼핑몰에서 결제할 때
    • ① 판매자에게 돈 보내기
    • ② 판매자에게 상품 받기
  • 중요한 원칙:
    두 작업은 모두 성공하거나,
    둘 다 실패해서 원상복구 되어야 한다.
  • 핵심 개념:
    • commit: 모든 작업이 성공했을 때 결과를 확정
    • rollback: 하나라도 실패하면 처음 상태로 되돌리기

-> 트랜잭션은 작업 전체가 완전히 성공하거나, 아예 안 한 것처럼 만드는 기술~!

<트랜잭션 ACID>

  • Atomicity (원자성)
    모든 작업이 다 성공하거나, 하나라도 실패하면 전부 실패한 걸로 처리
  • Consistency (일관성)
    → 트랜잭션 전후에 DB의 규칙이 항상 지켜져야 함
  • Isolation (격리성)
    → 동시에 여러 트랜잭션이 실행돼도 서로 간섭하지 않아야 함
  • Durability (지속성)
    성공한 트랜잭션은 영구적으로 기록되어야 함

<비즈니스 로직과 트랜잭션 코드 분리>

비즈니스 로직과 트랜잭션 처리 로직이 동시에 존재한다면?

->코드가 복잡해지고

->핵심 로직에 집중하기 어려움

<Spring의 트랜잭션 처리 방식>

1. TransactionTemplate 

  • 다양한 DB 기술(JdbcTemplate, JPA 등)에 따라 트랜잭션 처리 방법이 다르기 때문에,
    Spring에서는 공통된 방식으로 트랜잭션을 처리할 수 있도록 추상화를 제공함.
  • PlatformTransactionManager 인터페이스를 통해 다음 3가지 기능 제공:
    • getTransaction(): 트랜잭션 상태 조회
    • commit(): 성공 시 변경 사항 저장
    • rollback(): 실패 시 변경 사항 되돌림
  • 개발자가 직접 트랜잭션 시작/종료 시점을 명확히 지정할 수 있음.

2. @Transactional 어노테이션

  • 트랜잭션이 필요한 클래스나 메서드에 붙이기만 하면 자동으로 트랜잭션 적용
  • 클래스에 붙이면: 해당 클래스와 하위 메서드에 모두 적용
  • 메서드에 붙이면: 해당 메서드에만 적용됨

<프로그래밍적 트랜잭션 관리 vs 선언적 트랜잭션 관리>

  • 프로그래밍 트랜잭션 관리 Programmactic Transaction Management
    • ex: TransactionManager, TransactionTemplate
    • 트랜잭션 관련 코드를 직접 작성하는 방법
  • 선언적 트랜잭션 관리 Declarative Transaction Management
    • ex: @Transactional
    • 과거엔 xml을 이용해 설정하기도 함
    • 프로그래밍 트랜잭션 관리에 비해 간편

@Transactional 트랜잭션 AOP를 위해 Spring에서 지원해주는 어노테이션!!

@Transactional
public void buy(Long money) { 
    userDao.loseMoney(money);
    sellerDao.gainMoney(money);
}

 

loseMoney 또는 gainMoney 둘 중 하나라도 실패하면 전체 작업을 취소한다.

모든 작업이 성공할 경우 DB에 해당 변경 내역이 반영된다.

 <@Transactional의 동작 구조>

  • Spring은 AOP를 활용해서 @Transactional이 붙은 메서드나 클래스에 자동으로 트랜잭션을 적용한다.
  • 즉, 우리가 트랜잭션을 일일이 작성하지 않아도, Spring이 프록시(proxy)를 만들어서 중간에서 트랜잭션을 관리해준다.

1.  Client가 API 호출 → 사용자가 요청을 보냄 (예: 주문하기)

2. 프록시 실행 → Spring이 만든 트랜잭션 프록시가 중간에 끼어듦

3. 트랜잭션 시작 코드 실행 → 트랜잭션 매니저가 트랜잭션을 시작함

4. 비즈니스 로직 실행 → 실제로 주문 처리, 결제 처리 등 핵심 로직 수행

5. 트랜잭션 종료 코드 실행 (commit 또는 rollback) → 성공하면 저장(commit), 실패하면 되돌리기(rollback)

프록시(Proxy)란?
-대리인 또는 중간 관리자
-진짜 객체(서비스 클래스 등)를 호출하기 전에 가로채서 어떤 작업을 먼저 하거나 뒤에 덧붙일 수 있는 객체

<OrderController>

  • 역할: HTTP 요청 처리
  • fail=true일 경우 예외 발생시켜 트랜잭션 롤백 테스트 가능

<OrderService>

@Transactional: 이 메서드에서 예외가 나면 모든 DB 작업 롤백

 

<Order.java (엔티티)>

 

<LoggingAspect (AOP 로깅)>

  • @Around: 서비스 메서드 실행 전후로 감싸서 로직 삽입
  • joinPoint.proceed(): 원래 메서드 (placeOrder) 실행
  • AOP의 핵심 장점: 모든 서비스에 공통 로그 작성 가능

로그예시

메서드 [placeOrder] 실행 시간: 68ms

 

@Transactional을 안붙이면??

@Transactional을 Service에 안 붙였기 때문에 트랜잭션이 적용되지 않았고,
예외가 발생했지만 롤백되지 않고 DB에 커밋이 되어버림..