이전 프로젝트 진행 시에 Spring Boot로 구글 SMTP
를 이용하여 이메일을 텍스트 형태로 보낸 적이 있다.
이번 재능교환소
팀프로젝트에선 JWT를 활용하여 계정 활성화를 시켜 로그인이 가능하도록 만들었다.
계정 활성화는 회원가입 시 입력한 이메일 주소로 전송되며, 버튼을 누르면 계정이 활성화되도록 구현하였다.
이때 사용할 홈페이지 로고 이미지를 함께 첨부해보려고 한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
mail:
host: smtp.gmail.com
port: 587
username: [이메일 계정]
password: [비밀번호]
properties:
mail:
smtp:
auth: true
starttls:
enable: true
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>재능 교환소 계정 활성화 인증 메일</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
text-align: center;
padding: 20px;
}
.container {
background-color: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
max-width: 600px;
margin: 0 auto;
}
h2 {
color: #333;
}
p {
color: #666;
margin-bottom: 20px;
}
button {
background-color: #FC5230;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin-top: 20px;
border-radius: 4px;
cursor: pointer;
border: none;
}
button:hover {
background-color: #fc3210;
}
</style>
</head>
<body>
<div class="container">
<img src='/img/Logo.png' alt="로고 이미지">
<h2>안녕하세요, <span th:text="${id}"></span>님</h2>
<p>누군가가 새로운 위치에서 회원님의 계정으로 접속을 시도하고 있어요.</p>
<p>본인이라면 아래 버튼을 클릭하여 로그인을 승인해주세요.</p>
<a th:href="${jwtLink}">
<button type="button">로그인 인증</button>
</a>
</div>
</body>
</html>
Servie와 통신 시 사용할 화면이다.
메일 전송 시 해당 View가 전송될 예정이다.
본격적으로 로직을 구현해보자!
package place.skillexchange.backend.service;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.io.IOException;
import java.util.HashMap;
@Service
@RequiredArgsConstructor
public class MailService {
private final TemplateEngine templateEngine;
private final JavaMailSender emailSender;
@Value("${spring.mail.username}")
private String sender;
public void getEmail(String email, String id, String activeToken) throws MessagingException, IOException {
MimeMessage message = emailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
//메일 제목 설정
helper.setSubject("재능교환소 계정 활성화를 위한 로그인 인증");
//수신자 설정
helper.setTo(sender);
//참조자 설정
helper.setCc(email);
//템플릿에 전달할 데이터 설정
HashMap<String, String> emailValues = new HashMap<>();
emailValues.put("id", id);
emailValues.put("jwtLink", "http://localhost:3000/active/"+activeToken);
Context context = new Context();
emailValues.forEach((key, value)->{
context.setVariable(key, value);
});
//메일 내용 설정 : 템플릿 프로세스
String html = templateEngine.process("email-template", context);
helper.setText(html, true);
//템플릿에 들어가는 이미지 cid로 삽입
helper.addInline("image", new ClassPathResource("static/img/Logo.png"));
//메일 보내기
emailSender.send(message);
}
}
여기서 주의깊게 봐야 할 것은 MimeMessageHelper 클래스
의 addInline 메소드
다.
만약, 해당 메소드 없이 html 템플릿 메일을 보내게 되면 이미지가 깨진다.
왜냐? 당연히... 프로젝트 내부에 파일이 있으니 이미지 경로를 못찾는 것이다.
html 템플릿 안의 이미지를 전송하는 방법은 여러 가지가 있는데, cid를 이용하여 이미지를 html안에 임베딩하여 보내는 방식을 사용해보자.
전송할 html 양식 안의 img 경로를 다음과 같이 설정한다.
<img src='cid:image' alt="로고 이미지">
그리고 이메일 전송 전 addInline 메소드
로 해당 cid
를 적용한다.
helper.addInline("image", new ClassPathResource("static/img/Logo.png"));
프로젝트 내부의 이미지를 사용하기 위해서는 ClassPathResource
를 사용하여 이미지 파일을 로드한다.
코드에서 new ClassPathResource("static/img/Logo.png")
와 같이 이미지 파일의 경로를 지정하여 리소스를 가져올 수 있는 것이다.
해당 이미지를 MimeMessage
에 첨부하고, HTML 템플릿에서 <img src='cid:image'>
와 같이 cid를 사용하여 이미지를 참조할 수 있다.
좀 더 자세히 말하자면
이미지 파일(예: Logo.png)은 프로젝트의 리소스(static/img/ 디렉토리에 위치)로부터 읽혀진다.
파일을 바이트 배열로 읽어들인다. 이 과정에서 Files 클래스의 readAllBytes() 메서드를 사용할 수 있다.
바이트 배열로 읽은 이미지 데이터는 Base64 인코딩으로 인코딩된다. 이렇게 하면 이진 데이터를 텍스트 형식으로 변환할 수 있다.
변환된 Base64 문자열은 MimeMessageHelper의 addInline() 메서드를 사용하여 MimeMessage에 첨부된다. 이때 Content-ID (cid)가 이미지에 부여된다.
(cid는 Content-ID의 약자이며, 이는 메일의 본문에 첨부된 이미지를 식별하는 데 사용)
이메일 수신자가 이메일을 열면, 이진 데이터가 Base64 디코딩되고 이미지로 표시된다.
이렇게 하면 이미지 파일이 이메일의 일부로 효과적으로 첨부되고, 이메일을 받는 사람이 이미지를 별도로 다운로드할 필요 없이 바로 볼 수 있다.
해당 메서드를 컨트롤러에서 불러 사용하게 되면 이메일은 성공적으로 전송된다.