개발/Spring Boot

Spring 테스트할 때 @Transactional 써야 할까 말아야 할까

registry 2023. 7. 30. 23:18

Spring으로 테스트코드를 작성하다 보면 한 번쯤은 이런 고민을 하게 된다. (안 했으면 죄송함다)

“테스트에 @Transactional 붙여야 하나? 붙이면 자동 DB 데이터 rollback 되이서 편하긴 한데..”

 

어떤 글은 “무조건 붙여라”고 하고,
어떤 글은 “절대 쓰지 말라”고 한다.

그래서 더 헷갈린다.

 

결론부터 말하면 정답은 없다.
대신 용도에 따라 명확히 갈린다.

 

이 글에서는

  • @Transactional이 정확히 어떤 역할을 하는지
  • 테스트에서 사용할 때의 진짜 장점과 진짜 단점
  • 실무에서 언제 쓰고, 언제 피해야 하는지

를 정리해보려고 한다.


@Transactional 의미

@Transactional은 Spring이 제공하는 선언적 트랜잭션 관리 어노테이션이다.
해당 메서드(또는 클래스)는 트랜잭션 경계 안에서 실행되며,

  • 정상 종료 → commit
  • 예외 발생 → rollback

여기서 중요한 포인트는 이거다.

원자성 보장은 Spring의 기능이 아니다.
그건 DB 트랜잭션의 ACID 특성이고,
Spring은 트랜잭션을 시작하고 종료해주는 역할을 할 뿐이다.

테스트에서 @Transactional이 특별한 이유

Spring Test 환경에서는 @Transactional이 조금 다르게 동작한다.

@SpringBootTest
@Transactional
class UserServiceTest {
    ...
}

이렇게 테스트 클래스나 메서드에 붙이면,

테스트 메서드가 끝나는 순간 자동으로 rollback 된다. (기본값)

즉, commit이 발생하지 않는다.

장점 1. DB 상태를 신경 쓰지 않아도 된다

  • 매 테스트마다 DB를 초기화하지 않아도 되고
  • 테스트 간 데이터가 섞일 일도 없고
  • AfterEach에서 delete 치는 코드도 필요 없다

테스트 격리를 아주 싸게 얻을 수 있다!

장점 2. 테스트 코드가 깔끔해진다

@Transactional을 테스트에 붙이면
테스트가 끝날 때마다 자동으로 rollback 되기 때문에
데이터 정리 코드를 전부 제거할 수 있다.

원래라면 이런 코드가 따라온다.

  • 테스트 끝나고 deleteAll()
  • 테이블 truncate
  • 테스트 전용 데이터 정리 로직

그런데 @Transactional을 쓰면 이게 전부 필요 없다.

@SpringBootTest
@Transactional
class UserServiceTest {

    @Test
    void 회원_저장_테스트() {
        userRepository.save(user);
    }
}

정리 코드도 없고, 상태 복구 로직도 없다.

단점 1.  실제 환경을 반영하지 못한다

테스트에 @Transactional을 붙이면,
서비스 코드의 트랜잭션 경계가 사라진다.

예를 들어,

    @Transactional
    public void serviceA() {
        repository.save(...);
    }

그리고 테스트에서

    @Transactional
    @Test
    void test() {
        serviceA();
    }

이렇게 되면,

  • 실제 서비스: serviceA에서 트랜잭션 시작
  • 테스트 환경: 이미 테스트 트랜잭션이 열려 있어서 serviceA는 참여만 함

즉,

트랜잭션 전파, 분리, 중첩 같은 동작을 검증할 수 없게 된다.
트랜잭션 구조 자체가 왜곡된다.

단점 2. 커밋 시점에 터지는 버그를 못 잡는다

운영에서 자주 터지는 버그들을 보면 이런 것들이 있다.

  • unique constraint 위반
  • flush 시점 오류
  • cascade 설정 오류

이런 것들은 대부분 commit 시점에 터진다.

그런데 테스트에서 rollback만 하고 commit을 안 하면?

운영에서는 터질 버그가, 테스트에서는 안 터진다.

그래서 언제 쓰고, 언제 쓰지 말아야 할까?


한 줄 정리

@Transactional은 테스트를 편하게 만들어주지만, 실제 동작과 다르게 동작할 수 있다.

결정은 동료들과 얘기를 해서 결정!

'개발 > Spring Boot' 카테고리의 다른 글

MDC를 활용한 추적 가능한 로그 남기기  (1) 2024.04.18
JPA 복합키 @IdClass vs @EmbeddedId  (0) 2023.07.24