실제로 있었던 일이다. 관리자가 "전체 발송" 버튼을 눌렀다. 화면이 멈췄다. 5초... 10초... 30초... 그리고 떴다. 504 Gateway Timeout. 100명한테 메일 한 통 보내려다 서버가 드러누운 것이다..!
원인은 어이없을 만큼 단순했다.
메일을 for문으로 한 통씩, 동기로 보내도록 작성한 코드였다.
1명당 1초면 100명은 100초.. 내 논리는 틀리지 않았다.
이 글은 그 문제를 해결하는 3가지 전략과, 최종적으로 비동기 + 스레드풀을 선택한 이유를 기록한다.
@Service
@RequiredArgsConstructor
public class OwnerService {
private final JavaMailSender javaMailSender;
public void sendNotificationEmails(String toEmail, String subject, String text) {
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
// 중요: 여기서 설정한 주소가 수신자에게 보입니다.
// 단, Resend에 등록된 도메인(@smuclub.com)이어야 합니다.
helper.setFrom("[email protected]", "SMU-CLUB 알림");
helper.setTo(toEmail);
helper.setSubject(subject);
helper.setText(text, true); // true = HTML 허용
javaMailSender.send(message);
} catch (Exception e) {
e.printStackTrace();
// 예외 처리 로직
}
}
}
...
// 이걸 100번 호출
for (User user : users) {
mailService.sendNotificationEmails(user.getEmail(), subject, text);
}
만약 100명에게 보낸다면, 1명당 1초만 걸려도 사용자는 100초동안 로딩 화면을 봐야한다.
서버 부하를 줄이고 효율적으로 보내는 전략은 다음과 같다.
public void sendAllTogether(List<String> receivers, String subject, String text) {
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom("[email protected]", "SMU-CLUB 알림");
helper.setTo("[email protected]"); // 받는 사람은 발신자 자신으로 표기 (관례)
// 핵심: 수신자 리스트를 배열로 변환하여 한 번에 BCC 설정
helper.setBcc(receivers.toArray(new String[0]));
helper.setSubject(subject);
helper.setText(text, true);
javaMailSender.send(message); // 전송은 딱 1번만 일어남
} catch (Exception e) {
e.printStackTrace();
}
}
처음엔 이걸로 해결된 줄 알았다.
for문을 지우고, 수신자 100명을 BCC에 몽땅 때려넣었다.
전송은 딱 1번! SMTP 연결도 1번! 속도는 드라마틱하게 빨라졌다.