https://gist.github.com/ihoneymon/56dd964336322eea04dc
19:04:00 ERROR c.i.i.s.s.system.MailServiceImpl - >> Occur Exception: Failed messages: com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first. a11sm6769399pdj.54 - gsmtp
위의 메시지가 나타난다면, compile "com.sun.mail:javax.mail"
의존성을 추가하자.
○ build.gradle
dependencies {
//.. 중략
compile "org.springframework:spring-context-support"
/**
* @see http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-email
*
* Javax 관련 의존성: com.sun.mail:javax.mail 과 javax.mail:javax.mail-api 두개만 존재한다.
* @see https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml
* 를 살펴보면 javax.mail:mail 관련한 의존성이 빠져있는 것을 볼 수 있다. ㅡ_-)> 흠...
*/
compile "com.sun.mail:javax.mail"
//.. 중략
}
spring-context-support, javax.mail 에 대한 의존성을 추가한다.
○ properties.yml
mail:
host: smtp.gmail.com
port: 587
protocol: smtp
default-encoding: UTF-8
username: your-email@domain
password: password
smtp:
start-tls-enable: true
auth: true
○ MailConfig
메일발송에 사용할 템플릿엔진으로는 Thymeleaf(http://www.thymeleaf.org/doc/articles/springmail.html)를 참고하여 작성하였다. 기존에 사용하던 메일설정을 활용했다.
import java.util.Properties;
import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.ToString;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
/**
*
* @author jiheon
*
* @see <a
* href="http://www.thymeleaf.org/doc/articles/springmail.html">http://www.thymeleaf.org/doc/articles/springmail.html</a>
* @see <a
* href="http://stackoverflow.com/questions/25610281/spring-boot-sending-emails-using-thymeleaf-as-template-configuration-does-not">http://stackoverflow.com/questions/25610281/spring-boot-sending-emails-using-thymeleaf-as-template-configuration-does-not</a>
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "mail", locations = { "classpath:properties/properties.yml" })
@ToString
public class MailConfig {
private static final String MAIL_DEBUG = "mail.debug";
private static final String MAIL_SMTP_STARTTLS_REQUIRED = "mail.smtp.starttls.required";
private static final String MAIL_SMTP_AUTH = "mail.smtp.auth";
private static final String MAIL_SMTP_STARTTLS_ENABLE = "mail.smtp.starttls.enable";
@Data
public static class Smtp {
private boolean auth;
private boolean startTlsRequired;
private boolean startTlsEnable;
}
@NotBlank
private String host;
private String protocol;
private int port;
private String username;
private String password;
private String defaultEncoding;
@NotNull
private Smtp smtp;
@Bean
public JavaMailSender mailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(getHost());
mailSender.setProtocol(getProtocol());
mailSender.setPort(getPort());
mailSender.setUsername(getUsername());
mailSender.setPassword(getPassword());
mailSender.setDefaultEncoding(getDefaultEncoding());
Properties properties = mailSender.getJavaMailProperties();
properties.put(MAIL_SMTP_STARTTLS_REQUIRED, getSmtp().isStartTlsRequired());
properties.put(MAIL_SMTP_STARTTLS_ENABLE, getSmtp().isStartTlsEnable());
properties.put(MAIL_SMTP_AUTH, getSmtp().isAuth());
properties.put(MAIL_DEBUG, true);
mailSender.setJavaMailProperties(properties);
return mailSender;
}
@Bean
public TemplateResolver emailTemplateResolver() {
TemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("mails/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML5");
emailTemplateResolver.setCharacterEncoding("UTF-8");
emailTemplateResolver.setCacheable(true);
return emailTemplateResolver;
}
}
○ MailServiceTest
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@ActiveProfiles("test")
@WebAppConfiguration
public class MailServiceTest {
@Autowired
private MailService mailService;
@Test
public void test() {
ThymeleafMailMessage mailMessage = new ThymeleafMailMessage("test");
mailMessage.setFrom("test-email@domain");
mailMessage.setTo("test-email@domain");
mailMessage.setSubject("[Test] mailTest");
mailMessage.addAttribute("name", "Guest");
mailMessage.addAttribute("imageResourceName", "imageResourceName");
mailMessage.setEncoding("UTF-8");
mailService.send(mailMessage);
}
}
○ MailMessage 클래스들
● MailMessage
import java.io.File;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.mail.SimpleMailMessage;
import com.google.common.collect.Lists;
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MailMessage extends SimpleMailMessage {
private static final long serialVersionUID = 1830106734321133565L;
private List<File> attachments;
private String encoding;
private boolean htmlContent;
public MailMessage() {
super();
super.setSentDate(Calendar.getInstance().getTime());
this.attachments = Lists.newArrayList();
}
public MailMessage addAttachment(File file) {
if (null != file) {
this.attachments.add(file);
}
return this;
}
public MailMessage removeAttachment(File file) {
if (null != file && this.attachments.contains(file)) {
this.attachments.remove(file);
}
return this;
}
public List<File> getAttachments() {
return Collections.unmodifiableList(this.attachments);
}
public boolean isMultipart() {
return !this.attachments.isEmpty();
}
}
● ThymeleafMailMessage
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import com.google.common.collect.Maps;
/**
* Thymeleaf MailMessage
*
* @author jiheon
*
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ThymeleafMailMessage extends MailMessage {
private static final long serialVersionUID = -2313892947287620959L;
@Getter
private final String templateName;
private final Map<String, Object> attributes;
public ThymeleafMailMessage(String templateName) {
this.templateName = templateName;
this.attributes = Maps.newHashMap();
super.setHtmlContent(true);
}
public ThymeleafMailMessage addAttribute(String key, Object value) {
this.attributes.put(key, value);
return this;
}
public ThymeleafMailMessage removeAttribute(String key) {
if (this.attributes.containsKey(key)) {
this.attributes.remove(key);
}
return this;
}
public Map<String, Object> getAttributes() {
return java.util.Collections.unmodifiableMap(this.attributes);
}
}
○ MailService
● interface MailService
import java.util.List;
import org.springframework.mail.MailException;
/**
* MailService
*
* @author jiheon
*
*/
public interface MailService {
void send(MailMessage message) throws MailException;
void send(List<MailMessage> messages) throws MailException;
}
● class MailServiceImpl
import java.util.List;
import java.util.Locale;
import javax.annotation.PostConstruct;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
@Slf4j
@Service
public class MailServiceImpl implements MailService {
@Autowired
private JavaMailSender mailSender;
@Autowired
private SystemMailService systemMailService;
@Autowired
private TemplateEngine templateEngine;
@PostConstruct
public void setUp() {
if (systemMailService.hasSystemMailConfig()) {
mailSender = systemMailService.getMailSender();
}
log.debug("MailSender: {}", mailSender);
}
@Override
public void send(MailMessage message) throws MailException {
sendMail(message);
}
private void sendMail(MailMessage message) {
try {
log.debug(">> Send mailMessage: {}", message);
if (ThymeleafMailMessage.class.isAssignableFrom(message.getClass())) {
doThymeleafMailMessageSend((ThymeleafMailMessage) message);
} else {
doSend(message);
}
} catch (Exception e) {
log.error(">> Occur Exception: {}", e.getMessage());
}
}
private void doThymeleafMailMessageSend(ThymeleafMailMessage thymeleafMailMessage) throws MessagingException {
Context context = new Context(Locale.getDefault());
context.setVariables(thymeleafMailMessage.getAttributes());
thymeleafMailMessage.setText(templateEngine.process(thymeleafMailMessage.getTemplateName(), context));
doSend(thymeleafMailMessage);
}
private void doSend(MailMessage message) throws MessagingException {
try {
MimeMessage mimeMessage = new MimeMessage(Session.getInstance(System.getProperties()));
MimeMessageHelper helper = getMimeMessageHelper(message, mimeMessage);
mailSender.send(helper.getMimeMessage());
} catch (MessagingException e) {
throw e;
}
}
@Override
public void send(List<MailMessage> messages) throws MailException {
for (MailMessage mailMessage : messages) {
sendMail(mailMessage);
}
}
private MimeMessageHelper getMimeMessageHelper(MailMessage message, MimeMessage mimeMessage)
throws MessagingException {
MimeMessageHelper mimeMessageHelper = makeMessageHelper(message, mimeMessage);
mimeMessageHelper.setFrom(message.getFrom());
mimeMessageHelper.setTo(message.getTo());
if (null != message.getCc()) {
mimeMessageHelper.setCc(message.getCc());
}
if (null != message.getBcc()) {
mimeMessageHelper.setBcc(message.getBcc());
}
mimeMessageHelper.setSubject(message.getSubject());
mimeMessageHelper.setText(message.getText(), message.isHtmlContent());
mimeMessageHelper.setSentDate(message.getSentDate());
return mimeMessageHelper;
}
private MimeMessageHelper makeMessageHelper(MailMessage message, MimeMessage mimeMessage) throws MessagingException {
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, message.isMultipart());
if (StringUtils.hasText(message.getEncoding())) {
helper = new MimeMessageHelper(mimeMessage, message.isMultipart(), message.getEncoding());
}
return helper;
}
}
○ 정리
compile "com.sun.mail:javax.mail"
을 선언하지 않고 테스트를 돌려보면19:04:00 ERROR c.i.i.s.s.system.MailServiceImpl - >> Occur Exception: Failed messages: com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first. a11sm6769399pdj.54 - gsmtp
다음과 같은 메시지를 볼 수가 있다. ㅡ_-);; 이것 땜시 반나절을 삽질을 해버렸네... 두둥...