2024. 4. 29. 16:15ㆍ스프링 (Spring)/스프링 팁 (Spring Tip)
이거 하려고 삽질을 꽤 했는데... 다음부터 하지 않기 위해 기록으로 남겨둡니다.
App Password
먼저 당연히 로그인 가능한 gmail 계정이 있어야 합니다.
그리고 https://myaccount.google.com/apppasswords 에 접속하고 로그인합니다.
그럼 아래와 같은 화면이 등장합니다.
여기서 App name을 원하는 이름으로 작성한 후, Create 버튼을 누릅니다.
이후 app 비밀번호 16자리가 등장하는데, 이걸 어딘가에 잘 기록해둡니다.
여담으로 기존 username, password 방식은 deprecated 되었습니다.
따라서 구글에 떠도는 less secure apps 설정을 건드리라는 말은 무시하시길 바랍니다.
참고 : Control access to less secure apps
Spring Boot Starter Mail
build.gradle.kts 에 spring-boot-starter-mail 을 추가해줍니다.
...
dependencies {
...
implementation(group = "org.springframework.boot", name = "spring-boot-starter-mail")
}
Spring 환경에서 쉽게 메일을 보낼 수 있는 라이브러리입니다. 고수준으로 작성되어 있으니 편리하게 사용하면 되겠죠.
자세한 내용은 공식 문서를 참고하시면 됩니다.
Mail Configuration
필자는 귀찮아서 그냥 빈으로 만들었는데,
본인이 여러 개 계정을 쓸 필요가 있다면 싱글턴 객체로 만들어도 무방할 듯 합니다.
적당한 위치에 GmailConfiguration 클래스를 생성합니다.
@Configuration
class GmailConfiguration {
@Bean
fun gmailSender(): MailSender {
val mailSender = JavaMailSenderImpl()
mailSender.host = "smtp.gmail.com"
mailSender.port = 587
mailSender.username = "계정@gmail.com"
mailSender.password = "일전에 생성한 app password"
mailSender.javaMailProperties.setProperty("mail.transport.protocol", "smtp")
mailSender.javaMailProperties.setProperty("mail.smtp.auth", "true")
mailSender.javaMailProperties.setProperty("mail.smtp.starttls.enable", "true")
mailSender.javaMailProperties.setProperty("mail.debug", "true")
return mailSender
}
}
연결 정보는 Send email from a printer, scanner, or app 에서 얻을 수 있습니다.
여담으로 3가지 옵션을 소개하고 있는데, 1번과 3번 옵션은 google workspace 가 필요하기 때문에 무시... 했습니다.
고려 사항을 읽어보니 하루 최대 2,000 개까지 가능하나보군요.
MailSender를 빈으로 등록했으니, 이제 편리하게 사용할 일만 남았습니다.
Gmail Service
gmail 서비스를 만듭니다.
fun interface GmailService {
fun send(fromEmail: String, toEmail: String, subject: String, text: String)
}
@Service
class GmailServiceImpl(
private val mailSender: MailSender
) :
GmailService {
override fun send(fromEmail: String, toEmail: String, subject: String, text: String) {
val message = SimpleMailMessage();
message.from = fromEmail
message.setTo(toEmail)
message.subject = subject
message.text = text
mailSender.send(message)
}
}
mailSender.send(message) 함수는 MailException을 발생시킬 수 있으니, 필요하다면 핸들링해도 됩니다.
이제 테스트도 해봅니다.
@SpringBootTest(classes = [GmailConfiguration::class])
class GmailServiceImplTest(
@Autowired private val mailSender: MailSender
) {
private val gmailService: GmailService = GmailServiceImpl(mailSender)
@Nested
@TestMethodOrder(OrderAnnotation::class)
inner class SendTest {
@Test
@Order(10)
fun givenToEmail_whenSendSubjectAndText_thenSentSuccess() {
// given
val givenFromEmail = "보내는 사람@gmail.com"
val givenToEmail = "받는 사람@gmail.com"
val givenSubject = "test mail subject"
val givenText = "this is for test."
// when & then
gmailService.send(
fromEmail = givenFromEmail,
toEmail = givenToEmail,
subject = givenSubject,
text = givenText
)
}
}
}
성공입니다.
TMI로 dataSource 문제로 테스트가 실행되지 않는 경우엔 아래처럼 Mock을 만들어주면 됩니다.
(당연히 데이터 통신 못합니다)
@TestConfiguration
class TestDataSourceConfiguration {
@Bean
@Primary
fun testDataSource(): HikariDataSource = Mockito.mock(HikariDataSource::class.java)
}
'스프링 (Spring) > 스프링 팁 (Spring Tip)' 카테고리의 다른 글
Kotlin 플러그인 All-open 파헤치기 (feat, JPA Fetch) (0) | 2024.05.18 |
---|---|
테스트 시 발생하는 위험 메시지 무시해보기 (0) | 2024.05.01 |
Kotlin 답게 간단하게 로깅해보기 (0) | 2024.04.30 |
Mockito를 이용해 JpaRepository 테스트하기 (0) | 2024.04.30 |
Spring에 Jacoco 적용해보기 (0) | 2024.04.29 |