
이메일 발송 서비스로 Mailgun을 선택했습니다.
Spring boot 서버와 연결해서 사용하기 위해 Mailgun 문서를 참고했습니다
Mailgun은 이메일 발송 방식으로 두 가지를 제공합니다
최적의 선택을 위해 각 방식의 특징과 장단점을 알아보겠습니다.

SMTP는 인터넷을 통해 이메일 메세지를 발송하는 통신 프로토콜입니다.
클라이언트가 메일 서버로 메세지를 발송할 수 있도록 도와줍니다.
과거에는 25번 포트를 사용했습니다
하지만 사용자 인증 기능이 없어 스펨메일에 취약하다는 보안 문제 때문에,
최근에는 암호화된 이메일 포트인 587번 포트 (SMTPS)를 사용합니다
번외: 다른 메일 프로토콜은 없을까?
다음과 같은 두가지 프로토콜이 존재합니다
- IMAP: 메일 서버에서 전자메일을 관리하고 메일 수신 처리 및 검색을 지원합니다
- POP3: IMAP과 비슷하게 메일 수신 처리 및 검색을 지원합니다
IMAP VS POP3
둘의 기능이 비슷해서 차이가 없다고 생각할 수 있으나, 몇 가지 차이점이 존재합니다차이점:
- IMAP은 다중 로그인을 지원해서 사용자가 여러 디바이스를 통해 접근 가능합니다.
하지만 POP3는 한대의 PC에서만 접근 가능합니다- IMAP은 이메일 서버에 동기화하면서 관리하지만 POP3는 동기화하지 않습니다.
또한 POP3는 이메일을 로컬로 다운로드하면 서버에서 해당 이메일을 삭제합니다
의문점: IMAP이 상위호환 프로토콜로 보이는데 왜 POP3를 아직도 사용하는 것일까?
POP3 방식이 갖는 장점도 존재하기 때문입니다
- 오프라인 환경에서 접근할 때, POP3의 다운로드 후 클라이언트에서 보관하는 방식 덕에
IMAP보다 더 접근성이 좋습니다- 또한 IMAP는 동기화 방식으로 서버에서 이메일을 저장해서 관리하지만,
POP3는 클라이언트가 관리하므로 서버의 부담을 줄여줄 수 있습니다.따라서 오늘날까지도 POP3 방식이 IMAP과 함께 사용되고 있는 것입니다.
클라이언트가 SMTP 프로토콜을 이용하여
메일을 발송하면 Mailgun이 받아 해당 메세지를 안전하게 목적지로 포워딩해줍니다
RESTful API를 기반으로 동작합니다.
API요청을 기반으로 특정 메세지를 생성해서 클라이언트가 원하는 수신자에게
이메일을 발송합니다
Mailgun에서 추천하는 환경을 정리했습니다.
Transaction 메일이란?
운영중인 서비스에서 특정 작업이 발생한 후, 사용자에게 전송되는 자동화된 메일입니다현재 프로젝트에서는 회원가입과 로그인과정에서 이메일을 발송하는데,
이것도 트랜잭션 이메일이라고 할 수 있습니다.

두 방식 중 Mailgun API를 선택했습니다.
프로젝트에서 다수의 사용자가 회원가입과 로그인을 위해 이메일을 발송할 것이기 때문에
서버의 부하를 줄이고, 빠른 속도를 낼 수 있는 Mailgun API가 적합하다고 생각했습니다
또한 API의 단점도 결국 개발자의 역량 문제라서 충분히 극복해낼 수 있다고 생각했습니다
따라서 SMTP 방식이 아닌 Mailgun API 방식을 선택했습니다.
Mailgun에서는 자바 8 버전부터 사용가능한 전용 API를 제공해줍니다
Gradle에 다음 의존성을 추가하면 사용할 수 있습니다
implementation 'com.mailgun:mailgun-java:1.1.3'
아래 Message 타입의 객체를 빌더 패턴으로 생성해준 다음
Message message = Message.builder()
.subject(SUBJECT)
.from(SENDER_EMAIL)
.to(toEmail)
.html(mailUtil
.setContextUtil(toEmail, code
, registerCheck ? MAIL_LOGIN_HTML
: MAIL_REGISTER_HTML))
.build();
아래와 같은 방식으로 전송하면 됩니다
MessageResponse messageResponse = mailgunClient.mailgunMessagesApi()
.sendMessage(EMAIL_DOMAIN,message);
참고, 위 코드는 프로젝트에서 작성한 코드입니다.
약간 변형했으므로 원형을 보고 싶다면 Mailgun API 문서를 참고해주세요!
실제 개발했을 때는 Mailgun API를 사용하지 않았습니다
개발할 당시에 "mailgunMessagesApi()" 부분에서 빨간불이 떴고,
이 문제를 해결하기 위해
'직접 api 레퍼런스를 참고해서 Repository 형태로 구현해야한다'고 착각했습니다
이후에도 나오겠지만 사실 이 문제는 설정 몇 줄 추가하면 되는 문제였습니다
자세한 내용이 깃허브에 있었는데도, 제대로 확인하지 못하고 다른 방법을 탐색하였습니다
Spring OpenFeign은 선언적인 HTTP 클라이언트 도구로,
외부 API호출을 쉽게할 수 있도록 도와줍니다
보통 Spring Cloud 환경에서 다른 기술들과 통합하기 위해 주로 사용하며,
Spring Data JPA처럼 애노테이션 기반으로 동작해서
세부 로직을 직접 구현할 필요 없이 편하게 사용할 수 있습니다
단순 선언만으로 기능 구현이 가능하다는 내용을 보고,
해당 기술을 사용해보자고 생각했습니다
다음 종속성을 추가하면 OpenFeign을 사용할 수 있습니다
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
앞서 말했듯이 애노테이션 기반으로 동작하기 때문에,
다음 메소드만 추가하면 쉽게 Mailgun API 이메일 발송 로직을 개발할 수 있습니다
@FeignClient(name = "mailgun", url="${mailgun.base-url}")
@Qualifier(value = "mailgun")
public interface MailgunClient {
@PostMapping("${mailgun.messages-url}")
ResponseEntity<String> sendEmail(@SpringQueryMap SendMailForm form);
}
@SpringQueryMap에서 DTO를 토대로 원하는 형식을 만들어줍니다
다만 외부 API 호출을 쉽게 도와주는 HTTP 클라이언트 도구이다보니,
API호출할 때 인증이 필요한 경우에는 호출마다 인증 작업을 추가해야합니다.
이 과정을 편하게 하기 위해서 OpenFeign에서는 다음과 같은 설정을 지원해줍니다
@Qualifier(value = "mailgun")
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("api", key);
}
이제 별도의 인증 로직 없이 Mailgun API 주소로 이메일 발송을 요청할 수 있게 되었습니다
이메일 발송 로직은 다음과 같습니다
SendMailForm sendMailForm = SendMailForm.builder()
.subject(SUBJECT)
.from(SENDER_EMAIL)
.to(toEmail)
.html(mailUtil
.setContextUtil(toEmail, code
, registerCheck ? MAIL_LOGIN_HTML
: MAIL_REGISTER_HTML))
.build();
mailgunClient.sendEmail(sendMailForm);
이 방식으로 개발을 완료하였고, 정상동작하는 것도 확인하였습니다.
사실 이 방법을 쓸 때도 이 방식이 과연 나은 대안일까라는 고민을 했습니다.
하지만 더 나은 방법을 찾지 못하고 그대로 OpenFeign 사용을 유지했었습니다.
이후 리팩토링을 진행하던 와중 Mailgun API 깃허브 문서를 발견하였고,
더 자세한 내용을 확인할 수 있었습니다
implementation 'com.mailgun:mailgun-java:1.1.3'
Mailgun API를 사용하기 위해서는 위 Gradle 종속성을 추가해주면 됩니다
@Bean
public MailgunMessagesApi mailgunMessagesApi(){
return MailgunClient.config(key)
.createApi(MailgunMessagesApi.class);
}
Mailgun Client를 위와같이 Bean으로 등록하면 됩니다.
잘 보면 메소드명이 앞서 빨간불이 떴다는 코드명과 일치합니다.
개발과정에서 직접 구현해야한다고 생각했던 부분이
알고보니 설정을 주입하는 단순한 코드였던 것이었습니다
설정만 해주면 내부 로직에 대해 깊게 알 필요 없이, 지정한 이메일을 발송할 수 있습니다
Message message = Message.builder()
.subject(SUBJECT)
.from(SENDER_EMAIL)
.to(toEmail)
.html(mailUtil
.setContextUtil(toEmail, code
, registerCheck ? MAIL_LOGIN_HTML
: MAIL_REGISTER_HTML))
.build();
MessageResponse messageResponse = mailgunClient.mailgunMessagesApi()
.sendMessage(EMAIL_DOMAIN,message);
Message 객체를 만든다음, MailgunClient로 메세지 발송하면 끝입니다.
OpenFeign 동작을 위해 별도의 Client 인터페이스를 만들어서 선언할 필요없이
더 편하게 사용할 수 있습니다

또한 발송과정에서 Mailgun Client는 보기 편한 로그 기능도 제공해줍니다.
Mailgun API가 코드양도 적으면서 원하는 기능도 제공해주고,
로그 기능도 제공해주기 때문에 사용하지 않을 이유가 없습니다
깃허브 38번 PR로 변경한 코드를 Merge했습니다.
이제 Mailgun API로 이메일 발송로직이 동작합니다!
Spring OpenFeign이나 Mailgun API 모두 기능적으로는 정상적으로 동작합니다
하지만 효율성을 생각했을 때, Mailgun API를 사용하는 것이 더 합리적입니다.
외부 API와 통신하는 로직이 Mailgun API를 제외하고는 없으며,
이후 확장 가능성도 없기 때문입니다.
따라서 설정방법도 더 간단하고, 로그 편의성도 제공해주는
Mailgun API를 사용하는 것이 더 효율적인 방법입니다.
따라서 잘못된 방식을 선택했던 Spring OpenFeign을 Mailgun API로 변경하였습니다
Mailgun API로 전환한 후에도 테스트를 진행하였습니다

정상적으로 메일이 도착하는 것을 확인할 수 있었습니다!
문서를 꼼꼼히 읽는 습관을 길러야할 것 같습니다
잘못된 선택도 결국 문서를 제대로 안 읽어서 발생한 문제입니다.
조급함보다는 꼼꼼함으로 더 나은 선택을 할 수 있도록, 많이 학습해야겠습니다.