Back-End

[Spring boot] Transactional의 함정

Minch13r 2025. 8. 15. 16:14

업무 중 실제 겪은 일이지만, 보안상 다른 예시로 대체하겠다. CRUD의 내용은 같으니 참고하면 좋을 것 같다.

 

경험

회원 가입이 됐을 경우 쿠폰을 발급되고 DB에 저장되는 기능을 개발하고 있었다.

 

Service Layer가 길어지는게 보기 싫었던 나는 Event Listner를 사용하기로 결정했다.

@Transactional
public void signUp(User user) {
        // 1. 회원 정보 저장
        memberRepository.save(user);

        // 2. 회원가입 완료 이벤트 발행
        eventPublisher.publishEvent(new UserSignedUpEvent(user.getId()));
    }

 

회원가입이 되면 이벤트를 발생시키는 코드

 

@EventListener
    public void handleUserSignedUpEvent(UserSignedUpEvent event) {
        // 이벤트가 발행되면 쿠폰을 발급
        User newUser = memberRepository.findById(event.getUserId())
                                    .orElseThrow(() -> new EntityNotFoundException("회원 못찾음"));
        
        couponService.issueCoupon(newUser);
    }

 

이벤트가 발행되면 쿠폰을 발급시키는 EventListner 코드이다.

 

여기서 문제점이 생겼다. 분명 회원가입이 완료되면, 그 회원에게 쿠폰을 발행시키도록 하는 완벽한 설계라 생각했다.

 

하지만 EventListener에서 memberRepository.findById(...)를 실행할 때, 회원을 찾지 못해서 EntityNotFoundException이 터지는 현상이 계속 발생했다.

 

이유를 모르겠으니 EntityNoFoundException이 터질 때마다 내 속도 터져갔다.

 

DB 문제일까? 로직 문제일까? 여러 고민을 해보고, Debug로 하나하나 메세드를 짚어가며 찾아봤다. 답이 안 나왔다.

 

CS를 공부해보니 AOP가 문제가 아닐까? 라는 생각이 들어 @Transactional을 공부해보니 알 수 있었다.


원인

@EventListener는 기본적으로 @Transactional 메서드와 같은 트랜잭션 안에서 동작한다.

 

signUp 메서드가 아직 커밋(Commit)되기 전에 handleUserSignedUpEvent가 호출된 것이다.

 

즉, 회원가입이 다 완료가 안 됐는데 EventListner를 호출해 쿠폰을 발급한 것이다.

 

DB에 회원 정보가 최종적으로 반영이 안 됐는데, EventListner가 "DB 가서 회원 찾아오렴" 이러는데 어떻게 찾을까

 

정신이 번쩍 들었다.


@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUserSignedUpEvent(UserSignedUpEvent event) {
	...
}

 

코드를 이와 같이 바꿨다

 

phase = TransactionPahse.AFTER_COMMIT 옵션은 singUp 메서드의 트랜잭션이 성공적으로 커밋된 후에야 쿠폰 발급 로직이 실행되도록 하는 것이다.

 

이와 같이 Transaction에 옵션을 걸어 관리한다면 보다 더 편리하고 AOP에 걸리지 않게 개발할 수 있다.

 

결론 : Transcation 알고 쓰자. 남발하지 말자.

'Back-End' 카테고리의 다른 글

LDAP  (3) 2025.09.02
[Spring boot] 중앙 집중식 예외 처리  (2) 2025.08.14
동기와 비동기  (1) 2025.05.30
[Redis] 기본 개념  (1) 2025.05.26
[Java] JPA 영속성  (1) 2025.05.23