깔끔하게 보고 싶으면 아래 링크에서 보라.
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

다음과 같은 메시지를 볼 수가 있다. ㅡ_-);; 이것 땜시 반나절을 삽질을 해버렸네... 두둥...


+ Recent posts