Chapter 8-5 Django 이메일 인증을 위한 SMTP 설정 가이드

2025. 5. 12. 20:18·Django

Django 이메일 인증을 위한 SMTP 설정 가이드

이메일 인증은 회원가입이나 비밀번호 재설정 기능에서 자주 사용되며, Django에서는 SMTP 설정을 통해 손쉽게 구현할 수 있습니다.
이번 글에서는 SMTP가 무엇인지, 어떻게 설정하는지, 네이버 메일을 기준으로 SMTP 정보를 입력하는 법까지 하나씩 정리합니다.


💡 SMTP란?

SMTP(Simple Mail Transfer Protocol)는 이메일을 전송하는 표준 통신 프로토콜입니다.

웹 애플리케이션에서 이메일을 보내려면 SMTP 서버와의 연결이 필요하며, Django에서는 이 설정을 통해 메일을 발송할 수 있습니다.


✅ SMTP 설정에 필요한 정보 정리

항목 설명

SMTP 서버 주소 이메일 제공 업체의 메일 전송 서버 주소 (예: smtp.naver.com)
SMTP 포트 번호 TLS: 587, SSL: 465
발신자 이메일 주소 메일을 보낼 계정
이메일 비밀번호 보안을 위해 앱 비밀번호 권장
TLS / SSL 사용 여부 전송 암호화 방식 설정

✅ Django의 SMTP 설정 방법

setting.py에 설정

 

import json
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

with open(BASE_DIR / '.config_secret' / 'secret.json') as f:
    config_secret_str = f.read()

SECRET = json.loads(config_secret_str)

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = SECRET['DJANGO_SECRET_KEY']

 

secret.json 에 다음과 같이 작성합니다:

{
  "DJANGO_SECRET_KEY": "django-insecure-3413@cggy0b%9ie&7g_15nnh3+4la@0xx3q!%vxuda24bs0x1a%b0y$",
  "email": {
    "user":"jangpanjangsu@naver.com",
    "password":"비밀번호"
  }
}

 

settings.py에 다음과 같이 작성합니다:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

EMAIL_HOST = 'smtp.naver.com'         # ✅ SMTP 서버명
EMAIL_PORT = 465                      # ✅ 포트 번호
EMAIL_USE_SSL = True                  # ✅ SSL 사용 (중요!)
EMAIL_USE_TLS = False                 # ❌ TLS는 사용하지 않음

EMAIL_HOST_USER = SECRET["email"]["user"]
EMAIL_HOST_PASSWORD = SECRET["email"]["password"]  # 앱 비밀번호 사용 권장

DEFAULT_FROM_EMAIL = 'jangpanjangsu@naver.com'

 

 


 터미널에서 발송체크

python manage.py shell

from django.core.mail import send_mail
from django.conf import settings

title = '제목입니다.'
message = '이것은 django에서 보낸 메시지입니다.'
from_email = settings.EMAIL_HOST_USER
to_email = ['받는대상']

send_mail(title, message, from_email, to_email)
# views.py

class SignupView(FormView):
    template_name = 'auth/signup.html'
    form_class = SignupForm

    def form_valid(self, form):
        user = form.save()
        # 이메일 발송
        signer = TimestampSigner()
        signed_user_email = signer.sign(user.email)
        signer_dump = signing.dumps(signed_user_email)
        # print(signer_dump)
        #
        # decoded_user_email = signing.loads(signer_dump)
        # print(decoded_user_email)
        # email = signer.unsign(decoded_user_email, max_age=60 * 30)
        # print(email)

        url = f"{self.request.scheme}://{self.request.META["HTTP_HOST"]}/verify/?code={signer_dump}"
        print(url)

        return render(self.request, 'auth/signup_done.html', {'user': user})

 

📌 1. 클래스 개요

class SignupView(FormView):
  • FormView: Django CBV(Class-Based View) 중 하나
  • 회원가입 폼 처리 전담 (GET 요청: 폼 렌더링 / POST 요청: 폼 유효성 검사 수행)

📌 2. 주요 속성 설정

template_name = 'auth/signup.html'
form_class = SignupForm
  • template_name: 회원가입 화면으로 렌더링할 템플릿 경로
  • form_class: 사용자 입력을 받을 폼 클래스 지정

📌 3. form_valid 메서드 분석 (핵심 로직)

def form_valid(self, form):
  • 폼 데이터가 유효할 경우 자동 실행되는 메서드
  • 회원가입 이후 이메일 인증 URL 생성 로직 포함

✅ 코드 흐름별 분석

① 사용자 저장

user = form.save()
  • DB에 새 사용자(User 객체)를 저장

② 이메일 서명 처리

signer = TimestampSigner()
signed_user_email = signer.sign(user.email)
  • TimestampSigner: 시간정보 포함 서명 생성기 (위변조 방지)
  • 사용자의 이메일에 서명 부여

③ URL 전달용 문자열 직렬화

signer_dump = signing.dumps(signed_user_email)
  • signing.dumps: 파이썬 객체를 안전한 문자열로 변환 (URL에 포함 가능)

④ 서명 복호화 및 만료 검증 (테스트 목적)

decoded_user_email = signing.loads(signer_dump)
email = signer.unsign(decoded_user_email, max_age=60 * 30)
  • loads: 문자열을 역직렬화하여 객체로 복원
  • unsign: 원래 이메일 추출 (30분 제한 설정)

⑤ 인증 URL 생성

url = f"{self.request.scheme}://{self.request.META['HTTP_HOST']}/verify/?code={signer_dump}"
  • 완성된 URL 예시: http://127.0.0.1:8000/verify/?code=...
  • 이 URL을 이메일에 포함시켜 사용자에게 발송함

⑥ 완료 페이지 반환

return render(self.request, 'auth/signup_done.html', {'user': user})
  • 회원가입 완료 템플릿 반환 (signup_done.html)

views.py verify_email 함수 생성 및 최종수정

from django.contrib.auth import get_user_model
from django.core import signing
from django.core.signing import TimestampSigner, SignatureExpired
from django.shortcuts import render, get_object_or_404
from django.views.generic import FormView
from django.urls import reverse_lazy
from django.shortcuts import render

from member.forms import SignupForm
from utils.email import send_email

User = get_user_model()

class SignupView(FormView):
    template_name = 'auth/signup.html'
    form_class = SignupForm

    def form_valid(self, form):
        user = form.save()
        # 이메일 발송
        signer = TimestampSigner()
        signed_user_email = signer.sign(user.email)
        signer_dump = signing.dumps(signed_user_email)
        # print(signer_dump)
        #
        # decoded_user_email = signing.loads(signer_dump)
        # print(decoded_user_email)
        # email = signer.unsign(decoded_user_email, max_age=60 * 30)
        # print(email)

        url = f"{self.request.scheme}://{self.request.META["HTTP_HOST"]}/verify/?code={signer_dump}"

        subject = '[Pystagram] 이메일 인증을 완료해주세요'
        message = f'다음 링크를 클릭해주세요. <br><a href="{url}">url</a>'

        send_email(subject, message, user.email)

        return render(self.request, 'auth/signup_done.html', {'user': user})


def verify_email(request):
    code = request.GET.get('code', '')

    signer = TimestampSigner()
    try:
        decoded_user_email = signing.loads(code)
        email = signer.unsign(decoded_user_email, max_age=60 * 30)
    except (TypeError, SignatureExpired):
        return render(request, template_name='auth/not_verified.html')

    user = get_object_or_404(User, email=email, is_active=False)
    user.is_active = True
    user.save()

    # return redirect(reverse('login'))
    return render(request, template_name='auth/email_verified_done.html', context={'user': user})

 

email.py 에 메일보내는 기능 작성

from typing import Union

from django.core.mail import send_mail
from django.conf import settings


def send_email(subject, message, to_email):
    # to_email이 리스트인지 확인하여, 아니면 리스트로 감쌈
    to_email = to_email if isinstance(to_email, list) else [to_email, ]

    # 아래는 같은 로직의 주석 처리된 버전 (이전 방식)
    # if isinstance(to_email, list):
    #     to_email = to_email
    # else:
    #     to_email = [to_email, ]

    # 이메일 발송 함수 호출

    send_mail(subject, message, settings.EMAIL_HOST_USER, to_email)

 

not_verfied.html / verified_done.html 작성

# not_verified.html
{% extends 'base.html' %}

{% block content %}
    <div>
        <p>
            이메일이 제대로 인증 되지 않았습니다. 다시 시도해주세요.
        </p>
    </div>

{% endblock %}

# email_verified_done.html

{% extends 'base.html' %}


{% block content %}
    <div>
        <p>
            이메일이 인증이 완료되었습니다.
        </p>
    </div>

{% endblock %}

 

urls.py 작성

 

from django.contrib import admin
from django.urls import path
from member import views as member_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('signup/', member_views.SignupView.as_view(), name='signup'),
    path('verify/', member_views.verify_email, name='verify_email'),
]

💡 실전 팁 & 주의사항

항목 설명

🔐 이메일 서명 TimestampSigner를 통해 위변조 방지 가능
⏰ 만료 시간 max_age=1800 (30분 제한)으로 보안 강화
📧 이메일 발송 현재는 print()로 URL 확인 → 실제 사용 시 send_mail() 활용 필요
❌ 예외 처리 SignatureExpired 발생 가능 → try-except 문으로 예외 처리 필요

🎯 요약

  • 사용자의 이메일에 시간 기반 서명을 추가하여 위변조 방지
  • 직렬화한 서명을 포함한 인증 URL 생성
  • 인증 URL은 30분간 유효하며, 해당 링크로 본인 인증 가능
  • 본 기능은 실전 배포 시 이메일 전송 로직(send_mail)과 예외 처리 코드가 반드시 필요함

 

⚠️ 보안 주의사항

  • 네이버 계정에서 2단계 인증이 설정된 경우 일반 비밀번호가 아닌 앱 비밀번호를 발급받아 사용해야 합니다.
  • .env 또는 config_secret.json 등 환경변수 파일에 저장하고 코드에 직접 노출되지 않도록 관리합니다.

✅ 네이버 SMTP 사용 시 필수 설정 사항

  1. IMAP/SMTP 사용 설정
    • [네이버 메일 > 환경설정 > POP3/IMAP 설정] 진입
    • "IMAP/SMTP 사용함" 으로 설정
  2. 모바일/메일 앱 등록 방법
    • 안드로이드: 메일 앱에서 네이버 선택 후 등록
    • iOS: 설정 > Mail > 계정 추가 > 기타 > 이메일 입력
  3. IMAP/SMTP 서버 정보 요약

구분 서버 주소 포트 보안

IMAP imap.naver.com 993 SSL
SMTP smtp.naver.com 587 TLS (또는 SSL)
  1. 사용 중단 시 방법
    • 메일 환경설정에서 "IMAP/SMTP 사용안함"으로 변경 가능

🔐 환경변수로 SMTP 정보 보호하기

# .env
EMAIL_HOST_USER=your_email@naver.com
EMAIL_HOST_PASSWORD=your_app_password
# settings.py
import os
from dotenv import load_dotenv
load_dotenv()

EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD')

✅ 반드시 .env 파일은 .gitignore에 추가하여 Git에 푸시되지 않도록 해야 합니다.


📌 마무리 체크리스트

  • SMTP 서버 정보 입력 완료 (EMAIL_HOST, PORT 등)
  • 발신자 계정은 앱 비밀번호 사용 권장
  • 네이버 IMAP/SMTP 사용 설정 "사용함"으로 변경
  • 환경변수 또는 JSON 파일로 보안 정보 관리

다음 글에서는 이 SMTP 설정을 기반으로 실제 이메일 인증 링크를 생성하고 인증 로직을 처리하는 방법을 알아보겠습니다! ✉️

'Django' 카테고리의 다른 글

🔍 Django에서 User.objects.model의 정체는?  (0) 2025.05.12
Chapter 8-6 Mini Project: Django Login & Logout 기능 만들기  (0) 2025.05.12
Chapter 8-4 환경변수 관리와 python-dotenv 사용법  (0) 2025.05.12
Chapter 8-3 Django 회원가입 페이지 만들기 (정적 파일 + 폼 커스텀 + 뷰 구현)  (0) 2025.05.12
Chapter 8-2 Django에서 커스텀 유저 모델 만들기 (AbstractBaseUser 활용)  (0) 2025.05.12
'Django' 카테고리의 다른 글
  • 🔍 Django에서 User.objects.model의 정체는?
  • Chapter 8-6 Mini Project: Django Login & Logout 기능 만들기
  • Chapter 8-4 환경변수 관리와 python-dotenv 사용법
  • Chapter 8-3 Django 회원가입 페이지 만들기 (정적 파일 + 폼 커스텀 + 뷰 구현)
Chansman
Chansman
안녕하세요! 코딩을 시작한 지 얼마 되지 않은 초보 개발자 찬스맨입니다. 이 블로그는 제 학습 기록을 남기고, 다양한 코딩 실습을 통해 성장하는 과정을 공유하려고 합니다. 초보자의 눈높이에 맞춘 실습과 팁, 그리고 개발하면서 겪은 어려움과 해결 과정을 솔직하게 풀어내려 합니다. 함께 성장하는 개발자 커뮤니티가 되기를 바랍니다.
  • Chansman
    찬스맨의 프로그래밍 스토리
    Chansman
  • 전체
    오늘
    어제
    • 분류 전체보기 (798)
      • Python (32)
      • 프로젝트 (113)
      • 과제 (25)
      • Database (40)
      • 멘토링 (11)
      • 특강 (37)
      • 기술블로그 (41)
      • 기술블로그-Fastapi편 (33)
      • 기술블로그-Django편 (154)
      • 기술블로그-Flask편 (36)
      • AI 분석 (5)
      • HTML & CSS (31)
      • JavaScript (17)
      • AWS_Cloud (21)
      • 웹스크래핑과 데이터 수집 (14)
      • Flask (42)
      • Django (77)
      • Fastapi (16)
      • 연예 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    life reflection
    chatgpterror
    remittance
    뷔
    travel ban
    gpterror
    btscomeback
    americaparty
    lawsuitculture
    btsreunion
    livebroadcast
    global politics
    americanlaw
    chinanightlife
    classaction
    smartphonedurability
    bts
    btsdischarge
    hotcoffeecase
    newpoliticalparty
    titaniumcase
    homebartrend
    RM
    btsjungkook
    urbantrends
    college reunions
    youngprofessionals
    basalcellcarcinoma
    self-growth
    trumpmuskclash
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Chansman
Chapter 8-5 Django 이메일 인증을 위한 SMTP 설정 가이드
상단으로

티스토리툴바