ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Hibernate + Redis + MailSender 트랜잭션 동기화
    spring/JPA 2021. 5. 24. 11:31

    배경

    현재 프로젝트에서 사용자가 회원가입을 한 경우 흐름을 보면 DB에 사용자 저장(Hibernate) -> 인증코드 생성 후 이메일로 전송(MailSender) -> Redis에 이메일(key)+인증코드(value) 저장하는 로직이다. 이후 사용자 인증을 시도한 경우 Redis에서 데이터를 찾아 인증을 확인 후 사용자의 권한을 업그레이드한다.

     

    흐름은 크게 3가지로 나뉘고 이 3가지 흐름은 원자적으로 묶여야 한다. 즉, 하나라도 실패한 경우 전부 실패해야하고 성공한다면 전부 성공해야 한다. 트랜잭션과 예외처리를 통해 간단하게 세 프로세스를 원자적으로 묶어보자.

     

     

    상황 분석

    1) redis에 우선 Hello를 key로, World를 value로 저장한다.

    2) Hibernate로 member를 저장한다.

    3) 이메일을 보낸다.

    (각자 구현은 기본구현이므로 스킵한다. 의존성 참고)

    기본적으로 java의 특성상 선행메서드에서 에러가 발생한다면 다음 메서드는 수행되지 않는다. 즉, redis에서 에러가 발생하면 hibernate나 mailsender는 아예 실행되지 않아 트랜잭션을 고려하지 않아도 된다. 문제가 되는 점은 redis는 커밋된 상태에서 hibernate가 에러를 뱉은 경우 또는 redis와 hibernate가 커밋된 상태에서 mailsender가 에러를 뱉은 경우다

     

     

    redis와 hibernate의 동기화

    트랜잭션 자체를 글로벌 트랜잭션으로 동기화하는 방법을 생각했지만 의외로 간단하게 구현할 수 있었다. 공식문서의 내용을 보게 되면 레디스의 setEnableTransactionSupport를 true로 설정하면 스레드로컬에 저장된 트랜잭션과 동기화된 레디스 동작을 수행하도록 할 수 있게 된다. 자세한 건 코드를 봐야 알겠지만 레디스가 내부적으로 트랜잭션이 커밋되는 시점까지 본인의 커밋을 지연시킨 뒤 트랜잭션이 커밋되면 커밋, 롤백되면 롤백하는 것 같다.

     

    동기화 전 테스트

    미리 이름이 yw로 지정된 Member를 저장하고 다시 yw로 저장한면 Unique제약조건으로 에러를 발생시키는 코드다. 아직 트랜잭션을 동기화하지 않았기 때문에 레디스만 값이 저장되고 H2에는 하나의 데이터만, 이메일은 전송되지 않았을 것이다.

    기대한 대로 됐다^^

     

    동기화 후 테스트

    코드는 단지 RedisConfig에서 주석 처리한 부분을 주석 해제하는 것뿐이다. redis의 결과만 확인해보자.

    원하는 대로 결과가 처리된 걸 확인할 수 있다.

     

     

    MailService 동기화

    MailService는 기술적으로 따로 필요한 부분은 없다. @Transactional로 선언한 부분에서 예외가 발생한 경우 모든 트랜잭션은 롤백된다. 이때 MailService에서 메일을 전송하는 부분의 예외는 Checked예외인데 이부분을 try catch하여 UnChecked예외로 전환해서 던져주기만 하면 된다.

     

     

    마치며

    추상화된 아키텍쳐에서 대충 이런식으로 흘러가겠구나는 예상이 가지만 아직 코드레벨에서 트랜잭션을 까보진 못했다. 여유가 된다면 트랜잭션을 관리하는 방법을 깊게 공부하면 많은 도움이 될 것 같다. 프로젝트부터 리팩토링하러 가자!

    댓글

Designed by Tistory.