HR μ†”λ£¨μ…˜μΈ κ·Έλ¦¬νŒ…μ²˜λŸΌ μ‚¬μš©μžκ°€ 직접 이메일 ν…œν”Œλ¦Ώμ„ κ΄€λ¦¬ν•˜λŠ” 건 μ‹œμŠ€ν…œμ΄λ‚˜ μ„œλΉ„μŠ€λ§ˆλ‹€ μš”κ΅¬μ‚¬ν•­μ΄ 생길 수 μžˆλ‹€. κ·Έλ¦¬νŒ… μ΄λ©”μΌμ˜ 경우 내뢀적인 ν…œν”Œλ¦Ώμ€ μ„œλΉ„μŠ€ μžμ²΄μ—μ„œ κ΄€λ¦¬ν•˜κ³  ν…œν”Œλ¦Ώ λ³€μˆ˜λ₯Ό μ œκ³΅ν•˜μ—¬ 이메일 λ‚΄μš©λ§Œ μž…λ ₯ν•˜λŠ” κ΅¬μ„±μ΄μ§€λ§Œ B2B μ„œλΉ„μŠ€(μ†”λ£¨μ…˜)의 경우 μ„œλΉ„μŠ€ μ‚¬μ—…μžκ°€ μ•„λ‹Œ ν•΄λ‹Ή μ‚¬μ—…μž μ •λ³΄λ‘œ λŒ€μ²΄ν•˜κ³  싢은 고객듀이 생긴닀.

μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬ 기반의 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ Thymeleaf, FreeMarker, Mustache 와 같은 ν…œν”Œλ¦Ώ 엔진을 μ‰½κ²Œ μ‚¬μš©ν•  수 μžˆμ–΄μ„œ 이메일 ν…œν”Œλ¦Ώμ„ λ§Œλ“€κ³  μ»¨ν…μŠ€νŠΈ 정보와 ν•¨κ»˜ HTML둜 λ³€ν™˜ν•˜μ—¬ μ΄λ©”μΌλ‘œ λ°œμ†‘ν•˜λŠ” 건 Sending email in Spring with Thymeleaf와 같은 μ˜ˆμ œλ„ κ³΅μœ λ˜μ–΄μžˆμ–΄ κ΅¬ν˜„ν•˜λŠ”κ±΄ κ°„λ‹¨ν•˜λ‹€. μ‚¬μš©μžκ°€ 직접 ν…œν”Œλ¦Ώμ„ κ΄€λ¦¬ν•˜λŠ” 방식을 νƒ€μž„λ¦¬ν”„ ν…œν”Œλ¦ΏμœΌλ‘œ μ΄μ•ΌκΈ°ν•΄λ³΄μžλ©΄ StringTemplateResolver 클래슀λ₯Ό 톡해 μ•„λž˜μ™€ 같이 ν…œν”Œλ¦Ώ 파일 κ²½λ‘œκ°€ μ•„λ‹Œ HTML λ¬Έμžμ—΄ 자체둜 λ³€ν™˜ν•  수 μžˆλ‹€.

@Slf4j
@AllArgsConstructor
@Service
public class MailService {
    private final MessageSource messageSource;

    private SpringTemplateEngine templateEngine;

    @PostConstruct
    public void init() {
        templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(new StringTemplateResolver());
        templateEngine.setTemplateEngineMessageSource(messageSource);
    }

    public String processTemplate(String template, Map<String, Object> variables) {
        return processTemplate(template, variables, Locale.getDefault());
    }

    public String processTemplate(String template, Map<String, Object> variables, Locale locale) {
        if (locale == null) {
            locale = Locale.getDefault();
        }
        return templateEngine.process(template, new Context(locale, variables));
    }
}

StringTemplateResolver

StringTemplateResolverλŠ” StringTemplateResourceλ₯Ό 톡해 μ™ΈλΆ€ νŒŒμΌμ΄λ‚˜ λ¦¬μ†ŒμŠ€μ— μ•‘μ„ΈμŠ€ν•˜λŠ”κ²Œ μ•„λ‹ˆλΌ λ¬Έμžμ—΄μ„ ν…œν”Œλ¦Ώ 자체둜 κ°„μ£Όν•˜λ©° κΈ°λ³Έμ μœΌλ‘œλŠ” μΊμ‹œν•  수 μ—†λ‹€κ³  μ„€μ •λœλ‹€. μ•„λž˜μ˜ ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” ν΄λž˜μŠ€νŒ¨μŠ€μ— μœ„μΉ˜ν•œ 메일 ν…œν”Œλ¦Ώ 양식을 λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•˜μ—¬ μ²˜λ¦¬ν•  수 μžˆμŒμ„ 보여쀀닀. μ‹€μ œλ‘œλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯된 ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•˜κ²Œ 될 것이닀.

@Test
void Test_sendEmail_from_TextTemplate_withHtml() {
    Assertions.assertDoesNotThrow(() -> {
        ClassPathResource resource = new ClassPathResource("templates/mail/2fa.html");
        String html = FileCopyUtils.copyToString(new BufferedReader(new InputStreamReader(resource.getInputStream())));
        // String html = """
        //     <h1>Two-Factor Authentication</h1>
        //     <h2>Hi, [[${name}]]</h2>
        //     <p>The two-step authentication code for the login request is as follows.</p>
        //     <p>Please enter it on the authentication screen within the time limit.</p>
        //     <p>Verification Code: <span style="font-size:20px;">[[${code}]]</span></p>
        //     """;
        Map<String, Object> variables = new HashMap<>();
        variables.put("name", "Mambo");
        variables.put("code", 123456);

        String htmlContent = mailService.processTemplate(html, variables, Locale.forLanguageTag("ko_KR"));
        Recipient recipient = new Recipient("Mambo", "kdevkr@gmail.com", Message.RecipientType.TO);
        Email email = mailService.prepare("[Auth] Requested Two Factor Authentication", htmlContent, recipient);
        MimeMessage mimeMessage = mailService.convertTo(email);
        mailService.send(mimeMessage);
    });
}

μœ„ μ½”λ“œμ—μ„œλŠ” 주석을 톡해 2fa.html νŒŒμΌμ— 2μ°¨ 인증을 μœ„ν•œ 이메일 ν…œν”Œλ¦Ώμ΄ μ–΄λ–»κ²Œ μž‘μ„±λ˜μ—ˆλŠ”μ§€λ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€.

ν…μŠ€νŠΈ ν…œν”Œλ¦Ώ λͺ¨λ“œ

μœ„ μ˜ˆμ‹œμ—μ„œλŠ” 일반적인 ν…œν”Œλ¦Ώ ν‘œν˜„μ‹μ΄ μ•„λ‹ˆλΌ ν…μŠ€νŠΈ λͺ¨λ“œμ˜ ν…œν”Œλ¦Ώ ν‘œν˜„μ‹μ„ μ‚¬μš©ν–ˆλ‹€. ν”„λ‘ νŠΈμ—”λ“œ κΈ°μˆ μ— μ˜ν•΄μ„œ μ‚¬μš©μžκ°€ 이메일 ν…œν”Œλ¦Ώμ„ μ‰½κ²Œ μž‘μ„±ν•  수 있게 μ§€μ›ν•˜λ©΄ μ’‹μ§€λ§Œ 그것이 μ€€λΉ„λ˜κΈ° μ „μ—λŠ” μœ„μ™€ 같이 ν…μŠ€νŠΈ λͺ¨λ“œλ‘œ μž‘μ„±ν•˜μ—¬ μ‘°κΈˆμ€ 더 μ‰½κ²Œ ν‘œν˜„ν•  수 μžˆλ‹€.

동적 ν…œν”Œλ¦ΏμœΌλ‘œ λ°œμ†‘λœ 이메일