Java

Transaction

변위니 2025. 1. 7. 02:33

DBMS에서 데이터의 무결성을 보장하기 위한 개념으로, 하나 이상의 데이터베이스 연산을 묶어서, 하나의 작업 단위로 처리하는 것을 의미한다. 여러 연산이 모두 성공해야 커밋되어 데이터베이스에서 반영되고, 하나라도 실패할 경우 롤백하여 이전 상태로 되돌려 데이터의 일관성을 유지한다.

 

 

 

트랜잭션은 ACID 기반으로 동작하여 데이터베이스 시스템은 오류, 하드웨어 장애, 그리고 다른 트랜잭션과의 충돌 상황에서도 데이터의 정확성과 안정성을 보장한다.

 


 

트랜잭션의 특징 (ACID)

  1. Atomicity 원자성
    한 트랜잭션 내의 모든 연산들이 완전히 수행되거나, 전혀 수행되지 않아야 함을 의미한다. 트랜잭션이 부분적으로 실행되는 것을 방지하는 특성이다. ( All or Nothing )
  2. Consistency 일관성
    트랜잭션이 실행되기 전화 후에 데이터베이스가 일관된 상태를 유지해야 함을 의미한다. 
  3. Isolation 고립성
    각각의 트랜잭션은 독립적으로 실행되어, 서로 영향을 미치지 않아야 한다.  
  4. Durability 영속성
    트랜잭션이 성공적으로 완료되면 시스템 오류가 발생하더라도 해당 트랜잭션의 결과가 영구적으로 반영되어야 한다.

 

 

 

 

 


 

트랜잭션이 어떤 것인지 [상품 구매]를 예로 보자

 

 

상품을 구매하는 과정을 살펴보자.

1. 상품의 재고를 확인합니다.
2. 재고가 있다면 장바구니에 상품을 추가합니다.
3. 결제를 진행합니다.
4. 결제가 성공하면 주문을 생성하고 재고를 업데이트합니다.

 

 

이 과정에서 트랜잭션을 사용하지 않으면 어떤 문제가 발생할까?

동시에 여러 사용자가 같은 상품을 구매하려고 할 때, 재고가 부족하더라도 중복으로 결제가 이루어져 재고가 음수가 되거나,  결제는 성공했지만, 주문 생성이 실패하여 결제만 하고 물건을 받지 못하는 상황이 생길 수 있다. 

 

 

 

이런 문제를 막기 위해 트랜잭션이 필요하다. 

 

트랜잭션을 사용하면 구매 과정의 여러 단계(재고 확인, 결제, 주문 생성, 재고 업데이트)를 하나의 작업 단위로 묶어서 처리할 수 있다. 

모든 단계가 성공적으로 완료되면 데이터베이스에 커밋되어 변경 사항이 영구적으로 반영되고, 어느 한 단계라도 실패하면 롤백(Rollback)되어 이전 상태로 되돌아가기 때문에 데이터의 무결성과 일관성을 보장할 수 있다. 

 

 

 

 

 


스프링에서는 @Transactional 어노테이션을 사용하여 트랜잭션을 관리할 수 있다.

 

예제 코드로 보자. 

@Service
public class OrderService {

    ...
    
    @Transactional
    public void orderProcess(Long productId, int quantity, PaymentInfo paymentInfo) {
        Product product = productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));

        // 재고 확인
        if (product.getStock() < quantity) {
            throw new RuntimeException("Not enough stock");
        }

        // 결제 처리
        paymentService.processPayment(paymentInfo);

        // 재고 감소
        product.setStock(product.getStock() - quantity);
        productRepository.save(product);

        // 주문 생성
        Order order = new Order();
        order.setProduct(product);
        order.setQuantity(quantity);
        order.setTotalPrice(product.getPrice() * quantity);
        orderRepository.save(order);
    }
}

@Transactional 어노테이션은 트랜잭션의 시작과 종료를 자동으로 관리한다.
메서드나 클래스에 @Transactional을 붙이면, 해당 메서드 내의 모든 연산이 하나의 트랜잭션으로 묶여 일괄 처리되도록 보장한다.



 

 


영속성 컨텍스트(Persistence Context)

 

 

엔티티(Entity) 객체를 관리하는 환경으로, 주로 ORM 프레임워크에서 사용된다.

엔티티 매니저가 영속성 컨텍스트를 관리하고, 엔티티의 상태 변화를 추적하고 변경 내용을 트랜잭션이 끝날 때 데이터베이스에 반영(flush)한다.

  1. 엔티티의 생명주기
    영속성 컨텍스트는 엔티티의 생명주기를 관리한다.
    엔티티 객체가 생성되어 영속성 컨텍스트에 저장되면 ‘영속 상태’가 되며, 데이터베이스와 동기화된다.
    수정된 엔티티는 트랜잭션이 커밋될 때 자동으로 데이터베이스에 반영된다.
  2. 지연 로딩(Lazy Loading)
    영연관된 엔티티 객체를 실제로 사용할 때까지 데이터베이스 조회를 지연시킬 수 있다.
  3. 동일성 보장
    영속성 컨텍스트는 동일한 엔티티 식별자를 가진 객체에 대해 같은 인스턴스를 반환하여 동일성을 보장한다.




트랜잭션과 영속성 컨텍스트의 상호작용

@Transactional이 적용된 메서드가 호출되면 트랜잭션이 시작되고, 해당 트랜잭션 범위 안에서 영속성 컨텍스트가 활성화된다. 그리고 트랜잭션이 종료될 때(commit or rollback) 영속성 컨텍스트의 변경 내용이 데이터베이스에 반영된다. 

  1. 트랜잭션 범위:
    • 영속성 컨텍스트는 트랜잭션 내에서 엔티티의 상태 변경을 추적한다.
    • 트랜잭션 커밋 시 영속성 컨텍스트는 데이터베이스에 변경 사항을 반영하고, 롤백 시 변경 사항을 취소한다.
  2. 트랜잭션의 격리 수준:
    • 트랜잭션의 격리 수준이란 동시에 여러 트랜잭션이 실행될 때, 한 트랜잭션이 다른 트랜잭션의 연산에 영향을 받지 않도록 하는 정도이다.
    • 격리 수준 : (낮음)READ UNCOMMITTED > READ COMMITTED > REPEATABLE READ > SERIALIZABLE (높음)
    • 격리 수준이 높을수록 동시성 문제를 줄일 수 있지만, 성능 저하가 발생할 수 있다
  3. 데이터 일관성:
    • 영속성 컨텍스트 내의 엔티티는 트랜잭션 동안 데이터베이스와의 일관성을 유지하고, 트랜잭션이 커밋될 때 영속성 컨텍스트의 변경 사항이 데이터베이스에 반영된다.
  4. 플러시(Flushing):
    • 영속성 컨텍스트의 변경 사항을 데이터베이스에 동기화하는 작업
    • 일반적으로 트랜잭션 커밋 시 자동으로 플러시가 발생한다.

즉, 영속성 컨텍스트는 엔티티의 생명주기를 관리하고, 트랜잭션은 데이터베이스 작업을 단위로 묶어 원자적인 실행을 보장한다.

'Java' 카테고리의 다른 글

람다 표현식과 함수형 인터페이스  (0) 2025.01.07
@Transactional과 Proxy  (0) 2025.01.07
@RequiredArgsConstructor, @AllArgsConstructor  (0) 2025.01.07
index와 B-tree 그리고 Hash Index  (0) 2025.01.07
StringBuffer & StringBuilder  (0) 2025.01.07