안녕하세요 여러분 베하입니다!
훌쩍 겨울이 다가왔네요! 다들 올해는 잘 보내고 계신가요?
오늘 이야기 저번 주제를 이어 spring boot의 @Transactional 애노테이션의 commit, rollback 전략을 이야기 하고자 합니다.
사실 저는 이 부분에 대해 몰랐기 때문에 트랜잭션 처리에 있어 많은 삽질을 해버렸습니다… 오늘의 내용이 꼭 도움이 되길 바라는 마음으로 시작하겠습니다.
서론
Spring Boot 애플리케이션을 개발하면서 @Transactional 애노테이션은 데이터베이스 트랜잭션 관리를 용이하게 해주는 중요한 도구 중 하나입니다. 그러나, 이 애노테이션을 사용할 때 어떻게 예외 상황을 처리하고, 언제 커밋 또는 롤백해야 하는지에 대한 전략을 제대로 이해하는 것은 매우 중요합니다. 이 블로그에서는 @Transactional 애노테이션의 사용 시, 상황별로 commit과 rollback 전략에 대해 알아보겠습니다.
상황별 commit, rollback
1. 기본적인 @Transactional 사용
첫 번째로, 기본적인 @Transactional 애노테이션 사용법을 살펴봅시다. 간단한 예제 코드와 함께 어떻게 커밋과 롤백이 작동하는지 설명하겠습니다.
@Service
public class TransactionalService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
throw new Exception("Something went wrong!");
}
}
다음과 같은 코드일 때는 rollback이 되지 않습니다. 그 이유는 @Transcational은 RuntimeException과 Error에 대해서만 rollback 전략으로 되어있기 때문에 일반적인 Exception 들에 대해서는 rollback 되지 않습니다.
여기서 중요한 것은 왜 RuntimeException에 대한 예외에 대해서만 rollback을 하냐 묻는다면 정말 간단합니다.
Exception에러는 개발자가 반드시 처리해야 할 예외입니다.
즉, 개발단계에서 해당 예외를 처리한다고 가정하고 그 외에 예측하지 못하는 RuntimeException에 대해서만(Error 포함) rollback 전략을 수행하는게 앞뒤가 맞다고 판단하는 것 같습니다.
2. 예외 발생 시 롤백
어떤 상황에서는 예외가 발생할 수 있습니다. 이때 @Transactional 애노테이션은 자동으로 롤백됩니다.
@Service
public class TransactionalService {
@Autowired
private UserRepository userRepository;
@Transactional(rollbackFor = Exception.class)
public void createUserWithException(User user) throws Exception {
userRepository.save(user);
throw new Exception("Something went wrong!");
}
}
Exception 클래스의 예외에 대해서도 rollback은 가능합니다. rollbackFor 옵션을 통해 예외 클래스를 지정해서 예외를 처리할 수 있습니다. 하지만 rollbackFor은 커스텀한 예외 클래스에서 rollback 전략을 위해 사용하는게 주 목적입니다.
사실 저는 SQLException에 대해서는 왜 rollback을 하지 않을까 했습니다.
rollbackFor 옵션에 Exception 클래스를 지정했는데도.
그 이유는 간단했습니다. SQLException의 과정에서 오류 로그를 따라가다보면 그 무엇도 Exception을 thorws 하지 않고 저의 트랜잭션까지 도달하지 않습니다.
즉, 그 오류는 개발자가 직접 처리해야 한다는 의미입니다. 그 예외가 있는 상황에서 어플리케이션을 동작하는건 앞 뒤가 안맞는게 당연하겠죠? ㅠㅠ 저는 왜그랬는지….
3. 특정 예외 발생 시 롤백 제어
특정 예외가 발생했을 때 롤백을 원하지 않을 수 있습니다.
@Service
public class TransactionalService {
@Autowired
private UserRepository userRepository;
@Transactional(noRollbackFor = MyCustomException.class)
public void createUserWithCustomException(User user) throws MyCustomException {
userRepository.save(user);
throw new MyCustomException("This is a custom exception, but no rollback!");
}
}
모든 특정 예외에서는 rollback하지 않고 싶을 수 있습니다. 그럴 떄는 noRollbackFor을 사용해서 일부 commit을 진행하고 다른 쿼리들은 commit 되게 하여 개발자의 의도를 구현할 수 있습니다.
4. 여러 메서드 간의 트랜잭션 관리
여러 메서드 간에 하나의 트랜잭션을 공유하고 싶을 때 @Transactional 애노테이션 사용
@Service
public class TransactionalService {
@Autowired
private UserRepository userRepository;
public void createUsers(List<User> users) {
for (User user : users) {
userRepository.save(user);
}
}
}
save함수는 내부적으로 트랜잭션 처리가 구현되어 있습니다.
이때 @Transactional을 사용하지 않은 함수가 save 함수를 쓰다가 예외가 발생하면 createUsers 내에 쿼리들은 모두 commit 되어 버립니다.
결론
Spring Boot에서 @Transactional 애노테이션을 사용할 때, 예외 상황과 커밋 또는 롤백 전략을 제대로 이해하면 데이터베이스 트랜잭션 관리가 훨씬 쉬워집니다. 이 글을 통해 작지만 어떤 상황에서 어떻게 @Transactional 애노테이션을 사용해야 하는지에 대한 지침을 얻었기를 바랍니다.
실제로 try catch 부분에 대한 경우 rollback, commit 전략이 달라집니다. 제가 실제로 테스트해보고 기회가 된다면 또 글을 남겨보겠습니다.
여러분 이제 11월 입니다. 1년이 참 빠르게 지나가는데 여러분은 어떠신가요?
모두 행복한 1년을 마무리 하는 단계가 되었으면 합니다.
그럼 다음에 만나요.
'Database' 카테고리의 다른 글
Cloud Spanner (0) | 2023.11.24 |
---|---|
Redis란 (0) | 2023.11.10 |
[Database] AQueryTool 사용 방법(1) (0) | 2023.10.24 |
구글 태그 매니저 (Google Tag Manager) (0) | 2023.10.12 |
Airflow Task의 BQ Job 다루기 (0) | 2023.09.28 |
댓글