이메일 전송 로직을 동기식으로 했을때 문제점
다음은 email을 전송하는 클래스인 EmailProvider의 email전송 메소드이다
public boolean sendCertificationMail(String email,String certificationNumber){
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
String htmlContent = getCertificationMessage(certificationNumber);
messageHelper.setTo(email);
messageHelper.setSubject(SUBJECT);
messageHelper.setText(htmlContent,true);
javaMailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
return false;
}
return true;
}
다음은 UserService에서 동기식으로 하였을 떄의 로직이다
public EmailCertificationResponse emailCertification(EmailCertificationRequest emailCertificationRequest) {
List<User> users = userRepository.findByEmail(emailCertificationRequest.getEmail());
if(users.isEmpty()) throw new NotFoundEmailExeption("Not Found Email");
if(users.size()>1) throw new DuplicateEmailExeption("Duplicate Email");
emailProvider.sendCertificationMail(emailCertificationRequest.getEmail(), emailCertificationRequest.getEmail());
return EmailCertificationResponse.builder()
.code(SUCCESS_CODE)
.message(SUCCESS_MESSAGE)
.build();
}
이렇게 비즈니스 로직을 구현하게 되면 메소드 안에서의 스레드의 기본 동작은 동기식이다. 따라서 SMTP 서버를 이용해서 이메일을 전송한 후에 응답이 제대로 왔는지 확인한 후에 해당 Json을 반환하기 때문에 속도의 저하가 발생할 수 밖에 없다
-> 그래서 Spring이 기본적으로 제공하는 @Async 어노테이션을 이용하여 별도의 스레드에서 이메일 전송을 비동기로 처리할수 있다
다음은 비동기로 처리하기 위해 @EnableAsync를 붙히고 해당 스레드를 얼마나 사용할지 스레드를 설정하는 부분이다
@EnableAsync
@Configuration
public class AsyncConfig {
@Bean(name = "customAsyncExecutor")
public Executor customAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(30);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("CustomExecutor");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
Async로직은 다른 로직과 달라서 클래스에 작성하였다
@Service
@RequiredArgsConstructor
public class AsyncService {
private final EmailProvider emailProvider;
@Async("customAsyncExecutor")
public void asyncTask(EmailCertificationRequest emailCertificationRequest){
emailCertification(emailCertificationRequest);
}
private void emailCertification(EmailCertificationRequest emailCertificationRequest) {
emailProvider.sendCertificationMail(emailCertificationRequest.getEmail(), certificationNumber());
}
private String certificationNumber(){
int number = (int) (Math.random() * 9000) + 1000;
return String.valueOf(number);
}
}
AsyncService의 메소드에 AsyncConfig에 해당하는 Bean이름을 붙혀주면 설정한 쓰레드풀에서 비동기로 처리하게된다
다음은 UserService를 수정한 부분이다
public EmailCertificationResponse emailCertification(EmailCertificationRequest emailCertificationRequest) {
List<User> users = userRepository.findByEmail(emailCertificationRequest.getEmail());
if(users.isEmpty()) throw new NotFoundEmailExeption("Not Found Email");
if(users.size()>1) throw new DuplicateEmailExeption("Duplicate Email");
asyncService.asyncTask(emailCertificationRequest);
return EmailCertificationResponse.builder()
.code(SUCCESS_CODE)
.message(SUCCESS_MESSAGE)
.build();
}
이렇게 되면 이메일 전송여부를 응답받기전에 200 OK를 내보내게된다
만약 이메일전송이 실패했을 경우에도 따로 예외처리를 해줘야한다
이건 다음에 다루도록 하겠다