우선 트랜잭션은 간단하게 설명하면, 하나의 안전한 작업을 보장합니다.
계좌이체를 예시로 들어보겠습니다.
- A 잔고 5000원 감소
- B 잔고 5000원 증가
A 잔고에서 5000원이 감소하고 나서, B 잔고가 5000원 증가하기 전에 이 과정이 실패했다고 가정해봅시다. 그러면 A 잔고에서만 5000원이 감소하는 문제가 발생합니다.
이 때 데이터베이스가 제공하는 트랜잭션 기능을 사용하면, 둘 다 함께 성공해야 저장하고, 중간에 실패한다면 이전의 상태로 돌아갑니다. 이 때 데이터베이스에 정상 반영하는 것을 커밋(Commit), 작업 중 하나라도 실패해서 이전 상태로 돌아가는 것을 롤백(Rollback)이라고 합니다.
스프링에서는 @Transactional 을 통해 트랜잭션을 수행합니다.
그런데 ‘단순히 DB에 조회하는 작업에도 트랜잭션이 필요할까?’라는 궁금증이 생겼습니다. 결론부터 말하면 YES입니다. 이제 그 이유에 대해서 알아보겠습니다.
지연 로딩(Lazy Loading)
@Transactional 은 메서드 실행 동안 영속성 컨텍스트를 활성화합니다. 지연 로딩된 연관관계의 Entity는 실제로 접근할 때 로딩됩니다. 이 때 영속성 컨텍스트가 활성화되어 있어야 합니다. 간단한 예시를 들어보겠습니다.
@Entity
public class User {
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
}
@Service
public class UserService {
@Transactional(readOnly = true)
public User getUserWithOrders(Long userId) {
User user = userRepository.findById(userId);
List<Order> orders = user.getOrders();
return user;
}
}
이 때 @Transactional 이 없으면 영속성 컨텍스트가 닫혀있으므로, 지연 로딩된 Entity에 접근하면 예외가 발생합니다. 이 때 @Transactional 을 사용하여 이러한 예외를 방지하고 안전하게 지연 로딩을 사용할 수 있습니다.
성능 최적화
@Transactional(readOnly = true) 옵션을 주게되면 쓰기 지연이 생략됩니다. 읽기 전용 트랜잭션에서는 Entity 변경 감지를 수행하지 않으므로 성능 오버헤드가 감소합니다. 플러시 작업을 수행하지 않으므로, 스냅샷 비교와 같은 로직을 수행하지 않습니다.
데이터 일관성 유지
트랜잭션 내에서 발생하는 SELECT와 밖에서 발생하는 SELECT는 Read Commited 격리 수준에서는 커밋이 완료된 데이터만 조회 가능하기에 차이가 없습니다.
하지만, Repeatable Read 격리 수준에서는 차이가 발생합니다. Repeatable Read 에서는 트랜잭션이 시작되기 전에 커밋된 내용에 관해서만 조회할 수 있기 때문에 트랜잭션 동안 아무리 다른 트랜잭션에서 데이터를 변경하고 커밋하더라도 같은 결과를 조회하게 됩니다.
반면에 트랜잭션 밖의 SELECT의 경우 같은 메서드 안에 여러 개의 SELECT가 있을 때, 다른 트랜잭션이 데이터를 변경하게 되면 SELECT마다 다른 결과가 나타날 수 있습니다. 결국 데이터의 정합성이 보장받지 못하게 됩니다.
JPA의 격리 수준 기본값은 데이터베이스에서 설정한 트랜잭션 격리 수준을 사용하는 DEFAULT 입니다. 이후 추가적으로 개발자가 직접 격리 수준을 지정할 수 있지만, 이는 상당히 드물다고 합니다.
- MySQL : Repeatable Read
- Oracle : Read Commited
- MariaDB : Repeatable Read
결론
지금까지 살펴본 내용을 보면, SELECT 작업을 하는 메서드에도 트랜잭션을 적용시켜야하는 것을 알 수 있습니다. ‘조회만 하는 작업에 트랜잭션이 필요할까?’라는 궁금증을 해결할 수 있는 시간이었습니다.
'스프링' 카테고리의 다른 글
@Modifying의 동작 알아보기 (0) | 2025.01.23 |
---|---|
[JPA] 연관관계의 주인이란? (0) | 2025.01.13 |
Mybatis vs JPA (0) | 2025.01.08 |
스프링 트랜잭션 (0) | 2025.01.07 |
테스트는 왜 필요할까? (2) | 2024.12.22 |