navis
Java Email 발송 본문
728x90
개발환경
- IntelliJ Ultimate
- Java 17
- Spring Boot 3.2.1
- Gradle
1. 우선 Google 이메일 계정을 사용하여 발송하기 때문에 Google 계정이 필요하다!
- 소개글에서 말했듯 앱 비밀번호 설정이 필요하다.
Google 계정관리 -> 보안
* 발급 받은 앱 비밀번호를 필수로 저장 해두자! 추후 사용!
2. 의존성 주입
// Email
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation group: 'commons-io', name: 'commons-io', version: '2.12.0'
3. application.yml
server:
mail:
host: smtp.gmail.com # 1
port: 587 # 2
username: ${mail.username} # 3 // 내 메일주소
password: ${mail.password} # 4 // 발급받은 비밀번호
properties:
mail:
smtp:
auth: true # 5
timeout: 5000 # 6
starttls:
enable: true # 7
mime:
charset: UTF-8
4. EmailService
@Value("${spring.mail.username}")
private String email;
// 프로모션 메일 배포
@Override
@Transactional
public ResponseEntity<String> sendMailPromo(PromotionsDTO promotionsDTO) {
// 프로모션 정보를 바탕으로 이메일 메시지 객체 생성
EmailMessage emailMessage = EmailMessage.builder()
.subject(promotionsDTO.getPromotionsTitle())
.message(promotionsDTO.getPromotionsContent())
.build();
// 프로모션 수신을 동의한 유저의 이메일 목록을 불러옴
List<String> recipients = adminRepository.getPromotionsAccept();
try {
// 수신자 목록에 대해 비동기 이메일 발송 작업을 생성하고 실행
CompletableFuture<?>[] futuresArray = recipients.stream()
.map(recipient -> CompletableFuture.runAsync(() -> {
try {
// 각 수신자에게 비동기적으로 이메일 발송
sendMailAsync(emailMessage, recipient);
} catch (MessagingException e) {
// 이메일 발송 중 예외 발생 시 RuntimeException으로 포장하여 던짐
throw new CustomException("Failed to send email to " + recipient, e);
}
}))
.toArray(CompletableFuture[]::new);
// 생성된 모든 비동기 작업이 완료될 때까지 대기
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futuresArray);
allFutures.join();
// 모든 이메일이 성공적으로 발송되면, 클라이언트에게 성공 메시지 전송
return ResponseEntity.ok("Emails successfully sent to all recipients.");
} catch (RuntimeException e) {
// 비동기 작업 중 예외 발생 시, 500 Internal Server Error와 함께 오류 메시지 반환
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to send emails to all recipients. Error: " + e.getCause().getMessage());
}
}
// 메시지 객체 생성
private void sendMailAsync(EmailMessage emailMessage, String recipient) throws MessagingException {
// JavaMailSender를 사용하여 MIME 메시지를 생성합니다.
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(email); // 발신자 설정
mimeMessageHelper.setTo(recipient); // 개별 수신자 설정
mimeMessageHelper.setSubject(emailMessage.getSubject()); // 제목 설정
processImage(emailMessage, mimeMessageHelper); // 이미지 첨부 처리 (해당 메서드는 별도로 정의되어야 함)
javaMailSender.send(mimeMessage); // 메일 발송
}
// 이미지 첨부
private void processImage(EmailMessage emailMessage, MimeMessageHelper mimeMessageHelper) throws MessagingException {
// 이메일 본문에서 이미지 경로 추출
List<String> imagePaths = extractImageUrls(emailMessage.getMessage());
String modifiedHtmlMessage = emailMessage.getMessage();
// 이미지 CID 매핑을 저장하기 위한 Map 생성
Map<String, String> cidMap = IntStream.range(0, imagePaths.size()).boxed().collect(Collectors.toMap(imagePaths::get, i -> "image" + i));
// 모든 이미지 경로를 CID로 대체
for (Map.Entry<String, String> entry : cidMap.entrySet()) {
modifiedHtmlMessage = modifiedHtmlMessage.replace(entry.getKey(), "cid:" + entry.getValue());
}
// 본문 텍스트 설정 (HTML로 처리)
mimeMessageHelper.setText(modifiedHtmlMessage, true);
// 각 이미지를 CID를 사용하여 첨부
imagePaths.forEach(imagePath -> {
try {
FileSystemResource res = new FileSystemResource(new File("C:/lumen" + imagePath.substring(1)));
mimeMessageHelper.addInline(cidMap.get(imagePath), res);
} catch (MessagingException e) {
throw new CustomException("Failed to attach image", e);
}
});
}
// 이미지 경로 검색
private List<String> extractImageUrls(String htmlContent) {
List<String> imageUrls = new ArrayList<>();
Pattern pattern = Pattern.compile("<img [^>]*src=[\"']([^\"']+)[\"']", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(htmlContent);
while (matcher.find()) {
imageUrls.add(matcher.group(1)); // 모든 일치하는 이미지의 src 값을 리스트에 추가
}
return imageUrls;
}
Email 발송할 때 이미지가 포함된 내용일 경우 이미지를 저장하고 이미지 파일을 불러들여서 메일에 첨부파일에 실어 보내야 본문 내용의 이미지가 제대로 나오게 된다.