본문 바로가기
Backend/Spring

[Spring] 첨부파일이 포함된 메일 보내기 (외부 메일 서버 사용)

by 그적 2022. 11. 9.

Naver 메일을 이용해 메일을 보내는 코드를 작성해보려고 한다.

 

메일을 어떻게 사용하냐에 따라 메일을 저장할 디비가 따로 필요할 수도 있지만, 이번엔 단순히 메일을 전송하는 로직만 필요했기 때문에 프론트에서 받은 데이터만 SMTP 프로토콜을 이용해 전송하도록 했다.

 

MailController.class

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/mail")
public class MailController {

    private final CommonService commonService;

    @PostMapping("/send")
    @ResponseBody
    public Map<String, String> sendMail(HttpServletRequest request, HttpServletResponse response, MailDto obj){
        return commonService.sendMail(request, response, obj);
    }
}

RequestParam을 이용해 MailDto를 받았고, 메일이 정상적으로 발송되면 result:success를 응답한다.

 

MailDto.class

@Getter
@Setter
@Builder
public class MailDto {
    String title;

    String contents;

    // 여러명일 경우 ,로 구분
    String to;

    // 여러명일 경우 ,로 구분
    String cc;

    List<MultipartFile>  files;

}

RequestParam의 경우에는 들어온 데이터들이 Dto 클래스 변수들과 자동 매핑되기 때문에

동일한 변수명과 Setter 어노테이션은 필수이다.

 

CommonService.class

    public Map<String, String> sendMail(HttpServletRequest request, HttpServletResponse response, MailDto dto) {
        Map<String, String> res = new HashMap<>();

        String mailAttachPath = "(임시 저장 폴더/절대경로 필수)";
        
        // 메일을 전송하려는 유저의 메일 아이디/비번
        String userEmail = "";
        String userPassword = "";

        // 외부 SMTP 서버 정보 설정
        // 외부 SMTP 서버의 호스트명, 포트번호, ssl 혹은 tls 사용여부 확인 필요
        Properties prop = new Properties();
        prop.put("mail.smtp.host", "smtp.naver.com");
        prop.put("mail.smtp.port", "465");
        prop.put("mail.smtp.auth", "true");
        prop.put("mail.smtp.ssl.enable", "true");
        prop.put("mail.smtp.ssl.trust", "smtp.naver.com");

        // 네이버 로그인(세션 생성)
        Session session = Session.getDefaultInstance(prop, new javax.mail.Authenticator(){
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(userEmail, userPassword);
            }
        });

        try{
            // 메일을 보낼 수 있는 MimeMessage 객체 생성
            MimeMessage message = new MimeMessage(session);

            // MimeMessage 객체에 보내는사람 세팅
            message.setFrom(new InternetAddress(userEmail));
            // MimeMessage 객체에 받는사람, CC 세팅
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(dto.getTo()));
            message.addRecipients(Message.RecipientType.CC, InternetAddress.parse(dto.getCc()));
            
            // MimeMessage 객체에 제목 세팅
            message.setSubject(dto.getTitle());

            // MimeMultipart 객체 생성 (BODY 전체 담기 위함)
            MimeMultipart mimeMultipart = new MimeMultipart();

            // MimeBodyPart 객체 생성 후 MimeMultipart에 담기 (내용을 담기 위함)
            MimeBodyPart contentBodyPart = new MimeBodyPart();
            contentBodyPart.setContent(dto.getContents(), "text/html;charset=UTF-8");
            mimeMultipart.addBodyPart(contentBodyPart);

            // MultipartFile을 임시로 저장해둘 폴더 생성
            File folder = new File(mailAttachPath);
            if (!folder.exists()) {
                folder.mkdirs();
            }

            for(MultipartFile file: dto.getFiles()) {
                // 절대 경로에다가 첨부파일 생성한 후, MultipartFile 데이터 담기
                File makeFile = new File(mailAttachPath+file.getOriginalFilename());
                file.transferTo(makeFile.getAbsoluteFile());

                // MimeBodyPart 객체 생성 후 MimeMultipart에 담기 (첨부파일을 담기 위함)
                MimeBodyPart fileBodyPart = new MimeBodyPart();
                fileBodyPart.attachFile(makeFile.getAbsoluteFile());

                mimeMultipart.addBodyPart(fileBodyPart);
            }
            // MimeMessage 객체에 모든 BODY 담은 MimeMultipart 세팅
            message.setContent(mimeMultipart);
            // MimeMessage 객체에 날짜 세팅
            message.setSentDate(new java.util.Date());

            // 전송
            Transport.send(message);

            res.put("result", "success");

        } catch (AddressException e) {
            throw new RuntimeException(e);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            for(MultipartFile file: dto.getFiles()) {
                File makeFile = new File(mailAttachPath+file.getOriginalFilename());
                makeFile.delete();
            }
        }

        if(!res.containsKey("result"))
            res.put("result", "fail");
        return res;
    }

코드를 작성하다가 두 군데에서 헤맸다.

첫 번째로 메일 내용과 첨부파일을 같이 보내려면 MimeMultipart에 모든 BODY를 담아서 Message 객체에 세팅해야 하는 것과 두 번째는 MultipartFile을 File로 변환해 담는 것이었다.

 

MultipartFile을 File 객체로 변환해 저장시키려면, 절대 경로가 필수이다.

로컬에 파일도 안 만들어지면서 첨부파일을 넣어서 메일을 전송하려 했던 건 바보 같은 삽질이었던 것 같다.

 

 

 

참고로 다른 블로그를 참고해보니 Gmail과 Naver를 사용했는데,

이제 구글은 이제 아이디와 비밀번호만으로 서드파티 로그인이 안된다고 한다.

(2차 인증이 된 아이디만 ...(?) 가능하다고 한다. 구글도 할 수 있는듯?)

참고 : https://hyunmin1906.tistory.com/276

댓글