ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django] Django 연습장 - 6. 회원가입 프로세스 개선 (이메일 보내기 & 인증)
    프로그래밍/Django 2022. 4. 16. 01:48
    반응형

    이번 시간에는 회원가입에 대해서 프로세스를 추가하도록 하겠습니다.

     

    현재 구현해놓은 내용은 이메일을 기반으로 회원가입을 진행합니다.

    하지만 지금처럼 가입을 시키면 아무 메일이나, 또는 남의 메일을 입력해서 회원가입을 해버릴 수 있습니다.

     

    따라서 해당 메일이 정말 사용자의 것인지 확인하는 프로세스를 추가하겠습니다.

     

    [작업순서]

    1. 회원가입 코드에 메일 전송 코드 추가 & 생성 시 user의 is_active값을 False로 저장할 것임,

    2. 메일 전송 코드 작성

    3. 메일 전송 내용 중 토큰 생성 함수 작성.

    4. 메일 전송에 필요한 html 작성

    5. 이메일 인증을 위한 엔드포인트 만들기 (user activate)

    6. django smtp서버 정보 연결 (Gmail)

    7. 회원가입 해보기

     

     

    1. 회원가입 코드에 메일 전송 코드 추가

    form_valid함수에 메일을 보내는 것을 추가했습니다.

    그럼 task_send_register_mail은 어디 있는 함수냐?

    app_accounts > task.py라는 파일을 만들고 함수를 생성했습니다.

    그리고 app_accounts > forms.py에 UserForm의 save 함수도 지워줍니다. (view단에서 user 생성할 거임)

    from django.shortcuts import render, redirect, HttpResponse, get_object_or_404
    from django.contrib.sites.shortcuts import get_current_site
    from django.contrib.auth.hashers import check_password, make_password
    from django.http import JsonResponse
    from django.views.generic.edit import FormView,View
    from django.views.generic import CreateView
    from django.contrib.auth import authenticate, login, logout
    from django.urls import reverse_lazy
    from django.contrib.sites.shortcuts import get_current_site
    
    from app_accounts.forms import UserForm, LoginForm
    from app_accounts.task import task_send_register_mail
    from app_accounts.models import User
    
    ....
    
    
    # 회원가입
    class CreateAccounts(FormView):
        template_name = 'register.html'
        form_class = UserForm
        success_url = '/accounts/signin'
    
        def form_valid(self, form):
            password = make_password(form.data.get('password'))
            user = User(
                email=form.data.get('email'),
                password=password,
                nickname=form.data.get('nickname'),
            )
            user.is_active = False
            user.save()
    
            #회원 가입 이메일 전송송
            task_send_register_mail(user, get_current_site(self.request).__str__())
    
            return super().form_valid(form)

    2. 메일 전송 코드 작성

    해당 작업이 task_send_register_mail 함수를 만드는 작업입니다.

    task.py라는 파일을 만들고 함수를 작성합니다.

    task.py로 따로 함수를 뺀 이유는 추후에 celery & redis를 적용하여 비동기적으로 email을 보낼 것이기 때문입니다.

    from django.template.loader import render_to_string
    from django.utils.http import urlsafe_base64_encode
    from django.utils.encoding import force_bytes
    from django.core.mail import EmailMultiAlternatives
    from config.settings import DEFAULT_FROM_EMAIL
    import time
    from datetime import datetime
    from app_accounts.models import User
    from app_accounts.tokens import activate_token
    
    from django.conf import settings
    
    
    def task_send_register_mail(user, domain):
        try:
    
            template = 'mail_register.html'
            now_time = time.mktime(datetime.now().timetuple())
    
            kwargs = {
                'uid': urlsafe_base64_encode(force_bytes(user.id)),
                'token': activate_token.make_token(user),
                'time': int(now_time)
            }
            message = render_to_string(template, {
                'user': user,
                'domain': domain,
                'kwargs' : kwargs,
            })
            mail_subject = '회원가입 인증메일'
            to_email = [user.email]
            from_email = DEFAULT_FROM_EMAIL
            body = ''
            email = EmailMultiAlternatives(mail_subject, body, from_email, to_email)
            email.attach_alternative(message, "text/html")
            email.send()
    
        except User.DoesNotExist as e:
            print('email send error')

    여기서 또 의문은 activate_token은 뭐지?라는 생각이 드는데 이것이 3번째 작업이다.

     

    3. 메일 전송 내용 중 토큰 생성 함수 작성.

    app_accounts > tokens.py 파일을 만들고 함수를 작성합니다.

    이곳은 그냥 django에서 auth token을 인증하기 위해 제공하는 기능입니다.

    그냥 사용하시면 됩니다.

    from django.contrib.auth.tokens import PasswordResetTokenGenerator
    import six
    
    class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
        def _make_hash_values(self,user, timestamp):
            return(six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.active))
    
    activate_token = AccountActivationTokenGenerator()

     

    4. 메일 전송에 필요한 html 작성

    이제 다시 2번 작업에서 작성한 함수를 보면 메일에 필요한 html이 필요하다.

    이는 간단하게 다음과 같이 작성하겠습니다.

    {% if user.is_active %}
        <h3>계정이 활성화 되었습니다.</h3>
        <script language="javascript">
            setTimeout(function () {
                window.close()
            }, 2000);
        </script>
    {% else %}
    
    <table border="0" width="671" background="">
      <tr>
        <td align="center" style="padding-bottom: 20px;">
          <a target="_blank" href="http://{{ domain }}{% url 'activate' %}?uidb64={{ kwargs.uid }}&token={{ kwargs.token }}&time={{ kwargs.time }}" style="line-height: 50px; font-family: Dotum;color: #fff;font-size: 16px; font-weight: 500; width: 212px;height: 50px; background: #614ED0; display: block; text-decoration: none;">이메일 인증</a>
        </td>
      </tr>
    </table>
    {% endif %}

    다른 건 사실 필요 없고 a태그에서 href에 인증하는 url이 필요합니다.

    url_name이 activate인 곳을 찾아가게 됩니다.

    요청 방식은 GET이고? 이후에서 query param 형식으로 데이터를 전달합니다.

    저것도 app_accounts > views.py에 추가해야 하는 요청인데 일단은 mail부터 잘 가는지 테스트해보겠습니다.

     

    5. 이메일 인증을 위한 엔드포인트 만들기 (user activate) 활성화 로직

    app_accounts > views.py

    이 부분의 token 체크는 django에서 제공해주는 기능이기 때문에 그냥 사용하면 됩니다.

     

    이메일에서 인증하기 버튼을 누르면 해당 token을 체크하는 이 함수가 동작합니다.

    해당 메일에서 제공한 데이터의 대상인 user가 존재하고, token이 올바르면

    해당 user의 계정을 활성화시키는 로직입니다.

    def activate(request):
        uidb64 = request.GET.get('uidb64')
        token = request.GET.get('token')
    
    
        uid = force_text(urlsafe_base64_decode(uidb64))
        user = get_object_or_404(User, pk=uid)
    
        user = User.objects.get(pk=uid)
    
        #활성화 인증
        if user is not None and activate_token.check_token(user, token):
            user.is_active = True
            user.save()
            return redirect('signin_page')
        else:
            return HttpResponse('비정상적인 접근입니다.')

     

    * 현재 urls.py

    urlpatterns = [
        path('', index, name='accounts_main'),
        path('signup', CreateAccounts.as_view(), name='register'),
        path('signin', LoginPage.as_view(), name='signin_page'),
        path('login', LoginView.as_view(), name='login'),
        path('logout', LogOutView.as_view(), name='logout'),
        path('activate', activate, name='activate')
    ]

     

    6. django smtp서버 정보 연결 (Gmail)

    조금 귀찮기는 한데. 한 번 설정하면 편합니다.

    대신 정보가 잘못 나가면 골치 아파지기 때문에. secret.json에 데이터를 넣는 게 좋습니다.

    사용하는데 한계가 있으므로 개발 서버에서만 사용하고 실제 서비스를 할 때는

    AWS SES 서비스 같은 것을 사용하는 게 편합니다.

     

    google 메일에 들어가서 2가지 설정을 완료하고 다음의 내용을 넣어주면 됩니다.

    설정 페이지
    https://support.google.com/mail/answer/7126229?hl=ko&rd=3&visit_id=1-636281811566888160-3239280507#ts=1665018을 참고하면 좋습니다.

    1.IMAP 사용

    - 일단 Gmail에서 설정을 들어간다.

    - 전달 및 POP/IMAP 설정을 들어간다.

    - IMAP 사용을 누르고 저장한다.

     

    2. 보안 수준 낮은 앱 허용

    [참고]

    https://support.google.com/accounts/answer/6010255#zippy=%2C%EA%B3%84%EC%A0%95%EC%97%90%EC%84%9C-%EB%B3%B4%EC%95%88-%EC%88%98%EC%A4%80%EC%9D%B4-%EB%82%AE%EC%9D%80-%EC%95%B1%EC%9D%98-%EC%95%A1%EC%84%B8%EC%8A%A4%EA%B0%80-%EC%82%AC%EC%9A%A9-%EC%84%A4%EC%A0%95%EB%90%9C-%EA%B2%BD%EC%9A%B0

     

    config > settings.py 설정

    하단에 smtp서버 정보를 넣어준다.

    #Email
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_USE_TLS = True
    EMAIL_PORT = 587
    DEFAULT_FROM_EMAIL = get_secret("DEFAULT_FROM_EMAIL")
    EMAIL_HOST = get_secret('EMAIL_HOST')
    EMAIL_HOST_USER = get_secret('EMAIL_HOST_USER')
    EMAIL_HOST_PASSWORD = get_secret('EMAIL_HOST_PASSWORD')

    Email host는

    • smtp.gmail.com로 작성하고
    • Host_user와 default_from_email은 본인의 구글 mail,
    • password는 google계정 앱 패스워드를 입력하면 됩니다.

    [참고] google계정 App password  설정

    http://support.google.com/accounts/bin/answer.py?answer=185833

     

    7. 테스트...

    회원가입해보기!

    1. 회원가입 시 is_active 값은 False여야 합니다..

     

    2. 회원 가입하면 해당 메일로 인증 메일이 발송되어야 합니다.

     

    3. 버튼 누른 후 is_active값의 변화

    is_active값이 성공적으로 true로 변하였습니다.

     

    이번 시간은 내용이 길어서 설명이 부족할 수 있는데.

    코드는 git에 업로드할 것이니 참고하시면 되고

     

    추가적으로 SMTP 서버 설정에 대한 내용은 굳이 google이 아니어도 상관이 없으므로 편하신 SMTP 서버를 가져와서 세팅한 후 사용하시면 됩니다.

    반응형
Designed by Tistory.