DevJong12

[Issue] 회원가입 인증 메일의 방식 수정 본문

프로젝트/NoobLoL

[Issue] 회원가입 인증 메일의 방식 수정

Jong12 2022. 9. 10. 23:23
728x90

개요

프로젝트의 기능 구현을 하면서, 회원가입을 진행 할 때 메일을 발송해서 인증을 진행하는 기능을 제작하게 되었다.

문자열이 많지 않다 보니 메소드에서 String으로 제작을 진행하고 있었으며, SimpleMailMessage를 활용해 내용을 제작한 이후, JavaMailSender를 통해 발송을 하고 있었다.


기존 소스의 문제점

크게 두가지의 문제점이 존재했다.

1. HTML형식으로 메일이 오지를 않고 모두 문자열로 인식.

2. 메소드에서 내용을 제작하다 보니 내용의 수정이 필요할 경우 무조건적인 소스코드의 수정


기존코드로직

메일 발송을 담당하는 메소드 받아온 내용들을 SimpleMailMessage에 담아 발송한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  @Override
  public boolean sendMail(String toUser, Map<StringString> mailContent) {
    if (validMailSendValue(toUser, mailContent)) {
      return false;
    }
    SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
    try {
      //수신인 설정
      simpleMailMessage.setTo(toUser);
      //제목
      simpleMailMessage.setSubject(mailContent.get("title"));
      //내용
      simpleMailMessage.setText(mailContent.get("content"));
      javaMailSender.send(simpleMailMessage);
    } catch (MailException mailEx) {
      log.warn("메일 발송 실패, 사용자메일 : " + toUser);
      log.warn("[UserSendMailServiceImpl MailException]", mailEx);
      return false;
    } catch (Exception e) {
      log.warn("메일 발송 실패, 사용자메일 : " + toUser);
      log.warn("[UserSendMailServiceImpl Exception]", e);
      return false;
    }
    return true
  
  }
cs

 

메일의 내용을 제작하는 메소드이다. 하단의 메소드에서 제작을 하면 위의 메소드를 통해 발송을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 @Override
  public Map<StringString> getAuthMailContent(UserSignUpRequestDto userDto) {
    Map<StringString> mailContent = new HashMap<>();
 
    String titleStr = "[NoobLoL]" + userDto.getUserName() + "님 회원가입 인증 메일입니다";
    mailContent.put("title", titleStr);
    try {
      mailContent.put("content", getContent(userDto));
    } catch (UnknownHostException ex) {
      log.error("AuthMail Content Make Fail");
    } catch (Exception e) {
      log.error("AuthMail Map Make Fail");
    } finally {
      //Content가 Null이어도 Validation을 Mail에서 진행하니, 오류가 나더라도 무조건 Return을 진행해야 함.
      return mailContent;
    }
  }
 
  private String getContent(UserSignUpRequestDto userDto) throws UnknownHostException {
    String domain = InetAddress.getLocalHost().getHostName();
 
    String[] activeProfilesAry = environment.getActiveProfiles();
    String portNum = environment.getProperty("local.server.port");
    String contentStr = "<a href=\"http://" + domain;
 
    if (!ObjectUtils.isEmpty(activeProfilesAry)) {
      for (String activeProfile : activeProfilesAry) {
        if ("local".equals(activeProfile) || "dev".equals(activeProfile)) {
          contentStr += ":" + portNum + "";
          break;
        }
      }
    }
    contentStr += "/user/auth/" + userDto.getUserId() + "\"> NoobLoL 회원인증 링크 입니다 </a>";
 
    return contentStr;
  }
cs

해당 메소드에서 만들어지고 발송된 내용은 아래처럼 오고 있었다.

 

보면 문자열이 그대로 넘어오는데 Type이 String 그대로 오고 있는 상황이다.

 


문제의 해결법

1. 메일을 HTML로 타입을 변경하면된다. 메일의 content-type을 설정하면 된다.

2. thymeleaf를 활용해 TemplateEngine을 활용해보자 하였다.


수정 내용

1. 먼저 thymeleaf의 주입을 진행하였다. Gradle을 사용하기에 build.gradle를 통한 추가를 하였다.

implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'

 

 

2. 뼈대가 될 회원가입 html의 제작

thymeleaf를 사용하다보니 해당 문구의 사용이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
h<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title th:title="${title}">NoobLoL 회원가입 인증</title>
</head>
<body>
<div>
  <p>안녕하세요 <span th:text="${name}"></span>님 NoobLoL인증 안내 메일입니다.<br>
    <a th:href="${content}">인증 링크입니다</a>
  </p>
</div>
</body>
</html>
 
cs

추가로 경로는 아래와 같이 resources/templates/mail 아래에 제작을 하였다.

 

3. 사용하게될 메일의 경로를 입력해줘야 하기 때문에 경로를 상수화 하였다.

1
2
3
4
public class MailConstants {
 
  public static final String USER_SIGNUP = "mail/signup.html";
}
cs

 

4. 내용, 제목, 경로를 생성하는 Map의 메소드의 내용을 변경하였다.

최초 소스와 변경된점은 경로의 추가, 메일 내용의 경우에는 `인증링크 경로`만 제작을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  @Override
  public Map<StringString> getAuthMailContent(UserSignUpRequestDto userDto) {
    Map<StringString> mailContent = new HashMap<>();
 
    String titleStr = "[NoobLoL]" + userDto.getUserName() + "님 회원가입 인증 메일입니다";
    mailContent.put("title", titleStr);
    mailContent.put("name", userDto.getUserName());
    mailContent.put("context", MailConstants.USER_SIGNUP);
    try {
      mailContent.put("content", getContent(userDto));
    } catch (UnknownHostException ex) {
      log.error("AuthMail Content Make Fail");
    } catch (Exception e) {
      log.error("AuthMail Map Make Fail");
    } finally {
      //Content가 Null이어도 Validation을 Mail에서 진행하니, 오류가 나더라도 무조건 Return을 진행해야 함.
      return mailContent;
    }
  }
 
  private String getContent(UserSignUpRequestDto userDto) throws UnknownHostException {
    String domain = InetAddress.getLocalHost().getHostName();
 
    String[] activeProfilesAry = environment.getActiveProfiles();
    String portNum = environment.getProperty("local.server.port");
 
    String contentStr = "http://" + domain;
 
    if (!ObjectUtils.isEmpty(activeProfilesAry)) {
      for (String activeProfile : activeProfilesAry) {
        if ("local".equals(activeProfile) || "dev".equals(activeProfile)) {
          contentStr += ":" + portNum + "";
          break;
        }
      }
    }
    contentStr += "/user/auth/" + userDto.getUserId();
 
    return contentStr;
  }
cs

 

5. 메일을 발송하는 Service를 전체적으로 수정하였다.

혹시 해당 포스트를 따라 할 사람이 있다면 import를 참고하라는 의미에서 해당 영역은 전문을 넣었다. 

 

 

해당영역에서 수정은 아래와 같다.

a) SimpleMailMessage의 사용에서 MimeMessage로 변경하였으며, 내용을 Html로 지정하도록 수정

b) Map으로 받은 메일의 내용을 thymeleaf의 Context에 담은 이후 TemplateEngine을 통해 메일 내용을 읽어 오도록 수정 후 발송

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.nooblol.community.service.impl;
 
import com.nooblol.community.service.UserSendMailService;
import java.util.Map;
import javax.mail.Message.RecipientType;
import javax.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
 
@Slf4j
@Service
@RequiredArgsConstructor
public class UserSendMailServiceImpl implements UserSendMailService {
 
  private final JavaMailSender javaMailSender;
 
  private final TemplateEngine templateEngine;
 
  @Override
  public boolean sendMail(String toUser, Map<StringString> mailContent) {
    if (validMailSendValue(toUser, mailContent)) {
      return false;
    }
    MimeMessage mailMessage = javaMailSender.createMimeMessage();
    try {
      //수신인 설정
      mailMessage.addRecipients(RecipientType.TO, toUser);
 
      //제목
      mailMessage.setSubject(mailContent.get("title"), CharEncoding.UTF_8);
 
      //내용
      Context context = getMailAuthContext(mailContent);
      String message = templateEngine.process(mailContent.get("context"), context);
      mailMessage.setText(message, CharEncoding.UTF_8, "html");
      javaMailSender.send(mailMessage);
    } catch (MailException mailEx) {
      log.warn("메일 발송 실패, 사용자메일 : " + toUser);
      log.warn("[UserSendMailServiceImpl MailException]", mailEx);
      return false;
    } catch (Exception e) {
      log.warn("메일 발송 실패, 사용자메일 : " + toUser);
      log.warn("[UserSendMailServiceImpl Exception]", e);
      return false;
    }
    return true;
  }
 
  private boolean validMailSendValue(String toUser, Map<StringString> map) {
    return ObjectUtils.isEmpty(map.get("title"))
        || ObjectUtils.isEmpty(map.get("content"))
        || StringUtils.isBlank(toUser);
  }
 
  private Context getMailAuthContext(Map<StringString> mailContent) {
    Context context = new Context();
    context.setVariable("title", mailContent.get("title"));
    context.setVariable("name", mailContent.get("name"));
    context.setVariable("content", mailContent.get("content"));
    return context;
  }
}
 
cs

결과

해당 작업이후 다시 메일을 테스트 하였으며, 아래와 같이 제작한 html처럼 수신이 정상적으로 이뤄지는 걸 확인했다.

 

 

 

 

해당 기능을 제작하며..

사실 내용을 크게 적을 일이 없어서 간단하게 메소드에서 제작하는 것으로 끝내려 했으나, HTML형식으로 안오는거에서 변환을 하고자 마음을 먹게 되었다.  eml파일에 대해서 주로 다루는 곳에 있었으면서 망각하는게 사람은 참..자주 잊는걸 다시 느꼈던것 같다 ㅎㅎ...

 

 

 

 

 

 

728x90
Comments