이 글에서 메일 보내기를 구현하고 나니 메일을 비동기로 보내야겠다는 생각이 들었다.
기존 프로젝트에서 가입과 메일 보내는걸 동기적으로 처리했다가 한참동안 기다려야 했던 기억이 나서 이번에는 꼭 스레드를 써봐야겠다고 마음을 먹었다.
그래서 찾아보니 SpringBoot에서는 쓰레드 생성이 어렵지 않았다.
예전에는 테스크 잡인가 뭔가 해서 진짜 복잡하게 돌렸던 기억이 나는데...
역시 스프링부트가 최고다.
1. AsyncConfig.java 추가
package me.huiya.core.Config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2); // 기본적으로 실행 대기 중인 Thread 개수
executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수
executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함)
// 단, MaxPoolSize가 초과되면 Thread 생성에 실패할 수 있음.
// 참고: https://medium.com/trendyol-tech/spring-boot-async-executor-management-with-threadpooltaskexecutor-f493903617d
// jooncco님 수정사항
executor.setThreadNamePrefix("async-"); // Spring에서 생성하는 Thread 이름의 접두사
executor.initialize();
return executor;
}
}
@EnableAync 어노테이션이 붙은 Configuration를 하나 추가해준다.
각 설정값에 대한 설명은 코드에 주석을 남겨놨다.
2. 기존 메소드에 @Async 어노테이션 추가
package me.huiya.core.Service;
import java.beans.ConstructorProperties;
import java.io.IOException;
import java.util.HashMap;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
@Component
public class Email {
// Autowired 대신 추천되는 의존성 주입 방식
private static JavaMailSender javaMailSender;
private static SpringTemplateEngine templateEngine;
@ConstructorProperties({"JavaMailSender", "SpringTemplateEngine"})
public Email(JavaMailSender javaMailSender, SpringTemplateEngine templateEngine) {
this.javaMailSender = javaMailSender;
this.templateEngine = templateEngine;
}
/**
* 이메일 발송 함수
* @param title 이메일 제목
* @param to 받는 사람
* @param templateName 이메일 템플릿
* @param values 이메일에 들어가는 값
* @throws MessagingException
* @throws IOException
*/
@Async
public void send(String title, String to, String templateName, HashMap<String, String> values) throws MessagingException, IOException {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
//메일 제목 설정
helper.setSubject(title);
//수신자 설정
helper.setTo(to);
//템플릿에 전달할 데이터 설정
Context context = new Context();
values.forEach((key, value)->{
context.setVariable(key, value);
});
//메일 내용 설정 : 템플릿 프로세스
String html = templateEngine.process(templateName, context);
helper.setText(html, true);
//메일 보내기
javaMailSender.send(message);
}
}
기존 글과 비교해보면 달라진 부분은 @Async 어노테이션 하나 뿐이다.
Autowired 관련해서 코드를 조금 변경한게 있는데 비동기 실행과는 전혀 상관 없다.
컨트롤러 쪽은 건들지 않아도 된다.
한가지 조심해야 할 점은 @Async 어노테이션은 private에서는 비동기로 동작하지 않으며, public에서만 비동기로 작동한다고 한다.
3. 비교
같은 데이터를 보냈을때 기존 코드로는 3.26초나 걸린 반면, 비동기를 적용하고 나니 77ms 밖에 걸리지 않았다. 단순 계산으로도 40배 이상 빨라졌다.
물론 사용자에게 응답이 리턴된 이후에도 뒤에서는 열심히 스레드가 돌고 있다.
옛날과 달리 스레드 적용이 어렵지도 않으니 메일 발송과 같은 시간이 오래 걸리는 작업을 실행할때는 스레드를 적극적으로 사용해야 할 것 같다.
출처
https://www.hanumoka.net/2020/07/02/springBoot-20200702-sringboot-async-service/
'개발 > Backend' 카테고리의 다른 글
[Spring Boot] Log4J2 shell 취약점 대응하기 (0) | 2021.12.13 |
---|---|
[Spring Boot] 인터셉터 인증 처리 제외 어노테이션 만들기 (0) | 2021.07.15 |
[Spring Boot] html 템플릿 메일 보내기 - Thymeleaf (0) | 2021.07.13 |
Access-Control-Allow-Origin을 넣어도 POST에서 CORS 에러가 발생할때 (0) | 2020.07.09 |
Apache start, stop 시 별도 스크립트 실행 (0) | 2019.06.21 |