spring @TransactionalEventListener 사용

김진욱·2024년 1월 25일

시나리오

회원 가입 시 이메일을 보내야 한다고 가정해 봅시다.

회원 가입 -> 가입 완료 웰컴 이메일 전송

이때 다같이 하나의 트랜잭션에 묶여있을 때 회원 가입에는 오류가 없고 이메일 전송 시 오류가 생겨 트랜잭션이 롤백이 된다면 다시 처음부터 회원가입을 해야합니다.. 이런 설계는 좋지 않은 설계라고 생각합니다
이메일 전송은 부가적인 서비스이고 회원 가입에는 지장이 없어야 합니다
이럴 때 @TransactionalEventListener 을 사용 할 수 있습니다

@TransactionalEventListener 이란

해당 트랜잭션의 상태(COMMIT, ROLLBACK)에 따라 이벤트를 처리해주는 이벤트 리스너입니다

option

  • @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    - commit 이후 이벤트를 실행합니다.
  • @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    - rollback 이후 이벤트를 실행합니다.
  • @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    - commit 또는 rollback 이후 이벤트 실행합니다.
  • @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    - commit 되기 전에 이벤트를 실행합니다.

실습

위와 같은 옵션에서 AFTER_COMMIT을 사용하면 회원가입 로직에 영향을 주지 않고 이메일 전송 오류 여부 상관없이 회원가입을 진행 할 수 있습니다.

@Service
@RequiredArgsConstructor
@Transactional
public class TestMemberService {

    private final MemberRepository repository;
    // TransactionalEventListener에서 구독하는 이벤트를 발행하는 주체
    private final ApplicationEventPublisher applicationEventPublisher;


    public SimpleMemberDto register(MemberRegisterDto dto) {
        // A : 회원 가입 : member 생성
        Member member = repository.save(Member.create(dto));
        // B : 웰컴 이메일 !
        sendMessage(member);
        // C : dto 반환
        return SimpleMemberDtoMapper.INSTANCE.toDto(member);
    }

    private void sendMessage(Member member) {
        applicationEventPublisher.publishEvent(new SendEmailEvent(this, member));
    }
}
// ApplicationEventPublisher 가 발행 할 이벤트 클래스,  ApplicationEvent 상속
@Getter
public class SendEmailEvent extends ApplicationEvent {
    private Member member;
    
    public SendEmailEvent(Object source, Member member) {
        super(source);
        this.member = member;
    }
    
}
@Component
@Transactional
@RequiredArgsConstructor
public class TransactionalEventListenerTest {

    private final AmqpService amqpService;

	// ApplicationEventPublisher 에서 발행한 이벤트를 구독
    @TransactionalEventListener
    public void sendEmail(SendEmailEvent event) {
        amqpService.sendEmailToMember(event.getMember());
    }
}

강결합 완화, 트랜잭션 문제 해결

위 로직은 서비스 안에서 묶인 회원가입 + 이메일에 대한 강결합을 완화 시켜주고 회원가입에 예외가 생기면 이벤트 메서드가 동작하지 않고 회원가입 성공 이후에만 동작하기 때문에 트랜잭션 문제를 해결 할 수 있습니다

profile
2021.12~ 공부의 기록

0개의 댓글