Spring에서 Gmail 보내기

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 환경에서 쉽게 메일을 보낼 수 있는 라이브러리입니다. 고수준으로 작성되어 있으니 편리하게 사용하면 되겠죠.

자세한 내용은 공식 문서를 참고하시면 됩니다. 

 

Email :: Spring Framework

A class that comes in pretty handy when dealing with JavaMail messages is org.springframework.mail.javamail.MimeMessageHelper, which shields you from having to use the verbose JavaMail API. Using the MimeMessageHelper, it is pretty easy to create a MimeMes

docs.spring.io

 

 

 

 

 


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)

}