본문 바로가기
개발/django

점프 투 장고 추가 기능(이메일 인증)

by wafla 2024. 11. 20.

이메일 인증 기능 추가

파이보 추가 기능 항목에는 없지만 필요한 기능인 것 같아 추가했습니다. 

SMTP 설정

먼저 이메일을 보내기 위해 SMTP 설정을 해줍니다. 저는 네이버를 사용했습니다.

(SMTP란? Simple Mail Transfer Protocol의 약자로 이메일을 송신하는 데 사용되는 인터넷 표준 프로토콜입니다)

네이버 메일에 접속한 후 환경설정 - IMAP/SMTP 설정에서 사용함을 체크하고 저장하면 됩니다.

 

config/settings/base.py에서 SMTP를 사용할 수 있도록 설정해줍니다.

EMAIL_HOST_USER에 계정 정보에 적힌 아이디를, PASSWORD에는 네이버 비밀번호를 적으면 됩니다.

보안에 유의하세요!

# SMTP 설정
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.naver.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = '아이디'
EMAIL_HOST_PASSWORD = '비밀번호'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

 

인증 모델 생성

개인 정보를 채우고 회원가입 버튼을 눌렀을 때 인증번호를 생성해서 이메일로 발송하고 입력받은 인증번호가 일치하는지 확인해야 합니다. 이를 위해서 common/models.py에 다음과 같이 모델을 생성해줍니다.

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
import uuid

class EmailVerification(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    verification_code = models.UUIDField(default=uuid.uuid4, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def is_valid(self):
        return timezone.now() < self.created_at + timezone.timedelta(minutes=10)

 

uuid를 통해 인증번호를 생성하고 이를 유저에게 값을 할당해줍니다.

is_valid는 10분 이내에 인증이 됐는지를 확인하는 함수입니다.

 

python manage.py makemigrations와 python manage.py migrate로 DB에 적용해줍니다.

만약 오타 등으로 수정한 뒤 다시 적용 시키려면 common/migrations 폴더에 해당되는 파일을 제거하고 다시 migrate해주면 됩니다.(예: 0001_EmailVerification.py)

 

코드 작성

이제 인증 기능을 구현해봅시다. 우선, signup 함수를 다음과 같이 수정합니다.

 

common/views.py

from .models import EmailVerification
def signup(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():
            user = form.save(commit=False)
            user.is_active = False # 이메일 인증 전까지 비활성화
            user.save()

            # 이메일 인증 객체 생성 및 저장
            verification = EmailVerification.objects.create(user=user)
            verification_code = verification.verification_code

            # 이메일 발송
            send_verification_email(user, verification_code)
            return redirect('common:verify_email') # 인증 코드 입력 페이지로 이동
    else:
        form = UserForm()
    return render(request, 'common/signup.html', {'form': form})

 

인증 객체를 생성하고 인증 코드를 이메일 발송 함수에 넘겨줍니다. 이후 인증 코드를 입력하는 창으로 넘어갑니다.

 

이메일 발송 함수는 다음과 같습니다.

from django.core.mail import EmailMessage
def send_verification_email(user, verification_code):
    subject = '이메일 인증'
    message = f'인증 코드는 {verification_code} 입니다'
    from_email = '네이버아이디@naver.com'
    to_email = [user.email]

    email = EmailMessage(subject, message, from_email, to_email)
    email.send(fail_silently=False)

 

제목, 내용, 발신자, 수신자로 내용을 채워줍니다.

 

다음은 인증 코드를 입력받고 확인하는 코드입니다.

def verify_email(request):
    if request.method == "POST":
        # 사용자로부터 POST로 인증 코드 받기
        code = request.POST.get('verification_code')
        code = code.strip() # 공백 제거
        try:
            verification = EmailVerification.objects.get(verification_code=code)
            if verification.is_valid():
                user = verification.user
                user.is_active = True  # 계정 활성화
                user.save()
                verification.delete()
                login(request, user)
                return redirect('index')
            else:
                return render(request, 'common/verify_email.html', {'error': '인증 코드가 만료되었습니다.'})
        except EmailVerification.DoesNotExist:
            return render(request, 'common/verify_email.html', {'error': '잘못된 인증 코드입니다.'})

    # GET 요청 시 인증 코드 입력 폼 페이지를 렌더링
    return render(request, 'common/verify_email.html')

 

확인이 완료되면 user.is_active가 True로 바뀌며 계정이 활성화 됩니다. 활성화된 아이디의 인증 객체는 삭제합니다.

 

common/urls.py에 path도 추가해줍니다.

path('verify_email/', views.verify_email, name='verify_email'),

 

마지막으로 인증코드를 입력하는 화면입니다.

 

templates/common/verify_email.html

{% extends "base.html" %}
{% block content %}
<div class="container my-3">
    <h3>이메일 인증</h3>
    {% if error %}
        <div class="alert alert-danger">{{ error }}</div>
    {% endif %}
    <form method="post">
        {% csrf_token %}
        <div class="mb-3">
            <label for="verification_code">인증 코드</label>
            <input type="text" class="form-control" name="verification_code" id="verification_code" required>
        </div>
        <button type="submit" class="btn btn-primary">인증하기</button>
    </form>
</div>
{% endblock %}

 

이렇게 이메일 인증 기능이 구현되었습니다.

기능 구현 결과 화면입니다.