[SpringBoot] SpringBoot를 이용한 email 전송하기(첨부파일 포함)
FRAMEWORK/Spring

[SpringBoot] SpringBoot를 이용한 email 전송하기(첨부파일 포함)

반응형

들어가기 전에

하기 포스팅은 일반적으로 많이 사용하는 Google SMTP나 Naver SMTP 서버가 아닌 따로 구성된 SMTP 서버를 통해 email을 전송하는 법에 대해 다룹니다.

  • 사내 SMTP 서버의 경우 relay server이기 때문에, postfixmynetworks 변수로 관리되는 IP를 통해 email이 전송된다면 따로 username/password가 필요하지 않습니다.

이로 인해, Google SMTP 또는 Naver SMTP를 통해 email을 전송하기 위해서는 application.yml과 같은 설정 파일 내 추가 설정이 필요할 수 있습니다. 이 점 고려하시고 봐주시길 바랍니다.

SpringBoot를 이용한 Email 전송하기

Dependencies 추가하기

먼저, SpringBoot를 이용해 Email을 전송할 수 있도록 dependencies를 추가해줍니다.

[ Maven을 사용하는 경우 ]

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>VERSION 명</version>
</dependency>

[ Gradle을 사용하는 경우 ]

implementation 'org.springframework.boot:spring-boot-starter-mail:VERSION 명'

Java mail이 제공하는 interfaces & class

interface & class description
MailSender interface email 전송 시 가장 기본이 되는 top-level interface
JavaMailSender interface MailSender의 sub-interface로 MIME message를 지원하고 MimeMessageHelper class와 함께 사용됩니다.
JavaMailSenderImpl class JavaMailSender interface를 어떻게 실행할 지 제공하며, MimeMessage와 SimpleMailMessage를 지원합니다.
SimpleMailMessage class simple mail message를 만드는데 사용하며, 보내는 사람/받는 사람/참조/숨은 참조/메일 내용 필드를 제공합니다.
MimeMessagePreparator interface MIME messages를 준비하기 위한 callback interface를 제공합니다.
MimeMessageHelper class MIME messages를 만들 수 있는 class이며, image와 전형적인 메일의 첨부파일과 text 컨텐츠를 HTML 형식으로 제공합니다.

SpringBoot Mail Server Properties

spring:
  mail:
    host: SMTP 서버 IP
    port: SMTP PORT
    properties:
      mail:
        debug: true
        smtp:
          connectiontimeout: 5000
          auth: false
          starttls:
            enable: false
          ssl:
            enable: false
  • 필자의 경우, 사내 SMTP 서버를 사용하였기 때문에 많은 예제에서 볼 수 있는 smtp.gmail.com에서 사용하는 587 포트를 사용하지 않았습니다. 사용하고자하는 SMTP 서버를 확인하시어 spring.mail.hostspring.mail.port를 넣어야 합니다.
  • spring.mail.properties.mail.debug의 경우 메일 전송 시 어떻게 전송되고 있는지 상황을 파악하고자 켜두었습니다.
  • spring.mail.properties.mail.smtpauth/starttls/ssl의 경우, 사내 SMTP 서버 특성상 relay server이고, mynetworks라는 설정값을 통해 mail 전송이 가능한 IP를 제한하고 있어 따로 인증이 필요하지 않아 false로 하였습니다.
    • google이나 naver의 SMTP를 사용할 때에는 해당 값을 true로 한 후, spring.mail.usernamespring.mail.password를 작성하셔서 사용해야 합니다.

MailService.java와 MailServiceImpl.java

package earth.mail.service;

public interface MailService {

    public void sendMail(MailDto mailDto);
}
  • MailService 구현부를 여러개 만들 일은 없을 것 같지만 기존 프로젝트 구조와 맞추기 위해 interface와 implement 부분으로 나누었습니다. 상황에 따라 interface 없이 바로 class로 생성하여 사용하셔도 됩니다.
package earth.mail.service;

import java.io.File;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import earth.common.utils.MailUtil;
import earth.mail.dto.AttachFileDto;
import earth.mail.dto.MailDto;

@Service
public class MailServiceImpl implements MailService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private JavaMailSenderImpl javaMailSender;

    @Override
    public void sendMail(MailDto mailDto) {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();

        try {
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); // use multipart (true)

            InternetAddress[] toAddress = MailUtil.listToArray(mailDto.getToAddressList(), "UTF-8");
            InternetAddress[] ccAddress = MailUtil.listToArray(mailDto.getCcAddressList(), "UTF-8");
            InternetAddress[] bccAddress = MailUtil.listToArray(mailDto.getBccAddressList(), "UTF-8");

            mimeMessageHelper.setSubject(MimeUtility.encodeText(mailDto.getSubject(), "UTF-8", "B")); // Base64 encoding
            mimeMessageHelper.setText(mailDto.getContent(), mailDto.isUseHtmlYn()); 
            mimeMessageHelper.setFrom(new InternetAddress(mailDto.getFromAddress(), mailDto.getFromAddress(), "UTF-8"));
            mimeMessageHelper.setTo(toAddress);
            mimeMessageHelper.setCc(ccAddress);
            mimeMessageHelper.setBcc(bccAddress);

            if(!CollectionUtils.isEmpty(mailDto.getAttachFileList())) {
                for(AttachFileDto attachFileDto: mailDto.getAttachFileList()) {
                    FileSystemResource fileSystemResource = new FileSystemResource(new File(attachFileDto.getRealFileNm()));
                    mimeMessageHelper.addAttachment(MimeUtility.encodeText(attachFileDto.getAttachFileNm(), "UTF-8", "B"), fileSystemResource);
                }
            }

            javaMailSender.send(mimeMessage);

            logger.info("MailServiceImpl.sendMail() :: SUCCESS");
        } catch (Exception e) {
            logger.info("MailServiceImpl.sendMail() :: FAILED");
            e.printStackTrace();
        }

    }

}
  • 메일 전송 시, 첨부 파일도 함께 보낼 수 있도록 MimeMessageHelper를 통해 메일 전송 서비스를 작성했습니다.
    • 메일 전송과 관련된 Dto(MailDto) 내에 isUseHtmlYn 속성을 넣어 HTML 형식으로 메일을 전송하고자 할 때 사용했습니다.
    • 특히, 기존 메일이 HTML 형식으로 보내고 있던터라 실 사용시, 해당 속성을 true로 하고 진행했습니다.
  • 메일 받는 사람(to), 참조(cc), 숨은 참조(bcc) 모두 넣어서 보낼 수 있도록 Dto(MailDto)를 구성하여 해당 Dto의 내용을 mimeMessageHelper에 세팅하여 전송하고 있습니다.
  • 첨부파일의 경우 첨부파일용 Dto를 따로 구성하여 해당 첨부파일의 실제 경로와 파일 명을 Dto에 담아 가져왔습니다. 가져온 Dto를 통해 어디 위치에 있는 파일을 어떤 이름으로 첨부할지 작성했습니다.
  • 메일 깨짐 방지를 위해 Base 64 encoding을 사용하였으며, 관련된 참고 자료는 하단에 첨부하였습니다.

MailDto.java와 AttachFileDto.java

package earth.mail.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import earth.common.BaseDto;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MailDto extends BaseDto {

    private String fromAddress;
    private List<String> toAddressList = new ArrayList<>();
    private List<String> ccAddressList = new ArrayList<>();
    private List<String> bccAddressList = new ArrayList<>();
    private String subject; // 제목
    private String content; // 메일 내용
    private boolean isUseHtmlYn; // 메일 형식이 HTML인지 여부(true, false)
    private List<AtchFileDto> attachFileList = new ArrayList<>();

}
  • 앞서 MailServiceImpl.java에서 사용하는 MailDto입니다.
  • 기본적으로, 메일 보내는 이는 한명이지만 받는 사람/참조/숨은 참조는 각각 여러명이 될 수 있기 때문에 List 형식으로 작성하였고, 첨부 파일 역시 여러개를 한 번에 보낼 수 있기 때문에 List로 구성했습니다.
package earth.mail.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import earth.common.BaseDto;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class AttachFileDto extends BaseDto {

    private String realFileNm;
    private String attachFileNm;

}
  • 첨부파일의 실제 경로 및 경로 내 파일명을 뽑기 위해 첨부파일용 Dto를 따로 뽑았습니다.

sendMail() 메소드 실 사용 예시

@Component
public class EarthJob extends BaseJob {

    @Autowired
    protected ConfigLoader configLoader;

    @Autowired    
    private EarthService earthService;        

    @Autowired
    private MailService mailService;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String FROM_ADDRESS = "보내는 이 메일주소";

    @Override
    public void execute() throws Exception {
        try {
            / *
            * 다른 로직을 통해 첨부파일로 등록할 파일 경로(attachPath)와 첨부파일명(attachName)을 들고 왔다고 가정합니다. 
            * /

            String mailSubject = "첨부파일 메일 전송 테스트입니다.";
            String mailContent = "<p>안녕하세요.</p><p>"+ " 첨부파일 메일 전송 테스트입니다.</p><p>감사합니다.</p>";

            List<String> toAddressList = ListUtil.splitStringToArrayList(this.getParam("options.toaddress"), ","); 
            List<String> ccAddressList = ListUtil.splitStringToArrayList(this.getParam("options.ccaddress"), ","); 
            List<String> bccAddressList = ListUtil.splitStringToArrayList(this.getParam("options.bccaddress"), ","); 
            List<AtchFileDto> atchFileList = new ArrayList<>(Arrays.asList(new AtchFileDto(attachPath, attachName)));

            mailService.sendMail(new MailDto(FROM_ADDRESS, toAddressList, ccAddressList, bccAddressList, mailSubject, mailContent, true, atchFileList));    
        } 
        catch (Exception e) {
            e.printStackTrace();
        } 
    }
}
  • 필자의 경우, 메일 전송 시 함께 보낼 첨부파일을 미리 만든 후 첨부 파일 경로 및 이름을 추출해온 뒤 메일 전송을 진행했습니다.
  • 이에 위와 같이 진행하였으며, 위처럼 메일 전송 시 결과는 아래와 같습니다.

첨부파일 메일 전송 테스트 결과

참고 자료

반응형