스프링 부트에서의 DB 트랜잭션은?
트랜잭션이란?
DBMS에서 일련의 연산들을 하나의 단위로 묶어 처리하는 작업이다.
한 작업 단위는 모두 성공하거나 모두 실패해야 하고, 부분적으로만 성공하는 상태는 허용되지 않는다.
트랜잭션은 데이터베이스의 일관성을 유지하고 데이터 무결성을 보장하기 위해 중요한 역할을 하고있다.
스프링 부트는 트랜잭션 관리를를 위해 다양한 기능을 제공하고 있고, 덕분에 디비 작업은 일관성과 무결성이 보장되고 있다.
@Transactional 애너테이션을 사용하면 코드의 간결성과 가독성을 높이면서도 트랜잭션의 일관성과 무결성을 보장할 수 있다!
트랜잭션 전파 속성, 격리 수준, 롤백 조건 등을 적절히 활용하여 안정적이고 신뢰할 수 있는 데이터베이스 작업을 구현하는 것이 상당히 중요하당
트랜잭션의 기본 개념
스프링 부트의 트랜잭션 관리는 기본적으로 ACID 원칙을 준수한다.
- 원자성(Atomicity): 트랜잭션 내의 모든 작업이 성공적으로 완료되거나 모두 실패
- 일관성(Consistency): 트랜잭션 완료 후 데이터베이스는 모든 무결성 제약 조건 충족
- 격리성(Isolation): 트랜잭션 간의 간섭을 방지하여 독립적으로 실행
- 지속성(Durability): 트랜잭션이 완료된 후에는 시스템 오류가 발생해도 결과 영구적으로 반영
스프링 부트에서의 트랜잭션 관리
스프링 부트에서는 주로 @Transactional
애너테이션을 사용해 선언적 트랜잭션(메서드나 클래스 수준에서 트랜잭션 속성을 설정)을 관리한다.
선언적 트랜잭션 관리
@Transactional
애너테이션을 사용하면
트랜잭션 전파(Propagation), 격리 수준(Isolation), 시간 초과(Timeout), 읽기 전용(Read-Only) 등의 속성을 설정할 수 있다.
기본 사용법
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
@Transactional(readOnly = true)
public User getUser(Long id) {
return userRepository.findById(id).orElse(null);
}
}
createUser
메서드는 트랜잭션 내에서 실행되고, getUser
메서드는 읽기 전용 트랜잭션으로 실행된다.
트랜잭션 전파 속성
트랜잭션 전파 속성은 기존 트랜잭션이 존재하는 경우 새로운 트랜잭션을 어떻게 처리할지를 정의한다.
스프링은 여러 전파 옵션:
- REQUIRED: 기본 전파 속성, 현재 트랜잭션이 존재하면 이를 사용하고, 없으면 새로운 트랜잭션 생성
- REQUIRES_NEW: 항상 새로운 트랜잭션을 생성하며, 기존 트랜잭션이 존재하면 이를 일시 중단
- MANDATORY: 반드시 현재 트랜잭션이 존재해야 하며, 없으면 예외 발생
- SUPPORTS: 현재 트랜잭션이 존재하면 이를 사용하고, 없으면 트랜잭션 없이 실행
- NOT_SUPPORTED: 트랜잭션 없이 실행하며, 현재 트랜잭션이 존재하면 이를 일시 중단
- NEVER: 트랜잭션이 존재하면 예외 발생
- NESTED: 현재 트랜잭션 내에서 중첩된 트랜잭션 생성
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createOrder(Order order) {
orderRepository.save(order);
}
createOrder
메서드는 항상 새로운 트랜잭션 생성해 실행
트랜잭션 격리 수준
아래는 동시에 실행되는 여러 트랜잭션 간의 상호 작용 제어 정도다.
- DEFAULT: 기본 데이터베이스의 격리 수준 따름
- READ_UNCOMMITTED: 가장 낮은 격리 수준으로, 커밋되지 않은 데이터 읽음
- READ_COMMITTED: 커밋된 데이터만 읽을 수 있음
- REPEATABLE_READ: 트랜잭션 동안 동일한 데이터를 여러 번 읽어도 동일한 결과 보장
- SERIALIZABLE: 가장 높은 격리 수준으로, 트랜잭션을 직렬화하여 실행
@Transactional(isolation = Isolation.SERIALIZABLE)
public void processData() {
// 트랜잭션 코드
}
processData
메서드는 가장 높은 격리 수준인 SERIALIZABLE
로 실행
프로그래매틱 트랜잭션 관리
스프링 부트는 PlatformTransactionManager
와 TransactionTemplate
을 통해 프로그래매틱 트랜잭션 관리도 지원한다.
이는 코드 내에서 명시적으로 트랜잭션을 시작하고 종료할 수 있게 된다.
@Service
public class ProductService {
@Autowired
private PlatformTransactionManager transactionManager;
public void addProduct(Product product) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(status -> {
// 트랜잭션 내에서 실행될 코드
productRepository.save(product);
return null;
});
}
}
addProduct
메서드는 TransactionTemplate
을 사용하여 프로그래매틱하게 트랜잭션을 관리한다.
트랜잭션 롤백 처리
@Transactional
애너테이션을 사용하여 특정 예외가 발생했을 때 트랜잭션을 롤백하도록 설정할 수 있다.
@Transactional(rollbackFor = Exception.class)
public void updateAccount(Account account) throws Exception {
accountRepository.save(account);
if (someCondition) {
throw new Exception("Rollback this transaction");
}
}
updateAccount
메서드는 Exception
이 발생할 경우 트랜잭션을 롤백한딩
'Back-end > SpringBoot' 카테고리의 다른 글
AWS S3 사용하는법! (2) | 2024.07.15 |
---|---|
Persistence Context란 무엇일까? (1) | 2024.07.15 |
JWT 토큰 기반 로그인 구현하기(1) (0) | 2024.07.08 |
Bean이란 무엇일까? (0) | 2024.06.24 |
의존성 주입이란 무엇일까? (0) | 2024.06.23 |