이번 글에서는 AWS의 SES(Simple Email Service)를 사용하여, 이메일을 보낼 것입니다.
Java 버전의 SDK도 존재하지만, 이번에는 Kotlin 용으로 나온 AWS SDK Kotlin을 사용해보려고 합니다.
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("aws.sdk.kotlin:ses:0.16.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
이메일을 보낼 때는, thymeleaf를 통해 html 템플릿 형태의 메일을 전송할 것이며 aws sdk kotlin을 사용할 것입니다.
aws sdk kotlin은 코루틴 방식으로 구현되어 있기 때문에 kotlinx-corutines 도 함께 추가해야합니다.
간단하게, 아래와 같은 인증번호를 받는 템플릿을 만들어보겠습니다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<div style="font-family: 'Apple SD Gothic Neo', 'sans-serif' !important; width: 540px; height: 600px; border-top: 4px solid #2A7AF3; margin: 100px auto; padding: 30px 0; box-sizing: border-box;">
<h1 style="margin: 0; padding: 0 5px; font-size: 28px; font-weight: 400;">
<span style="color: #2A7AF3">인증번호</span> 안내입니다.
</h1>
<p style="font-size: 16px; line-height: 26px; margin-top: 20px; padding: 0 5px;">
아래 <b style="color: #2A7AF3">인증번호</b>를 <b>5분내로</b> 입력해주세요.
</p>
<div style="color: #FFF; text-decoration: none; text-align: center;">
<p style="display: inline-block; width: 210px; height: 45px; margin: 30px 5px 40px; background: #2A7AF3; line-height: 45px; vertical-align: middle; font-size: 16px;" th:text="${code}"></p>
</div>
<div style="border-top: 1px solid #DDD; padding: 5px;">
<p style="font-size: 13px; line-height: 21px; color: #555;">
만약 버튼이 정상적으로 클릭되지 않는다면, 아래 링크를 복사하여 접속해 주세요.<br/>
아무에게도 이 링크를 공유하지마세요. 회원 정보가 유출되는 문제가 발생 할 수 있습니다.<br/>
</p>
</div>
</div>
</body>
</html>
th:text="${code}"
는 thymeleaf의 문법으로, code에 가변적인 값을 템플릿 형태로 넣어줄 수 있게 해줍니다.
위 template을 Spring의 src/main/resources/mail-templates
아래에 code.html
으로 저장해줍니다.
spring:
thymeleaf:
prefix: classpath:/mail-templates/
suffix: .html
mode: HTML
encoding: UTF-8
check-template-location: true
cache: false
위와 같이 thymeleaf를 위한 application.yml 설정 값을 세팅해줍니다.
이렇게 설정하면, 코드에서 src/main/mail-templates/
에 존재하는 thymeleaf 템플릿을 가져올 수 있게 됩니다.
aws:
access-key: 액세스키
secret-key: 시크릿키
위와 같이, application.yml 에 ses에 사용할 액세스키와 시크릿키를 넣어줍니다.
위 값들은, 아래 글을 보고 생성하면 됩니다.
https://lannstark.tistory.com/66
메일을 전송하는 비즈니스 로직이며, 간단하게 인증번호를 보내는 코드입니다.
package com.example.mail.service
import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider
import aws.sdk.kotlin.services.ses.SesClient
import aws.sdk.kotlin.services.ses.model.Body
import aws.sdk.kotlin.services.ses.model.Content
import aws.sdk.kotlin.services.ses.model.Destination
import aws.sdk.kotlin.services.ses.model.Message
import aws.sdk.kotlin.services.ses.model.SendEmailRequest
import org.thymeleaf.context.Context
import org.thymeleaf.spring5.SpringTemplateEngine
@Service
class MailService(
private val templateEngine: SpringTemplateEngine,
@Value("\${aws.access-key}")
private val accessKey: String,
@Value("\${aws.secret-key}")
private val secretKey: String
) {
// coroutine이라 suspend
suspend fun sendMail(to: String) {
// 인증번호 생성
val code = generateRandomCode()
// thymeleaf template에 사용 할 context 생성
val context = Context()
context.setValue("code", code)
// email 요청 생성
val emailRequest = SendEmailRequest {
// 메일 받는 사람
destination = Destination {
toAddresses = listOf(to)
}
// 메일 메시지 생성
message = Message {
// 메일 제목
subject = Content {
data = "인증 번호"
}
// 메일 내용
body = Body {
html = Content {
// code.html에 인증번호 삽입
data = templateEngine.process("code", context)
}
}
}
// 보내는 사람
source = "no-reply@test.com"
}
// aws ses 를 이용하여 mail 전송
SesClient {
// region 설정
region = "ap-northeast-2"
// aws 인증
credentialsProvider = StaticCredentialsProvider {
accessKeyId = accessKey
secretAccessKey = secretKey
}
}.use {
// mail 전송
it.sendEmail(emailRequest)
}
}
// 인증번호 생성
fun generateRandomCode(): String {
return (1..5).map { (0..9).random() }.joinToString("")
}
}
위에서 구현한 비즈니스 로직을 실행할 수 있도록, Controller에서 응답을 받도록 하였습니다.
@RestController
@RequestMapping("/api/mail")
class MailController(
private val mailService: MailService
) {
@GetMapping("/{mail}")
fun sendPasswordChangeMail(@PathVariable("mail") mail: String): ApiResponse<String> {
// 동기적인 Spring MVC의 특성상, runBlocking을 이용하여 suspend fun을 실행
return runBlocking {
mailService.sendMail(mail)
ApiResponse.success("success")
}
}
}
사실 아까 위에서, 예시로 보여줬던 사진이 실제 결과입니다.
정말 깔끔하게 잘 갑니다.
AWS SES가 sendbox stage인 경우, AWS 팀에 연락을 하여 실 서비스에 쓸 수 있도록 해야합니다.
그렇지 않으면, 등록한 이메일로 밖에 못보내게됩니다.