[Django] AWS SES로 이메일 보내기

우노·2023년 8월 8일
0
post-custom-banner

django로 이메일을 통한 사용자 인증 기능을 구현하면서 구글링도 많이 하고 질문도 많이 해서 정리해보려고 한다 !

이메일 인증

이메일로 사용자 인증을 하는 방식은 크게 두가지가 있다.

  1. 메일에 첨부된 링크로 인증하는 방식
  2. 인증 코드를 보내고 사용자가 코드를 입력해서 인증하는 방식

우리 팀은 2번 방식을 사용했다.
우리가 개발하는 서비스는 가입 과정에서 계정 입력과 정보 입력이 나누어져 있어서 페이지 이동을 위해 2번이 낫다고 판단했지만 한 번에 모두 입력받아서 넘어가는 경우에는 확실히 1번이 편하다.

AWS SES

사용 이유

물론 파이썬에서는 SMTP로 메일을 보낼 수 있다. 하지만 확인되지 않는 도메인 또는 사용자가 보낸 이메일은 지메일, 네이버 메일 등에서 걸러져서 메일이 도착하지 않을 수 있다는 조언을 듣고 처음부터 SES를 쓰기로 결정했다.

세팅 과정

당연하게도 AWS 계정은 있어야 한다.

도메인으로 서비스를 배포할 계획이라면 본인 소유의 도메인도 있어야 하고 단순히 본인 계정으로 SES를 통해 메일을 보낼 생각이라면 이메일 계정만 있으면 된다.

이메일은 자격 증명부터 사용이 비교적 간단하다! 그래서 아래의 모든 내용은 본인 소유의 도메인을 기준으로 설명한다.

자격 증명

도메인 자격 증명을 생성해서 도메인이 본인 소유임을 증명해야한다.나는 위와 같은 옵션으로 설정했으나 이 설정은 자율이다 !

자격 증명 생성을 누르면 아래에 DNS CNAME 레코드가 3개 나오는데 이를 도메인에 추가해주면 된다.

호스트: xxx._domainkey
값/위치: xxx.dkim.amazonses.com.

다음과 같이 추가하면 된다. 이걸 적어두는 이유는 내가 헤매서...
DNS 레코드는 더 공부해야할 것 같다.

레코드를 입력하면 얼마 지나지 않아 보안 인증 상태확인됨으로 바뀐다.

샌드박스

자격 증명을 생성하면 하루에 200건 정도의 이메일을 샌드박스 내부에서 보낼 수 있게 된다. 발신 가능 건수를 높이고 샌드박스에서 꺼내오려면 AWS에 직접 문의를 넣어야한다.
나는 서비스 목적과 용도, 예상 발신 건수, 반송 및 수신 거부 건수 예상 및 대응책 정도를 간단하게 제출했다. 근데 오래 걸리는 사람도 있다고 하니 한 번에 최대한 자세하게 설명하는 게 좋을 것 같다.
위와 같은 메일을 받으면 샌드박스에서 나오기 성공 !

샌드박스에서 나오면 24시간 내에 발신 가능 건수가 50000건으로 늘어나고 이메일 자격 증명을 통해 원하는 계정으로 메일을 보낼 수 있게 된다.

이메일 자격 증명

도메인 자격 증명으로 메일을 보낼 수 있게 된 것은 아니다.
도메인에서 메일을 보통 no-reply@example.com 과 같은 계정으로 보내는데 이 계정에 대한 자격 증명도 필요하다.

우선 이메일 자격 증명을 생성한다. 그러면 이메일의 링크를 통해 이메일을 인증하라고 할텐데 여기서 막혔었다.
도메인에 메일 계정을 어떻게 생성하고 메일을 열람하지...?
그래서 질문한 결과 메일 호스팅 서비스를 이용하라는 답변을 받았다.

무료 메일 호스팅 서비스인 zoho를 추천받아 이용했다. 가입하고 메일 계정을 등록하고 제시되는 여러개의 TXT, MX 레코드를 본인의 도메인에 추가해주면 된다. 그럼 해당 메일 계정의 메일을 열람할 수 있다 !
좀 바보같지만 여기서도 메일함 찾는 법을 헤매서.. 로고 옆 좌측 상단을 누르면 메일함으로 이동할 수 있다.

메일함으로 들어가서 aws가 보낸 메일의 링크를 누르면 이메일 자격 증명 성공 ! 이제 해당 계정으로 SES를 이용해 메일을 보낼 수 있다 !

IAM 사용자 생성

해당 과정은 자료가 많은 것 같아 생략한다 !
중요한 점은 SES Access 에 대한 권한을 추가하는 것, access_key_idsecret_access_key를 저장하는 것이다.

django

이제 SES 세팅은 마치고 django에서 SES를 통해 메일을 보내도록 구현해야한다. 가상 환경, 프로젝트 구조 등은 자율로 맡기고 주요 코드만 설명하겠다.

pip install boto3

Python용 AWS SDK인 boto3를 설치해준다. SES 뿐만 아니라 S3 등에서도 boto3를 사용한다.

django에서 SES로 메일 보내기

AWS SES를 이용한 회원가입 email 인증 python으로 구현하기
이 파트는 위 링크를 많이 참고했다 !

사용자 인증 로직

  1. 이메일로 인증 코드 발송 + 이메일, 코드, 인증 여부, 만료 시간 DB에 저장
  2. 사용자가 서비스에 인증 코드 입력
  3. 인증 코드 유효 여부 확인 및 인증

주요 코드

아래가 SES를 이용해서 메일을 발송하는 주요 코드이다.

import boto3
import botocore

def send_email(to_email, code):
    client = boto3.client('ses',
                aws_access_key_id = AWS_ACCESS_KEY_ID,	# IAM 사용자
                aws_secret_access_key = AWS_SECRET_ACCESS_KEY,
                region_name = AWS_REGION)
    sender = SES_SENDER			# 발신 메일 계정

    try:
        response = client.send_email(
            Source = sender,
            Destination={
                "ToAddresses": [
                    to_email,			# 수신 메일 계정
                ],
            },
            Message={
                "Body": {
                    "Html": {
                        "Charset": "UTF-8",
                        "Data": f"인증번호를 입력해주세요.\n인증번호: {code}",
                    },
                    "Text": {
                        "Charset": "UTF-8",
                        "Data": f"인증번호를 입력해주세요.\n인증번호: {code}",
                    },
                },
                "Subject": {
                    "Charset": "UTF-8",
                    "Data": "[FINE] 이메일 인증 코드",
                },
            },

        )
        print("success")
    except botocore.exceptions.ClientError as e:
        print(e.response["Error"]["Message"])
    else:
        print("Email sent! Message ID:"),
        print(response["MessageId"])

결과적으로 다음과 같은 이메일을 받을 수 있었다 !

boto3 SES send_email 공식 문서

이메일 템플릿

위에 Body를 보면 정말 인증코드와 텍스트 그대로 전송된다.
하지만 나름 서비스 이메일인데 그렇게 보낼 수는 없으니.. html로 이메일 템플릿을 만들어줬다.
Source Code 이 링크의 코드를 수정해서 HTML 코드를 완성했다 !

완성한 email.html을 <앱 이름>/templates/email.html에 위치시켜준다. templates 폴더를 생성해 넣지 않으면 render에서 email.html을 못 찾는다.. settings.py에서 Templates의 DIR 설정을 해주는 방법도 있지만 개인적으로 settings.py는 건들고 싶지 않았다.

from django.shortcuts import render

content = render(None, "email.html", {"code": code}).content.decode("utf-8")

위 코드로 html 파일에 인증 코드를 넣어 string으로 변환했다 !
content까지만 하면 bytes 형태로 반환되기 때문에 string 형태로 decode가 필요하다.

그 후에 위의 send_email에서 Body의 Html을 수정한다.

"Html": {
	"Charset": "UTF-8",
    "Data": content,	# 기존 텍스트 대신에 html 파일 전달
},

완성!
인증 코드 만료시간을 5분으로 설정해서 위 코드는 보여져도 상관이 없다.

Outlook

이메일 템플릿이 지메일, 네이버 메일 다 괜찮은데 Outlook에서만 CSS가 깨진다..
style을 인라인으로 작성하면 괜찮아지니 외부 CSS 내용을 전부 태그에 옮겨주자.

마무리

이렇게 SES를 써도 outlook에서는 우리가 보낸 메일을 정크메일로 인식한다..
정크메일 아니라고 리포트했더니 나한테는 잘 오는데 새로운 사용자한테는 정크메일로 갈 것 같다ㅜㅜ

글 길이에서도 보이는 것 같은데 실제 django에서 코드를 쓰는 시간보다 SES 설정하고 헤매는 시간이 훠얼씬 컸던 것 같다.
질문 받아주신 naGPT 매우매우 감사합니다🙌

profile
기록하는 감자
post-custom-banner

0개의 댓글