Python SMTP + Application

소스코드





SMTP_SERVER = ''
SMTP_ID = ''
SMTP_PW = ''
SMTP_SSL = True
SMTP_PORT = 587
FROM_NAME = ""
FROM_EMAIL = ""
TO_EMAIL = ""


import smtplib
import datetime
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart


def set_smtp_session():
    try:
        # SMTP session
        smtp = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
        smtp.set_debuglevel(True)

        # SMTP authentication
        smtp.ehlo()
        smtp.starttls()  # TLS 사용시 호출
        smtp.login(SMTP_ID, SMTP_PW)  # 로그인
    except Exception as e:
        print(f"smtp session error : {e}")

    return smtp


def send_email(_session, sender, receiver):
    try:
        msg = MIMEText("test code")
        msg['Subject'] = "Verification email"
        msg['From'] = sender
        msg['To'] = receiver

        _session.sendmail(sender, receiver, msg.as_string())
    except Exception as e:
        print(f"email send error : {e}")

    return 0


smtp_start_time = datetime.datetime.now()

smtp_session = set_smtp_session()
send_email(_session=smtp_session, sender=FROM_EMAIL, receiver=TO_EMAIL)

smtp_end_time = datetime.datetime.now()

print(f"smtp turn around time : {smtp_end_time - smtp_start_time}")


Turnaround time

이메일 전송-완료 시간을 측정하기 위해
프레임워크에서가 아닌 파이썬 라이브러리 smtp를 활용하여
turn around time을 측정하였다.

backend로는 gmail 서버를 사용했다.

이메일의 내용물은 html, image, css 등의 에셋없이
텍스트뿐인 제목과 내용으로 구성되어있다.

약 7초의 시간이 걸렸다.
프로덕션에서는 stmp backend로 지메일이 아니라
전용 서버를 구축하거나 서비스를 이용하는 것이 권장된다.

https://salmon-mail.readthedocs.io/en/latest/
salmon이라는 파이썬 기반 메일 서버가 있다.
프로덕션에서 쓰는 회사는 아직 보질 못했는데
파이썬 기반 메일 서버라니 흥미롭다.


SMTP Usecase

Use case 1 : 사용자 가입 절차 시 입력된 이메일 확인용으로 인증코드를 보내는 로직
Use case 2 : 광고 발송 시점에 사용자들에게 메일을 발송 (뉴스레터)
Use case 3 : 비밀번호 변경 기능 


Production Django 애플리케이션에서의 SMTP

django 프로젝트의 View 코드에 이를 추가하게된다면
보통 wsgi서버의 동기식 코드 베이스 위에 추가하게 된다는 것을 뜻한다.

SMTP 기능을 프로덕션에 올릴 때에는 
다음과 같은 사항을 추가로 고려할 것이다.

django send_mass_mail

뉴스레터와 같은 대량의 메일을 보낼 때에는
from django.core.mail import send_mass_mail
을 이용하는 것이 하나씩 보내는 것보다 효율적이라고 알려져있다.

send_email은 메시지당 connection 1개,
send_mass_email은 대량의 메시지를 connection 1개로 보내는 차이가 있다.  

메시지큐 도입

django project를 운영한다면 celery 및 broker 서비스를 이용해서
분산 처리 및 비동기 처리를 도입할 수 있다.

기능면에서 connection을 생성하고 메일 데이터를 보내는 역할은
Threading, Multiprocessing을 사용해서도 가능하지만

celery와 broker 서비스를 이용하는 것이 
장애 대응과 분산처리에 더 적합하다. 


Throttling

애플리케이션 단에서 쓰로틀링 limit을 걸어서
단위시간당 호출할 수 있는 개수를 정해놓는다.

이메일 인증 재요청을 계속해서 클릭하는 등의
비정상적인 요청을 차단할 수 있다.

애플리케이션 서비스의 부하 방지를 위해
네트워크 계층에서의 차단을 고려해볼 수도 있다.


고성능 SMTP Service 이용

여유가 있다면 mailgun, aws ses와 같은 클라우드 제품을 이용한다.

사용자가 몰릴 경우 이메일 처리 작업들과
그 외 request들 처리로 인해 부하가 걸릴 것이다.

SMTP Session으로 인해 메일 트랜잭션이 수행되기까지 대기하기 때문이다.

Network i/o를 기다리면서(성공 response) 다른 작업을 하지 않고 있을 때
어러 requst가 들어오게되면 작업들이 대기 상태로 인해 
응답률이 떨어진다.

애플리케이션 서버에서 발생하는 부하의 분산을 위해
celery와 같은 메시지큐 시스템을 이용하는 것이 좋다.  




SMTP RFC

생각보다 길지 않다.
django.core.mail 코드와 비교해보며 읽으면 흥미로울 것 같다.
https://datatracker.ietf.org/doc/html/rfc5321#section-2.3.10


댓글

이 블로그의 인기 게시물

실무진 면접 경험으로 정리하는 백엔드 (1) : 에듀 테크 기업 면접

노마드코더 개발자북클럽 Clean code 완주, 독후감

Blogger 커스터마이징 : CSS 수정 (sticky-header)