서론
현재 개발하고 있는 프로젝트에서 html 형태의 보고서를 만들고, 이메일로 유저에게 전송해야할 일이 생겼다. 기존의 코드에서는 nodejs에서 'nodemailer'를 사용하고, gmail을 통해서 전송하였다.
이때 생기는 문제점이 gmail 이라서 유저에게 신뢰성이 떨어질 수 있고, 별도의 DKIM 인증 등 스팸 메일로 분류되는 것을 피하기 위한 설정을 하지 않았기에 일부 메일이 스팸으로 분류되는 문제가 있었다.
( 물론 nodemailer에서도 이러한 기능을 지원한다 https://nodemailer.com/dkim/ )
유저 뎁스가 깊어서 많이 사용하지 않던 기능이라 일단은 레거시로 놔두고 있었는데, 이번에 해당 부분을 개편하면서 아예 새로운 방법을 택했다. 현재 서비스에서 사용하고 있는 도메인이 AWS Route53에 등록되어 있었고, 도메인 자체는 외부 사이트(가비아)에서 구매하였다.
Amazon Simple Email Service(이하 SES)에서는 어플리케이션 안에서 이메일을 보내기에 용이하고, 인증 같은 부분들을 편하게 구현할 수 있었다.
이 글에서는 SES를 어떻게 세팅하고, 서비스에 적용했는지 기록해두려한다.
본격적인 사용법에 앞서 이번 세팅을 하면서 배운 이메일 송수신 프로토콜을 가볍게 정리해보겠다.
이메일 프로토콜
송신
SMTP : 클라이언트 to 메일서버, 메일서버 to 메일서버 사이의 메일 송수신에 사용되는 프로토콜이다.
수신
POP3 : 메일 서버 내의 메일함에서 로컬서버로 메일을 내려받은 후 읽는다. 네트워크에 연결되지 않아도 메일을 볼 수 있는 장점이 있지만, 다른 컴퓨터에서 읽을 수는 없는 단점이 존재
IMAP4 : 직접 메일 서버 내의 메일함에서 메일을 읽는다. POP3와는 반대로 인터넷만 연결되어 있으면 어디서든 메일을 읽을 수 있다.
AWS SES 세팅
먼저 지금 같이 자본이 없는 상황에서 중요한 프리티어..!
1. 콘솔 로그인하고 SES에 들어간다. Create identity 클릭
(이때, 버지니아 북부로 리전설정을 했는데, 서울 등 일부 리전에서는 송신만되고 수신을 지원하지 않는 부분도 있어서 확인해보고 사용하여야한다.)
2. 사용할 도메인 입력(미리 도메인을 사둬야함, 미리 Route53에 등록해두면 더 편함)
3. 도메인 인증 방법 선택(디지털 서명 같은거임, 스팸함으로 안빠지게)
4. 도메인 소유권 인증을 위한 CNAME이 온다. Route53에 도메인을 등록해둿다면 자동으로 들어가고 아니면 직접 레코드를 복붙해주면 된다.
5. Route53에 자동으로 등록되고 2,3분쯤있으면 인증이 완료된다.
6. 인증된 모습
7. SMTP credential을 만들어준다.
8. IAM 유저를 생성할 이름을 적어준다. 나중에 메일 보낼 때 필요하니 이때 나오는 사용자id와 비밀번호(CSV로도 줌) 잘 저장해두자.
9. 이후 테스트해볼 수신자 이메일을 등록한다.
AWS SES에서는 sandbox환경과 실제 환경이 구분되어 있는데, sandbox 환경에서는 이메일을 등록한 사용자에게만 송신을 할 수 있다. 잘 되는지 테스트를 위해 등록해본다.
10. 등록 대기 중인 모습, 수신할 메일의 메일함에 가보면
11. 이렇게 인증 메일이 와있다. 중간에 긴 링크를 클릭해 인증을 해주자
12. 돌아와서 새로고침해보면 정상적으로 verified 된것을 볼 수 있다. 테스트 메일을 보내기 위해 Send test email을 클릭한다.
13. 주절주절 보낼 내용을 적어본다.
14. 짠, 잘 보내진다.
15. 이제 잘 되는걸 확인했으니 sandbox 환경에서 실제 환경으로 꺼내달라고 요청해야한다.(아마 스팸 메일 무차별로 보낼 수 있어서 이런식으로 막아둔듯하다)
16. 꼭 들어가야하는 내용(어디에 써야하고, 수신제한은 어케 처리하고, 사용용도는 뭔지 등)을 포함해서 AWS에 요청해주자. 한 반나절 내로 승인되었던거 같다.
17. 이런 내용을 담아서 썼더니 바로 한큐에 통과되었다. 경우에 따라서는 추가정보를 요청할 수도 있다.(AWS 계정 이메일로 연락이 온다)
18. 이렇게 메일이 왔다. 하루에 50k 만큼 보낼수 있도록 허가되었다!
Node.js와 연결
node.js에서 바로 보내는 방법이 잘 안되서 python을 거치는 방법을 사용하였다.
어쩌다보니(알고 싶지 않았지만) Linux(ubuntu22.04), mac OS(M1), window10 환경에서 다 잘돌아가는걸 확인할 수 있었다. smtplib, MIMEText, Header 라이브러리를 사용했다. 코드설명은 직관적이니 생략
# send-email.py
import smtplib
import sys
from email.mime.text import MIMEText
from email.header import Header
def main(account, password, server, from_addr, to_addr, content, title):
con = smtplib.SMTP_SSL(server, 465)
con.login(account, password)
message = MIMEText(content, 'html')
message['Subject'] = Header(title, 'utf-8')
message['From'] = from_addr
message['To'] = to_addr
con.sendmail(from_addr, [to_addr], message.as_string())
con.close()
if(__name__ == '__main__'):
main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6], sys.argv[7])
JS에서 python-shell로 python 파일을 호출하는 방식으로 동작한다. .env 파일에 아까 받은 IAM 어카운트, 패스워드를 입력하고 MAIL_SERVER에는 (7)에 있는 SMTP endpoint를 넣어준다.
// send-report-by-email.js
import { PythonShell } from "python-shell";
require("dotenv").config();
const sendReportByEmail = async (
USER_EMAIL
) => {
const CONTENT = "CONTENT";
if (!CONTENT) {
return false;
}
const AWS_ACCOUNT = process.env.AWS_ACCOUNT;
const AWS_PASSWORD = process.env.AWS_PASSWORD;
const MAIL_SERVER = process.env.MAIL_SERVER;
const FROM_ADDR = "no-reply@ddakzip.com";
const TO_ADDR = USER_EMAIL;
const TITLE = `제목`;
var options = {
mode: "text",
pythonPath: "",
pythonOptions: ["-u"],
scriptPath: "",
args: [
AWS_ACCOUNT,
AWS_PASSWORD,
MAIL_SERVER,
FROM_ADDR,
TO_ADDR,
CONTENT,
TITLE,
],
encoding: "utf8",
};
return new Promise((resolve, reject) => {
try {
PythonShell.run(
"./src/utils/reports/send-email.py",
options,
function (err, results) {
if (err) {
throw err;
}
resolve(true);
}
);
} catch {
reject(false);
}
});
};
module.exports = sendReportByEmail;
돈이 있다면 이러지말고 google workspace 같은거 결제해서 쓰는게 속편하고 팀원들 관리도 훨씬 편할 것 같다... 특히나 수신할 때는 세팅이 더 힘들었어서 그냥 월 4달러씩 내고 쓸껄 그랬나 싶기도 하지만 좀 더 코드레벨에서 자유롭게 보낼 수 있다는 장점은 있는것 같다.
'인프라 > AWS' 카테고리의 다른 글
[AWS] Elastic Beanstalk 내부 로직, 구성, 네트워크 연결 정리 (0) | 2023.04.12 |
---|---|
[AWS] AWS S3 + CloudFront + Route53으로 https로 React 웹 호스팅하기 (0) | 2022.11.19 |
[ElasticSearch] AWS OpenSearch Service로 NodeJS 서버 logging하고, kibana로 분석하기 (0) | 2022.09.30 |
[AWS] Elastic Beanstalk에서 고용량 파일(이미지)을 받을 때 nginx 설정 (0) | 2022.07.18 |
[AWS] Github Action을 사용해서 Elastic Beanstalk에 nodejs 서버 CI/CD (0) | 2022.07.18 |