/ DJANGO

(Django) SMTP 사용해 회원가입 이메일 인증하기


SMTP(Simple Mail Transfer Protocol)

smtp는 인터넷에서 이메일을 보낼때 사용하는 프로토콜로 두 메일 서버간의 통신을 지원해준다. 클라이언트가 smtp를 통해 서버에서 보내는 인증메일을 받아 인증할 수 있다.


gmail SMTP를 사용하기 위해서는 두가지를 설정해주어야 한다

  1. IMAP 사용함 설정 (인터넷을 통해서 메일 서버에 접근하기 위한 프로토콜이다))

  2. 보안 수준이 낮은 앱 허용 (보안수준이 낮은 형태로 접근하기때문에 이를 허용해주어야 한다


user를 생성할때 is_active를 False로 설정한다

이메일 인증을 위해서 유저가 생성은 되지만 인증을 하지 않으면 로그인 될 수 없게 is_active를 False로 설정해준다. 이 후 인증이 되면 is_active를 True로 바꾸어준다



settings.py에 이메일에 관련한 추가사항을 넣어준다. 이메일을 Gmail 을 기준으로 설정하였다. 이때 gmail의 새로운 계정을 파서 하는걸 추천한다. (내가 쓰는 건 2단계 인증이 되어 있어서 앱 비밀번호등을 설정해주어야 한다)

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER='{사용할 gmial 주소}'
EMAIL_HOST_PASSWORD='해당 gmail pwd'
EMAIL_USE_TLS=True



로직을 작성하기 전 이메일 연결이 잘되었는지 확인하기 위해 테스트를 진행한다

python shell에서 진행해도 되지만 장고쉘에서 테스트를 진행하는것이 더 간편하다

# 장고 쉘 열기
$ python manage.py shell --settings=backend.settings.dev
>>>
# 이메일 보내기 테스트
>>> from django.core.mail import EmailMessge
>>> email = EmailMessage('text메일입니다', '테스트를 진행하고 있습니다', to=['gmail 주소'])
>>> email.send()

1

# 1이 출력되면 성공 -!



forms.Form과 View를 사용하여 회원가입 로직을 작성했다는 가정하에 시작하겠다.

먼저 유저들의 이메일에 보내질 메시지를 생성하는 text.py를 만든다

아래 링크를 클릭하면 해당 url로 리다이렉트 되는데 이때 uib64나 token안에는 주요 정보가 들어가 있기 때문에 해당 데이터들은 암호화해준 상태이다. view에서 해당 messge 메서드를 호출할 예정이다

def message(domain, uidb64, token):
  return f"아래 링크 클릭 프리즈: http://{domain}/user/account/activate/{uidb64}/{token}"



views에서 is_valid 로직에 이메일로 전송할 로직을 작성한다

유저의 회원가입 데이터의 유효성 검증이 성공하면 해당 현재 site의 도메인 주소와 user의 pk를 암호화한 uidb64, token을 생성한뒤 만들어놓았던 messge 메서드에 매개변수로 보내준다. 유저에게 보내질 msg title을 입력하고, 유저가 입력한 메일을 변수에 저장한뒤 EmailMeaage를 통해 유저에게 보내질 데이터를 담은 뒤 send()를 사용하여 보내준다.

# views.py

if signup_form.is_valid():

        current_site = get_current_site(request)
        domain = current_site.domain
        uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
        token = jwt.encode({'user_pk' : user.pk}, 'secretkey', algorithm = 'HS256').decode('utf-8')
        message_data = message(domain, uidb64, token)

        mail_title = '이메일 인증을 완료해주세요'
        data ={'email': signup_form.email}  
        mail_to = data['email']
        email = EmailMessage(mail_title, message_data, to=[mail_to])
        email.send()
        return JsonResponse({'context':'is_scucces'})



이후 유저가 링크를 선택했을때 넘어올 url을 작성한다.

이때 message메서드에서 작성한 url과 동일하게 만들어주어야 한다.

path('account/activate/<str:uidb64>/<str:token>/', EmailValid.as_view()),



인증에 성공하면 is_active를 True로 변경해주는 로직을 작성한다


class EmailValid(View):
    def get(self, request, uidb64, token):
        uidb = force_text(urlsafe_base64_decode(uidb64))
        user = UserService.get_by_user(uidb) # 유저 모델에서 uid(pk)와 동일한 유저를 가져온다
        token = jwt.decode(token,'secretkey',algorithm='HS256') # token을 decode한다
        result = UserService.verify_user_active(user,user.pk, token['user_pk']) # 받아온 데이터들이 동일한지 검사후 is_active를 변경해준다
        auth_login(request, user)
        
        if result:
            return redirect('user:signup_detail')
        else:
            return redirect('user:signup_detail')



verify_user_active 메서드



def verify_user_active(user,user_pk, token):
    """
    유저의 pk와 token에 담겨있는 유저의 pk같을 경우
    is_active=True로 변경
    """
    if user_pk == token:
        user.is_active = True
        user.save()
        return True
    else:
        return False


이메일이 잘 도착하지만 문제점이라면 문제점이 있다. 이메일이 보내질동안 1~2초간 화면이 멈추는 것처럼 보인다. 유저입장에서는 굉장히 좋지 못한 모습이다. 이후에 celery와 redis를 사용하여 이 문제를 해결할 예정이다.