같은 이메일, 매번 손으로 보내고 있다면.
내용은 거의 같은데 받는 사람만 다른 이메일. 보낼 때마다 이름 바꾸고, 첨부파일 붙이고, 주소 복사하는 과정을 반복하고 있다면 지금 당장 자동화할 수 있습니다.
이 글에서는 파이썬 기본 내장 라이브러리 smtplib를 사용해 이메일을 자동으로 발송하는 방법을 다룹니다. 단순 텍스트 발송부터 HTML 본문, 파일 첨부, 여러 명에게 한 번에 보내기까지 단계별로 설명합니다.
사전 준비
필요한 라이브러리
이 글에서 사용하는 라이브러리는 모두 파이썬 기본 내장 라이브러리입니다. 별도 설치가 필요 없습니다.
- smtplib: SMTP 프로토콜을 통해 이메일을 발송하는 핵심 모듈입니다.
- email: 이메일 본문, 첨부파일, 인코딩 등을 구성하는 모듈입니다.
Gmail 앱 비밀번호 발급 (필수)
Gmail은 보안 정책상 계정 비밀번호를 그대로 코드에 쓸 수 없습니다. 반드시 앱 비밀번호를 별도로 발급받아야 합니다.
- Google 계정 보안 페이지에서 2단계 인증을 먼저 활성화합니다.
- 같은 페이지에서 앱 비밀번호를 검색해 새 앱 비밀번호를 생성합니다.
- 생성된 16자리 비밀번호를 코드에서 사용합니다. (계정 비밀번호와 다릅니다)
Naver, Daum 등 다른 메일 서비스도 동일한 방식으로 SMTP 설정이 가능합니다. 각 서비스별 SMTP 서버 정보는 아래와 같습니다.
# Gmail
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
# Naver
SMTP_SERVER = "smtp.naver.com"
SMTP_PORT = 587
# Daum
SMTP_SERVER = "smtp.daum.net"
SMTP_PORT = 465
Case 1. 기본 텍스트 이메일 발송하기
가장 기본적인 형태입니다. 제목과 본문 텍스트를 지정해 단일 수신자에게 발송합니다.
# send_text.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# 발신자 정보
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SENDER = "내_이메일@gmail.com"
APP_PASSWORD = "앱_비밀번호_16자리" # 계정 비밀번호 아님!
# 이메일 내용 구성
msg = MIMEMultipart()
msg["From"] = SENDER
msg["To"] = "받는사람@example.com"
msg["Subject"] = "안녕하세요, 파이썬 자동 발송 테스트입니다"
body = """안녕하세요.
파이썬으로 자동 발송된 이메일입니다.
내용을 이곳에 작성하시면 됩니다.
감사합니다."""
msg.attach(MIMEText(body, "plain", "utf-8"))
# 발송
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls() # 암호화 연결 시작
server.login(SENDER, APP_PASSWORD) # 로그인
server.send_message(msg) # 발송
print("이메일 발송 완료!")
Case 2. HTML 본문으로 꾸며서 발송하기
일반 텍스트 대신 HTML을 사용하면 굵은 글씨, 색상, 링크, 표 등을 포함한 서식 있는 이메일을 보낼 수 있습니다. HTML을 지원하지 않는 수신자를 위해 텍스트 버전도 함께 첨부하는 것이 좋습니다.
# send_html.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SENDER = "내_이메일@gmail.com"
APP_PASSWORD = "앱_비밀번호_16자리"
msg = MIMEMultipart("alternative") # 텍스트/HTML 병행
msg["From"] = SENDER
msg["To"] = "받는사람@example.com"
msg["Subject"] = "파이썬 HTML 이메일 테스트"
# 텍스트 버전 (HTML 미지원 환경 대비)
text_body = "안녕하세요.\n파이썬으로 발송된 이메일입니다."
# HTML 버전
html_body = """
안녕하세요!파이썬으로 발송된 HTML 이메일입니다.자동화로 만든 이메일도 이렇게 깔끔하게 보낼 수 있습니다.감사합니다.
"""
msg.attach(MIMEText(text_body, "plain", "utf-8"))
msg.attach(MIMEText(html_body, "html", "utf-8"))
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls()
server.login(SENDER, APP_PASSWORD)
server.send_message(msg)
print("HTML 이메일 발송 완료!")
Case 3. 파일 첨부해서 발송하기
PDF, 엑셀, 이미지 등 파일을 첨부해서 보내는 방법입니다. 파일은 여러 개 첨부할 수 있습니다.
# send_with_attachment.py
import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SENDER = "내_이메일@gmail.com"
APP_PASSWORD = "앱_비밀번호_16자리"
def attach_file(msg: MIMEMultipart, file_path: str):
"""파일을 이메일에 첨부하는 함수"""
filename = os.path.basename(file_path)
with open(file_path, "rb") as f:
part = MIMEBase("application", "octet-stream")
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename=\"{filename}\""
)
msg.attach(part)
print(f" 첨부 완료: {filename}")
msg = MIMEMultipart()
msg["From"] = SENDER
msg["To"] = "받는사람@example.com"
msg["Subject"] = "파일 첨부 이메일 테스트"
body = "안녕하세요.\n파일을 첨부해서 보냅니다."
msg.attach(MIMEText(body, "plain", "utf-8"))
# 첨부할 파일 목록 (경로 지정)
files_to_attach = [
"report.pdf",
"data.xlsx",
]
for file_path in files_to_attach:
if os.path.exists(file_path):
attach_file(msg, file_path)
else:
print(f" [건너뜀] 파일 없음: {file_path}")
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls()
server.login(SENDER, APP_PASSWORD)
server.send_message(msg)
print("\n첨부 이메일 발송 완료!")
Case 4. 여러 명에게 개인화해서 발송하기
수신자 목록을 CSV로 관리하고, 이름 등 개인 정보를 본문에 자동으로 삽입해서 각각 따로 발송하는 방식입니다. 단체 발송처럼 보이지 않고 개별 발송처럼 보이는 것이 핵심입니다.
# recipients.csv 예시
# 이름,이메일
# 홍길동,hong@example.com
# 김철수,kim@example.com
# send_bulk.py
import smtplib
import csv
import time
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SENDER = "내_이메일@gmail.com"
APP_PASSWORD = "앱_비밀번호_16자리"
# 이메일 본문 템플릿 ({이름} 자리에 수신자 이름이 자동 삽입됨)
SUBJECT = "안녕하세요, {이름}님"
TEMPLATE = """\
안녕하세요, {이름}님.
보내드린 내용을 확인해 주시기 바랍니다.
감사합니다."""
def send_one(server, recipient_name: str, recipient_email: str):
msg = MIMEMultipart()
msg["From"] = SENDER
msg["To"] = recipient_email
msg["Subject"] = SUBJECT.format(이름=recipient_name)
body = TEMPLATE.format(이름=recipient_name)
msg.attach(MIMEText(body, "plain", "utf-8"))
server.send_message(msg)
# CSV 파일에서 수신자 목록 읽어 순서대로 발송
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls()
server.login(SENDER, APP_PASSWORD)
with open("recipients.csv", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
name = row["이름"]
email = row["이메일"]
try:
send_one(server, name, email)
print(f" 발송 완료: {name} <{email}>")
time.sleep(1) # 연속 발송 시 서버 차단 방지
except Exception as e:
print(f" [실패] {name} <{email}> → {e}")
print("\n전체 발송 완료!")
실전에서 바로 쓰는 완성 코드
위 내용을 모두 담아 실전에서 바로 사용할 수 있는 완성본입니다. HTML 본문, 파일 첨부, 개인화 발송을 모두 지원합니다.
실행 예시
# 단일 발송
python send.py --to "받는사람@example.com" --subject "제목" --body "본문"
# CSV 일괄 발송 + 파일 첨부
python send.py --csv recipients.csv --subject "안녕하세요 {이름}님" --attach report.pdf
# send.py
import smtplib
import csv
import os
import time
import argparse
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SENDER = "내_이메일@gmail.com"
APP_PASSWORD = "앱_비밀번호_16자리"
def attach_file(msg: MIMEMultipart, file_path: str):
if not os.path.exists(file_path):
print(f" [건너뜀] 첨부파일 없음: {file_path}")
return
filename = os.path.basename(file_path)
with open(file_path, "rb") as f:
part = MIMEBase("application", "octet-stream")
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", f"attachment; filename=\"{filename}\"")
msg.attach(part)
def build_message(to: str, subject: str, body: str, attach: list) -> MIMEMultipart:
msg = MIMEMultipart()
msg["From"] = SENDER
msg["To"] = to
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain", "utf-8"))
for file_path in attach:
attach_file(msg, file_path)
return msg
def get_args():
p = argparse.ArgumentParser(description="파이썬 이메일 자동 발송")
p.add_argument("--to", default=None, help="단일 수신자 이메일 주소")
p.add_argument("--csv", default=None, help="수신자 CSV 파일 경로 (이름, 이메일 컬럼)")
p.add_argument("--subject", required=True, help="이메일 제목 ({이름} 사용 가능)")
p.add_argument("--body", default="안녕하세요, {이름}님.\n\n내용을 입력하세요.", help="본문 ({이름} 사용 가능)")
p.add_argument("--attach", nargs="*", default=[], help="첨부파일 경로 (여러 개 가능)")
return p.parse_args()
def main():
args = get_args()
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls()
server.login(SENDER, APP_PASSWORD)
# 단일 발송
if args.to:
msg = build_message(
to = args.to,
subject = args.subject.format(이름=""),
body = args.body.format(이름=""),
attach = args.attach,
)
server.send_message(msg)
print(f"발송 완료: {args.to}")
# CSV 일괄 발송
elif args.csv:
with open(args.csv, encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
name = row.get("이름", "")
email = row.get("이메일", "")
if not email:
continue
try:
msg = build_message(
to = email,
subject = args.subject.format(이름=name),
body = args.body.format(이름=name),
attach = args.attach,
)
server.send_message(msg)
print(f" 발송 완료: {name} <{email}>")
time.sleep(1)
except Exception as e:
print(f" [실패] {name} <{email}> → {e}")
else:
print("[오류] --to 또는 --csv 중 하나를 지정하세요.")
if __name__ == "__main__":
main()
코드 핵심 옵션 설명
- --to: 단일 수신자에게 발송할 때 사용합니다.
- --csv: 수신자 목록이 담긴 CSV 파일 경로를 지정합니다.
이름,이메일컬럼이 필요합니다. - --subject: 이메일 제목입니다.
{이름}을 넣으면 수신자 이름이 자동 삽입됩니다. - --body: 이메일 본문입니다. 마찬가지로
{이름}삽입이 가능합니다. - --attach: 첨부파일 경로를 지정합니다. 여러 개는 띄어쓰기로 구분합니다. 예:
--attach a.pdf b.xlsx - time.sleep(1): 연속 발송 시 Gmail 서버의 속도 제한에 걸리지 않도록 1초 간격을 둡니다.
자주 발생하는 문제와 해결법
문제 1. SMTPAuthenticationError — 로그인 실패
가장 흔한 오류입니다. 계정 비밀번호를 그대로 사용했거나, 앱 비밀번호 발급 없이 시도했을 때 발생합니다. 반드시 앱 비밀번호를 별도로 발급받아 사용해야 합니다.
# 잘못된 예 — 계정 비밀번호 직접 사용 (작동 안 함)
server.login("내_이메일@gmail.com", "내_계정_비밀번호")
# 올바른 예 — 앱 비밀번호 사용
server.login("내_이메일@gmail.com", "xxxx xxxx xxxx xxxx") # 16자리 앱 비밀번호
문제 2. 비밀번호를 코드에 직접 쓰기 불안할 때
코드에 비밀번호를 직접 쓰면 실수로 공유될 위험이 있습니다. 환경 변수로 분리해서 관리하는 것이 안전합니다.
<code"># .env 파일 (절대 GitHub 등에 올리지 마세요)
GMAIL_SENDER=내_이메일@gmail.com
GMAIL_APP_PASSWORD=앱_비밀번호_16자리
pip install python-dotenv
import os
from dotenv import load_dotenv
load_dotenv() # .env 파일 로드
SENDER = os.getenv("GMAIL_SENDER")
APP_PASSWORD = os.getenv("GMAIL_APP_PASSWORD")
문제 3. 한글 제목이 깨져서 보일 때
제목에 한글이 포함될 때 일부 메일 클라이언트에서 깨져 보이는 경우가 있습니다. Header를 사용해 인코딩을 명시합니다.
from email.header import Header
msg["Subject"] = Header("한글 제목입니다", "utf-8")
문제 4. 대량 발송 시 Gmail 한도 초과
Gmail 무료 계정은 하루 발송 한도가 약 500건입니다. 이를 초과하면 일시적으로 발송이 차단됩니다. 대량 발송이 필요하다면 Google Workspace 계정을 사용하거나, SendGrid, Mailgun 같은 이메일 발송 전용 서비스를 고려하세요.
마치며..
오늘은 파이썬으로 이메일을 자동 발송하는 방법을 단계별로 살펴보았습니다. 반복 작업을 자동화하는 가장 빠른 방법은, 이미 만들어진 도구를 제대로 쓰는 것입니다. smtplib는 설치도 필요 없고 코드도 짧습니다. 오늘 30분만 투자해 만들어두면, 앞으로 같은 이메일을 손으로 보내는 일은 없을 거예요.
발송할 데이터를 엑셀에서 가져와야 한다면, 이전 글 파이썬으로 엑셀 파일 100개 한번에 합치기를 함께 참고해 보세요. 두 가지를 조합하면 데이터 수집부터 이메일 발송까지 완전한 자동화 파이프라인을 만들 수 있습니다.
관련 내용
본 포스팅의 코드는 자유롭게 수정 및 배포가 가능하나, 상업적 이용 시에는 출처를 반드시 밝혀주시기 바랍니다.