@Transactional , Spring Data JPA 을 같이 쓰는 점에 관하여

@Transactional , Spring Data JPA 를 공부하다 같이 사용할 때
왜 이렇게 동작하는거지? 라는 부분에 대하여 정리합니다.

 

개발을 하다보면 Spring Data JPA 는 기본적으로 트랜잭션을 자동으로 처리해 줍니다.

하지만 @Transactional, Spring Data JPA 를 같이 쓰고 있을 때 @Transactional 이 언제 필요한지, 언제 생략 가능한지에 대해서 세부적으로 알아야 할 것 같아서 정리했습니다.

 

기본적으로 적용되는 트랜잭션

Spring Data JPA 에서 제공하는 기본적인 메서드들은 아래와 같습니다.

 

1. save()

2. findById()

3. delete()

4. findAll() 

 

이러한 메서드들은 자동으로 트랜잭션이 적용되어 있습니다.

이건 Spring 내부적으로 @Transactional 이 이미 붙어 있기 때문입니다.

즉, Repository 에서 기본 제공하는 메서드만 사용한다면 @Transactional 을 따로 붙일 필요가 없습니다.

 

그럼 언제 @Transactional 을 써야 할까?

 

1. 직접 만든 Service 메서드에 여러 DB 작업을 묶어서 처리할 때

@Service
public class MyService {
    
    @Transactional
    public void createUserAndLog() {
        userRepository.save(new User(...));
        logRepository.save(new Log(...));
    }
}

 

위 예제처럼, 여러 저장소 작업이 하나의 트랜잭션으로 묶이길 원할 때 @Transactional 이 필요합니다.

하나라도 실패하면 전체가 롤백되길 원하는 것이죠.

 

즉, 위에 코드는 트랜잭션이 어떻게 동작하냐면

createUserAndLog() 메서드가 호출되면 @Transactional 덕분에 트랜잭션이 시작됩니다.

그 안에서 userRepository.save(...) 호출 후 이건 Spring Data JPA 가 자동으로 트랜잭션 처리되지만, 이미 시작된 트랜잭션 안에서 동작하니까 별도로 커밋되지 않습니다.

그다음 logRepository.save(...) 도 같은 트랜잭션 안에서 실행됩니다.

메서드가 정상적으로 끝나면 Spring 이 전체 트랜잭션을 commit 하고, 중간에 예외가 발생하면 전체가 rollback 됩니다.

그래서 @Transactional 을 createUserAndLog 에 붙이지 않는다면 만약 user 만 저장되고 log 는 rollback 되면
데이터 일관성이 깨질 수 있습니다.

 

2. 읽기 전용 트랜잭션을 명시하고 싶을 때

@Transactional(readOnly = true)
public List<User> getAllUsers() {
    return userRepository.findAll();
}

 

생각해 보면 꼭 필요하지는 않지만, 성능 최적화를 위해 읽기 전용임을 명시할 수 있습니다.

JPA 는 readOnly = true 인 경우 dirty checking( 변경 감지 ) 를 생략해서 성능이 좋아질 수 있습니다.

readOnly = true 를 붙이면 JPA 가 아래와 같이 생각합니다.
"어차피 변경 안 할 거지? 그럼 내가 굳이 변경 감지를 위한 스냅샷( 초기 상태 저장 ) 같은 것을 안 하고 더 빠르게 처리할게!!" 

 

변경 감지란?

JPA 핵심 기능 중 하나로, 영속성 컨텍스트에 있는 엔티티가 변경됐는지 자동으로 감지해서 DB 에 반영하는 것 을 말합니다.

@Transactional
public void updateUsername(Long id, String newName) {
    User user = userRepository.findById(id).get(); // 1. 엔티티를 영속 상태로 가져옴
    user.setUsername(newName);                     // 2. 객체의 값을 변경함
    // 3. save() 같은 걸 안 해도...
    // 4. 트랜잭션 끝날 때 JPA가 변경됐는지 감지해서 DB에 update 쿼리 날림!
}

 

예를 들어 jpaReository 를 상속받은 userRepository 메서드를 사용하고 있을 때 이게 바로 변경 감지입니다.

JPA 는 트랜잭션이 끝나기 직전에, "어? 너 가져온 객체 값이 바뀌었네? 그럼 update 쿼리 날려줄게!" 하고 자동으로 반영해 줍니다.

꿀팁
Spring Data JPA 에서 지원하는 메서드중 findById, findAll 등 조회하는 메서드는 기본적으로 readOnly = true 트랜잭션이 자동으로 적용되어 있습니다. ( save, delete 등 변경 메서드는 false 가 적용됩니다. )

 

3. 실수 방지용 / 예외 상황 대비

가령 " 조회만 하려고 만든 메서드" 에 실수로 save() 같은 걸 호출해도 @Transactional(readOnly = true) 가 있으면 쓰기 시도 시 예외가 터집니다. 즉, 조기 방지 기능을 사용할 수 있다는 말입니다.

'Spring' 카테고리의 다른 글

다양한 패턴  (0) 2025.04.25
트랜잭션 프록시와 예외  (0) 2025.04.08
@PostConstruct , @PreDestory  (0) 2025.03.26
스프링의 메세지, 국제화  (0) 2025.02.13
@valid vs @validated  (0) 2025.02.03