본문 바로가기
Spring

Mustache로 템플릿 메일 발송

by 행운의나무 2022. 4. 26.
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;" >
                                                                페이지이동 &#10095;&#10095;
                                                            </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;">
                                        &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
                                    </span>
                                    <a style="color: #666666; text-decoration: none;">
                                        풋터 객체 3
                                    </a>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>

</body>
</html>

결과

 

쿠팡으로 연결 클릭

 

제주삼다수 그린

COUPANG

www.coupang.com

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음

반응형