-
@Transactional 왜 안됨..?카테고리 없음 2022. 8. 6. 15:04
우아한 테크코스 Level3 팀 프로젝트 서비스에서 smtp를 통한 email 인증 기능이 있었습니다.
public void sendCodeToValidUser(EmailRequest emailRequest) { String serialNumber = encryptor.encrypt(emailRequest.getEmail()); authService.validateSignUpMember(serialNumber); String authCode = saveAuthCode(serialNumber); sendEmail(emailRequest, authCode); } @Transactional protected String saveAuthCode(String serialNumber) { authCodeRepository.deleteAllBySerialNumber(serialNumber); String authCode = authCodeGenerator.generate(); authCodeRepository.save(new AuthCode(authCode, serialNumber)); return authCode; } private void sendEmail(EmailRequest emailRequest, String authCode) { emailSender.send(emailRequest.getEmail(), authCode); }
emailSender.send가 호출되면 사용자에게 이메일을 발송을 하는데, 이메일 발송하는데 4-5초 가량의 시간이 걸려서 sendCodeToValidUser 메서드에 트랜잭션을 걸지 않았습니다. 이메일을 발송하는 오랜 시간 동안, 다른 요청에 따른 DB 접근이 불가능한 것을 피하기 위해서였습니다.
예상대로 동작하지 않음. 왜?
그 이유는 아래와 같습니다.
저희는 saveAuthCode가 호출되었을 때, 트랜잭션이 열릴 것이라고 예상했습니다. 하지만, 객체의 진입 메서드(위 코드에서는 sendCodeToValidUser)에 @Transactional이 걸리지 않으면, 내부에서 @Transactional이 걸린 메서드(saveAuthCode)가 호출되어도 Transaction이 열리지 않습니다.
authCodeRepository.save는 동작하고 authCodeRepository.delete는 작동하지 않는 이유..?
@Transactional protected String saveAuthCode(String serialNumber) { authCodeRepository.deleteAllBySerialNumber(serialNumber); // 1 String authCode = authCodeGenerator.generate(); authCodeRepository.save(new AuthCode(authCode, serialNumber)); // 2 return authCode; }
내부에서 호출된 saveAuthCode 메서드에서
1으로 표시한 authCodeRepository.deleteAllBySerialNumber는 수행되지 않고,2로 표시한 authCodeRepository.save는 수행이 잘 되었습니다.
따라서, 해당 메서드가 호출되면 save만 되고 delete는 되지 않아서 데이터가 계속 쌓이는 상황이었습니다.
아니 트랜잭션이 안열리는데 이게 왜 되는거지..?
DataJpaRepository의 save에는 @Transactional이 걸려있다.
public interface AuthCodeRepository extends JpaRepository<AuthCode, Long> { Optional<AuthCode> findBySerialNumber(String serialNumber); void deleteAllBySerialNumber(String serialNumber); }
저희 팀에서 작성한 AuthCodeRepository입니다. JpaRepository 인터페이스를 상속하고 있습니다.
JpaRepository 인터페이스를 구현한 구현체의 save 메서드에 @Transactional이 걸려있습니다.
Service 객체에서 Transaction이 열리지 않았어도, AuthCodeRepository는 save 메서드가 객체의 진입 메서드이기 때문에, save가 호출되는 순간 Transaction이 열리는 것입니다.
하지만, 저희가 정의한 deleteAllBySerialNumber에는 @Transactional을 걸어주지 않았기 때문에, Service에서도 Transaction이 열리지 않았고, deleteAllBySerialNumber이 호출될 때도 Transaction이 열리지 않기 때문에 삭제가 되지 않습니다.
그래서 어떻게 해결했는데?
팀 결론 : 일단 성능을 고려하지 말고.. 기능 구현이 급하니까, 추후에 성능 개선을 하자..!
@Transactional public void sendCodeToValidUser(EmailRequest emailRequest) { String serialNumber = encryptor.encrypt(emailRequest.getEmail()); authService.validateSignUpMember(serialNumber); String authCode = saveAuthCode(serialNumber); sendEmail(emailRequest, authCode); } private String saveAuthCode(String serialNumber) { authCodeRepository.deleteAllBySerialNumber(serialNumber); String authCode = authCodeGenerator.generate(); authCodeRepository.save(new AuthCode(authCode, serialNumber)); return authCode; } private void sendEmail(EmailRequest emailRequest, String authCode) { emailSender.send(emailRequest.getEmail(), authCode); }
성능 때문에 내부 메서드에서 @Transactional을 걸었기 때문에, 일단 성능 개선을 우선순위를 미루기러 했습니다. 추후에, 위의 성능 이슈를 해결하고 꼭 다시 공유하도록 하겠습니다~!
끗.
Written By Chris(공병주)