목차

개발환경

프로젝트 생성

의존성 추가

기본 설정 및 구글 SMTP 변수 설정

application.yml 설정

구글 메일 발송 구현

DTO 추가

AsyncConfig 구현

service 구현

컨트롤러, 프론트 페이지 추가

컨트롤러 생성

메인 페이지 생성

메일 템플릿

참고한 사이트

프로젝트 구조

GitHub 링크

실행화면

개발환경

개발 환경 버전
Java 17
Spring Boot 3.2.8
Spring Boot Starter Mail 3.2.8
Lombok 1.18.30
빌드 관리 도구 Gradle 8.8
템플릿 엔진 Mustache
개발 도구 Spring Tool Suite 4.22.1

프로젝트 생성

STS 상단 메뉴 > File > New > Spring Starter Project 선택합니다.

참고사진_1

New Spring Starter Project

Name, Java Version, Group, Artifact, Description, Package 항목을 입력한 후 아래 "Next" 버튼을 누릅니다.

참고사진_2

New Spring Starter Dependencies

Spring Boot Version 3.2.8 선택합니다.

검색란에 의존성 키워드를 입력하여 의존성을 추가합니다. (녹색 영역 참조)

필수 의존성 [Lombok, Java Mail Sender, Mustache, Spring Web] 추가 후 아래 "Finish" 버튼을 누릅니다.

참고사진_3

의존성 추가

Mustache 템플릿 compile에 필요한 라이브러리

URL : https://mvnrepository.com/artifact/com.github.spullara.mustache.java/compiler

Gradle (Short) : implementation 'com.github.spullara.mustache.java:compiler:0.9.14'

build.gradle 파일을 열어서 의존성을 추가 합니다. (아래 그림 참조)

참고사진_4

기본 설정 및 구글 SMTP 변수 설정

application.yml 설정

application.properties파일의 확장자를 application.yml 확장자로 변경합니다.

username : 앱 비밀번호 생성한 구글 아이디 ex) test@gamil.com

password : 앱 비밀번호

application.yml
spring:
  application:
    name: email

#mustache UTF-8
server:
  servlet:
    encoding:
      force: true

---
#mustache 설정
spring:
  mustache:
    cache: false
    servlet:
      expose-request-attributes: true #mustache에서 csrf 토큰 변수 오류 발생 때문에 추가
      expose-session-attributes: true #세션
      
---
#메일 발송
spring:
  mail:
    host: smtp.gmail.com
    port: 587
    username: [앱 비밀번호 생성한 구글 아이디] #test@gmail.com
    password: zrhq ceee dzdm mjlx #앱 비밀번호
    properties:
      mail:
        smtp:
          auth: true
          timeout: 5000
          starttls:
            enable: true

구글 메일 발송 구현

DTO 추가

dto 패키지를 생성 후 MailDto 클래스 파일을 생성합니다.

패키지 위치 : com.example.email.dto

MailDto.java
package com.example.email.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class MailDto {
    
    private String title;       // 제목
    private String contents;    // 내용
    
    private String company;     // 상호명
    private String name;        // 받는 사람 이름
    private String email;       // 상호명
    private String link;        // 버튼 링크
    
}

ResultDto 클래스 파일을 생성합니다.

패키지 위치 : com.example.email.dto

ResultDto.java
package com.example.email.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class ResultDto {

    private String resultCode   = "ERROR";
    private String message      = "오류가 발생하였습니다.";
    
    @Builder
    public ResultDto (String resultCode, String message, Object resultData, String url) {
        this.resultCode = resultCode;
        this.message    = message;
    }
    
}

AsyncConfig 구현

config 패키지를 생성 후 AsyncConfig 클래스 파일을 생성합니다.

패키지 위치 : com.example.email.config

Thread 설정 값은 프로젝트 환경에 따라 다를 수 있습니다.

AsyncConfig.java
package com.example.email.config;

import java.util.concurrent.Executor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Bean
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);    // 실행 대기 중인 최소 Thread의 개수
        executor.setMaxPoolSize(5);     // 동시에 동작하는 최대 Thread의 개수
        executor.setQueueCapacity(10);  // 큐의 최대 용량 (CorePool의 크기를 넘어서면 큐에 저장함)
        executor.setThreadNamePrefix("Async MailExecutor");
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
    }
}

service 구현

service 패키지를 생성 후 MailService 클래스 파일을 생성합니다.

패키지 위치 : com.example.email.service

import 에러 발생 시 "Refresh Gradle Project" 실행 해주세요.

MailService.java
package com.example.email.service;

import java.io.StringWriter;
import java.util.Map;

import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;

import com.example.email.dto.MailDto;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;

@RequiredArgsConstructor
@Service
public class MailService {

    private final JavaMailSender javaMailSender;
    
    @Async
    public void sendMail(MailDto mailDto, Map<String, String> userInfo) throws MessagingException {
        try {
            // 메일에 들어갈 값 설정
            MailDto sendMailDto = new MailDto();
            sendMailDto.setCompany("DevDream");                                      // 상호명
            sendMailDto.setLink("https://devdream.net");                             // 버튼 링크
            sendMailDto.setTitle(mailDto.getTitle());                                // 제목
            sendMailDto.setContents(mailDto.getContents().replaceAll("\n", "<br>")); // 내용, 개행문자 치환
            sendMailDto.setName(userInfo.get("name").toString());                    // 받는 사람 이름
            sendMailDto.setEmail(userInfo.get("email").toString());                  // 받는 사람 이메일
            
            MustacheFactory mf  = new DefaultMustacheFactory();
            Mustache m          = mf.compile("templates/mail_form.mustache");   // mail form (template)

            StringWriter sw = new StringWriter();
            m.execute(sw, sendMailDto);
            sw.flush();
            String mailContents = sw.toString();
            
            MimeMessagePreparator preparator = new MimeMessagePreparator() {
                @Override
                public void prepare(MimeMessage mimeMessage) throws Exception {
                    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
                    // 받는 사람 이메일 
                    helper.setTo(sendMailDto.getEmail());
                    // 제목
                    helper.setSubject(sendMailDto.getTitle());
                    // 내용
                    helper.setText(mailContents, true);
                }
            };

            // 메일 발송
            this.javaMailSender.send(preparator);
        } catch (MailException e) {
            System.out.println(e.getMessage());
        }
    }

}

컨트롤러, 프론트 페이지 추가

컨트롤러 생성

controller 패키지를 생성 후 MainController 클래스 파일을 생성합니다.

패키지 위치 : com.example.email.controller

이메일 발송 대상자를 DB 조회 하여 userList 리스트에 넣으면 됩니다.

MainController.java
package com.example.email.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import com.example.email.dto.MailDto;
import com.example.email.dto.ResultDto;
import com.example.email.service.MailService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Controller
public class MainController {

    private final MailService mailService;
    
    /**
     * 메인 페이지
     * @return
     */
    @GetMapping("/")
    public String getMethodName() {
        return "index";
    }
    
    /**
     * 메일 발송
     * @param mailDto
     * @return
     */
    @PostMapping(value = "/send_mail", produces = "application/json; charset=utf8")
    public ResponseEntity<ResultDto> sendMail(@RequestBody MailDto mailDto) {
        
        ResultDto result = new ResultDto();
        
        try {
            // 메일 발송 대상자
            List<Map<String, String>> userList = new ArrayList<Map<String, String>>();
            
            Map<String, String> map = new HashMap<String, String>();
            map.put("name", "김철수");
            map.put("email", "test@nate.com");
            userList.add(map);
            
            map = new HashMap<String, String>();
            map.put("name", "장철수");
            map.put("email", "test@gmail.com");
            userList.add(map);
            
            map = new HashMap<String, String>();
            map.put("name", "이영희");
            map.put("email", "test@gmail.com");
            userList.add(map);
            
            map = new HashMap<String, String>();
            map.put("name", "홍길동");
            map.put("email", "test@daum.net");
            userList.add(map);
            
            for (int i = 0; i < userList.size(); i++) {
                mailService.sendMail(mailDto, userList.get(i));
            }

            result = ResultDto.builder()
                    .resultCode("SUCCESS")
                    .message("발송이 완료되었습니다.")
                    .build();
            
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        return new ResponseEntity<ResultDto>(result, HttpStatus.OK);
    }
    
}

메인 페이지 생성

파일 다운로드 : index.mustache

index.mustache 파일 다운로드 후 templates에 넣어줍니다.

메일 템플릿

파일 다운로드 : mail_form.mustache

mail_form.mustache 파일 다운로드 후 templates에 넣어줍니다.

메일 템플릿 출처 : https://www.bootdey.com/snippets/tagged/email

참고한 사이트

1) https://javadoc.io/doc/com.github.spullara.mustache.java/compiler/latest/com/github/mustachejava/DefaultMustacheFactory.html

2) https://www.baeldung.com/mustache

프로젝트 구조

참고사진_5

GitHub 링크

전체 소스 다운로드

실행화면

참고사진_6

©2024, DevDream