728x90
반응형
반응형
준비
구글 2단계 보안 설정
- 2단계 인증 설정 후 앱 비밀번호 생성 (application.yml 에서 사용)
실습
의존성
- build.gradle
- 주의 : ‘org.springframework.boot:spring-boot-starter-mustache’가 아닌, com.github.spullara.mustache.java:compiler:0.9.5’를 이용한다.
dependencies {
// implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'com.github.spullara.mustache.java:compiler:0.9.5'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-mail'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
application.yml
spring:
mail:
host: smtp.gmail.com
port: 587
username: email@email.com
password: aaaaaaa #app 비밀번호 - google 2단계 보안설정 필요
properties:
mail:
smtp:
auth: true
starttls:
enable: true
Model
- MailDto
- 이메일 전송 시 필요한 데이터 정의
- mustache에 매핑되는 model로 사용
@Data @NoArgsConstructor @AllArgsConstructor @Builder public class MailDto { private String toEmailAddress; private String title; private String message; }
Service
@RequiredArgsConstructor
@Service
public class MailService {
private final JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String fromAddress;
public void send(MailDto mailDto) {
try{
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("templates/email.mustache");
StringWriter writer = new StringWriter();
m.execute(writer, mailDto);
writer.flush();
String messageText = writer.toString();
MimeMessagePreparator messagePreparator = mimeMessage -> {
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
messageHelper.setFrom(fromAddress);
messageHelper.setTo(mailDto.getToEmailAddress());
messageHelper.setSubject(mailDto.getTitle());
messageHelper.setText(messageText, true);
};
mailSender.send(messagePreparator);
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
Controller
@RestController
@RequestMapping("")
@RequiredArgsConstructor
public class MailController {
private final MailService mailService;
@PostMapping("")
public void send(@RequestBody MailDto mailDto){
mailService.send(mailDto);
}
}
email.mustache (템플릿)
<html>
<body style="margin: 0; padding: 0;">
<style type="text/css">
/* CLIENT-SPECIFIC STYLES */
#outlook a{padding:0;} /* Force Outlook to provide a "view in browser" message */
.ReadMsgBody{width:100%;} .ExternalClass{width:100%;} /* Force Hotmail to display emails at full width */
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;} /* Force Hotmail to display normal line spacing */
body, table, td, a{-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%;} /* Prevent WebKit and Windows mobile changing default text sizes */
table, td{mso-table-lspace:0pt; mso-table-rspace:0pt;} /* Remove spacing between tables in Outlook 2007 and up */
img{-ms-interpolation-mode:bicubic;} /* Allow smoother rendering of resized image in Internet Explorer */
/* RESET STYLES */
body{margin:0; padding:0;}
img{border:0; height:auto; line-height:100%; outline:none; text-decoration:none;}
table{border-collapse:collapse !important;}
body{height:100% !important; margin:0; padding:0; width:100% !important;}
/* iOS BLUE LINKS */
.appleBody a {color:#68440a; text-decoration: none;}
.appleFooter a {color:#999999; text-decoration: none;}
/* MOBILE STYLES */
@media screen and (max-width: 525px) {
/* ALLOWS FOR FLUID TABLES */
table[class="wrapper"]{
width:100% !important;
}
/* ADJUSTS LAYOUT OF LOGO IMAGE */
td[class="logo"]{
text-align: left;
padding: 20px 0 20px 0 !important;
}
td[class="logo"] img{
margin:0 auto!important;
}
/* USE THESE CLASSES TO HIDE CONTENT ON MOBILE */
td[class="mobile-hide"]{
display:none;}
img[class="mobile-hide"]{
display: none !important;
}
img[class="img-max"]{
max-width: 100% !important;
height:auto !important;
}
/* FULL-WIDTH TABLES */
table[class="responsive-table"]{
width:100%!important;
}
/* UTILITY CLASSES FOR ADJUSTING PADDING ON MOBILE */
td[class="padding"]{
padding: 10px 5% 15px 5% !important;
}
td[class="padding-copy"]{
padding: 10px 5% 10px 5% !important;
text-align: center;
}
td[class="padding-meta"]{
padding: 30px 5% 0px 5% !important;
text-align: center;
}
td[class="no-pad"]{
padding: 0 0 20px 0 !important;
}
td[class="no-padding"]{
padding: 0 !important;
}
td[class="section-padding"]{
padding: 50px 15px 50px 15px !important;
}
td[class="section-padding-bottom-image"]{
padding: 50px 15px 0 15px !important;
}
/* ADJUST BUTTONS ON MOBILE */
td[class="mobile-wrapper"]{
padding: 10px 5% 15px 5% !important;
}
table[class="mobile-button-container"]{
margin:0 auto;
width:100% !important;
}
a[class="mobile-button"]{
width:80% !important;
padding: 15px !important;
border: 0 !important;
font-size: 16px !important;
}
td[class="image-resize"]{
height: 140px !important;
padding: 40px 0 40px 0 !important;
background-size: 140% !important;
background-position-y: 60% !important;
}
td[class="reset-pad"]{
padding: 0 0 0 0 !important;
}
}
</style>
<!-- HEADER -->
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td bgcolor="#f0f0f0" style="padding: 70px 15px 0px 15px;" class="reset-pad">
<div align="center" >
<table border="0" cellpadding="0" cellspacing="0" width="580" class="wrapper">
<!-- LOGO/PREHEADER TEXT -->
<tr>
<td bgcolor="#010203" style="padding: 15px 0px 15px 0px;" class="logo">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="70" height="70" valign="middle" align="center">
</td>
<td width="110" height="70" valign="bottom"align="left">
<a href="#" target="_blank" style="text-decoration: none; font-family: tahoma, arial, sans-serif; color: #ffffff; font-size: 30px; font-weight: 100; line-height: 55px;">
제목 : {{title}}
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<!-- ONE COLUMN SECTION -->
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td bgcolor="#f0f0f0" align="center" style="padding: 0 15px 0 15px;" class="reset-pad">
<table border="0" cellpadding="0" cellspacing="0" width="580" class="responsive-table">
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<!-- HERO IMAGE -->
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td background="" bgcolor="#7bceeb" width="580" height="141" valign="top" style="background-repeat: no-repeat; background-size: 100%; padding: 121px 0 121px 0;" class="image-resize" >
<!--[if gte mso 9]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:580px;height:141px;">
<v:fill type="tile" src="" color="#7bceeb" />
<v:textbox inset="0,0,0,0">
<![endif]-->
<div>
<table align="center">
<tr>
<td align="center" style="font-family: tahoma, arial, sans-serif; color: #ffffff; font-size: 60px; font-weight: bold; line-height: 55px; border: 5px solid #ffffff; padding: 10px 15px 10px 15px; ">
메일<br />템플릿 테스트
</td>
</tr>
</table>
</div>
<!--[if gte mso 9]>
</v:textbox>
</v:rect>
<![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!-- COPY -->
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<span> message : {{message}} </span>
</tr>
</table>
</td>
</tr>
<!-- BULLETPROOF BUTTON -->
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="mobile-button-container">
<tr>
<td bgcolor="#010203" align="center" style="padding: 25px 5% 40px 5%;" >
<table border="0" cellspacing="0" cellpadding="0" class="responsive-table">
<tr>
<td align="center">
<a href="https://www.naver.com" target="_blank" style="font-size: 16px; font-family: tahoma, arial, sans-serif; font-weight: normal; color: #ffffff; text-decoration: none; background-color: #7bceeb; border-top: 15px solid #7bceeb; border-bottom: 15px solid #7bceeb; border-left: 25px solid #7bceeb; border-right: 25px solid #7bceeb; display: inline-block;" >
페이지이동 ❯❯
</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- FOOTER -->
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td bgcolor="#f0f0f0" align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td style="padding: 20px 0 70px 0;">
<!-- UNSUBSCRIBE COPY -->
<table width="500" border="0" cellspacing="0" cellpadding="0" align="center" class="responsive-table">
<tr>
<td align="center" valign="middle" style="font-size: 12px; line-height: 18px; font-family: tahoma, arial, sans-serif; color:#666666;">
<span class="appleFooter" style="color:#666666;">
풋터
</span>
<br>
<a class="original-only" style="color: #666666; text-decoration: none;">
풋터 객체 2
</a>
<span class="original-only" style="font-family: tahoma, arial, sans-serif; font-size: 12px; color: #444444;">
|
</span>
<a style="color: #666666; text-decoration: none;">
풋터 객체 3
</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
결과
파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음
반응형
'Spring' 카테고리의 다른 글
[에러] java.util.zip.ZipException: invalid code lengths set (1) | 2024.01.04 |
---|---|
[유효성검증] Spring Boot Validation (0) | 2023.04.14 |
[Spring Boot] docker-compose로 mysql컨테이너 생성 및 Spring Boot 연결 (0) | 2021.09.13 |
[Spring AOP] AOP를 이용한 Decode (0) | 2021.08.28 |
[Spring Interceptor] 커스텀 어노테이션과 Intercepter 구현 (0) | 2021.08.27 |