-
django mailgun.com 연동(interlocking)Project using python/Cloning Airbnb 2021. 3. 4. 08:36
mailgun.com
개요
mailgun.com은 메일을 보내주는 API를 제공하는 사이트다. 예를 들어 사용자가 회원가입을 했을 때, 회원 가입 알림 메일을 사용자에게 보내고, 사용자는 이를 확인한다. do-not-reply가 이에 해당한다.
START SENDING을 통해서 회원가입을 하거나 로그인을 한다. 회원가입을 하면 첫 3개월간 한 달에 5천 개의 메일이 무료다.
설정에 필요한 변수 값 얻기
우리는 django의 send_mail을 사용할 것이다. send_mail은 몇 개의 설정이 필요한데, 이 설정 값은 mailgun.com에서 얻을 수 있다.
Sending => Domain settings => SMTP credentials
여기서 필요한 설정 값은 EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD이다.
- EMAIL_HOST = "smtp.mailgun.org"
- EMAIL_PORT = 587
- EMAIL_HOST_USER= Add new SMTP user로 생성한다.
- EMAIL_HOST_PASSWORD= 계정을 생성하면 오른쪽 아래에 스크린샷과 같이 알림이 뜬다. 이것을 copy하면 password를 얻을 수 있다. Page를 나가면 password를 얻을 수 없으니 조심하자.
이 모든 값을 .env에 저장하고 settings.py에 추가한다. EMAIL_FROM은 settings.py에 정의하지 않아도 되지만 내가 임의의로 추가했다. EMAIL_FROM은 send_mail 함수를 사용할 때 필요한 인자 값이다.
Authorization
Sending => Overview에 가게 되면 오른쪽에 Email address를 기입하는 form이 있다. mailgun으로 spam mail을 보내는 경우가 있어서 이메일 인증 기능이 추가되었다. domain을 등록하기 전까지 권한을 부여한 이메일에게만 메일을 보낼 수 있다. 나는 나의 이메일을 등록해두었다. 등록을 하게 되면 등록된 이메일로 메일이 전송되는데, 이를 확인하면 권한이 부여된다.
code
Verify Email
email_verified와 email_secret이라는 두 개의 field를 추가했다. User가 회원 가입을 하면 user의 email로 email_secret 값을 담고 있는 mail을 전송한다. 사용자가 해당 mail의 링크를 클릭하면 인증을 하게 되어 로그인을 할 수 있게 된다. email_secret 값은 verify_email() method로 생성된다. Secret 값은 python의 내장 모듈인 uuid를 통해서 생성한다. 그리고 메일 전송은 django의 send_mail을 통해서 전송한다.
-send_email 인자
- 제목
- 내용
- 송신자
- 수신자
- html_message(optional) : html_message가 주어진다면 html tag가 text화 되지 않고 text/html content type이 될 것이다.
strip_tags는 html tag로 보이는 모든 것을 없애버린다. 예를 들어, value가 "<b>Joel</b> <button>is</button> a <span>slug</span>"라면 return 값은 "Joel is slug"다.
# users/models.py import uuid from django.contrib.auth.models import AbstractUser from django.core.mail import send_mail from django.db import models from django.template.loader import render_to_string from django.utils.html import strip_tags from . import managers from config import settings class User(AbstractUser): email_verified = models.BooleanField(default=False) email_secret = models.CharField(max_length=20, default="", blank=True) def verify_email(self): if self.email_verified is False: secret = uuid.uuid4().hex[:20] self.email_secret = secret html_message = render_to_string( "email/verify_email.html", {"secret": secret} ) send_mail( "Verify Hairbnb Account", strip_tags(html_message), settings.EMAIL_FROM, [self.email], html_message=html_message, ) self.save() return
email/verify_email.html은 밑에 코드와 같다. here 링크를 누르면 application이 준 secret 값과 같이 해당 url로 간다.
<h4>Verify Email</h4> <span>To verify your account click <a href="http://127.0.0.1:8000/users/verify/{{secret}}">here</a></span>
SignUpView class에 추가한다. 회원 가입이 완료되면 사용자의 email로 링크를 전송한다. 또한 success_url을 email을 확인하라는 page로 redirect한다.
회원 가입에 성공하면 redirect 되는 page다.
위 페이지에 대한 source code다.
// check_email.html {% extends 'base.html' %} {% block page_title %} Chekc your email {% endblock page_title %} {% block search-bar %} <div></div> {% endblock search-bar %} {% block content %} <div class="flex justify-center align-center"> <div> <h2 class="text-xl font-bold mb-4">Check you email</h2> <p class="text-lg">Please, go to your email and click <span>'here'</span> link to approve your account</p> </div> </div> {% endblock content %}
메일이 도착했지만, 스팸함에 있다. 그 이유는 아직 도메인을 등록하지 않았기 때문이다. mailgun.com-Authorization에서 email을 등록했지만 그래도 스팸함으로 간다.
내용은 verfiy_email.html과 같지만 'here' link가 활성화되어 있지 않다. 그 이유는 스팸함에 있기 때문에 구글에서 비활성화 처리했기 때문이다. '스팸 해제'를 눌러서 here link를 활성화 하자
받은 편지함으로 가니 here link가 활성화 된 것을 확인할 수 있다.
Complete verification
위 here의 href는 http://127.0.0.1:8000/users/verify/{{secret}}다. urls.py를 보면 밑에 code로 되어 있다.
urlpatterns=[ path("email/check/", views.check_email, name="check-email"), path( "verify/<str:key>/", views.complete_verification, name="complete-verification", ), ]
here link를 click 하면 실행되는 함수다. user를 (secret)key 값으로 찾고, user의 email을 인증한다. 그리고 secret 값을 없애고 저장한 후 login한다.
# users/views.py from .exception import LoggedOutOnlyFunctionView def complete_verification(request, key): try: if request.user.is_authenticated: raise LoggedOutOnlyFunctionView("Please verify email first") user = models.User.objects.get_or_none(email_secret=key) if user is None: messages.error(request, "User does not exist") return redirect(reverse("core:home")) user.email_verified = True user.email_secret = "" user.save() login(request, user) messages.success(request, f"{user.email} verification is completed") return redirect(reverse("core:home")) except LoggedOutOnlyFunctionView as error: messages.error(request, error) return redirect("core:home")
LoginView 부분도 user.email_verified가 True가 아니면 login을 할 수 없게 했다.
# users/views.py class LoginView(mixins.LoggedOutOnlyView, FormView): """ Login View """ form_class = forms.LoginForm success_url = reverse_lazy("core:home") template_name = "pages/users/login.html" def form_valid(self, form): email = form.cleaned_data.get("email") password = form.cleaned_data.get("password") user = authenticate(self.request, username=email, password=password) if user is not None and user.email_verified is True: messages.success(self.request, f"{user.first_name} logged in") login(self.request, user) else: return redirect(reverse("users:login")) return super().form_valid(form)
참고 자료
- 노마드 코더의 Airbnb 클론 강의
- mailgun.com
- send_mail
- strip_tags
소스 코드
github.com/zpskek/airbnb-clone-v3/commit/8df4f86d7a0dd96d241a83b8486ec7a6a54573dc
'Project using python > Cloning Airbnb' 카테고리의 다른 글
django user profile CBV (0) 2021.03.04 django user profile FBV (0) 2021.03.04 django kakao login (0) 2021.03.03 django managers.py (0) 2021.03.03 django github login (0) 2021.03.03