회원 가입 시 이메일을 보내야 한다고 가정해 봅시다.
회원 가입 -> 가입 완료 웰컴 이메일 전송
이때 다같이 하나의 트랜잭션에 묶여있을 때 회원 가입에는 오류가 없고 이메일 전송 시 오류가 생겨 트랜잭션이 롤백이 된다면 다시 처음부터 회원가입을 해야합니다.. 이런 설계는 좋지 않은 설계라고 생각합니다
이메일 전송은 부가적인 서비스이고 회원 가입에는 지장이 없어야 합니다
이럴 때 @TransactionalEventListener 을 사용 할 수 있습니다
해당 트랜잭션의 상태(COMMIT, ROLLBACK)에 따라 이벤트를 처리해주는 이벤트 리스너입니다
option
위와 같은 옵션에서 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());
}
}
위 로직은 서비스 안에서 묶인 회원가입 + 이메일에 대한 강결합을 완화 시켜주고 회원가입에 예외가 생기면 이벤트 메서드가 동작하지 않고 회원가입 성공 이후에만 동작하기 때문에 트랜잭션 문제를 해결 할 수 있습니다