본문 바로가기
Database

Transactional 애노테이션 상황별 commit, rollback 전략

by BTC_동동 2023. 10. 27.

안녕하세요 여러분 베하입니다!

훌쩍 겨울이 다가왔네요! 다들 올해는 잘 보내고 계신가요?

오늘 이야기 저번 주제를 이어 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

댓글