이메일 전송할 때 SMTP 서버부터 구축해야 하는 줄 알고 미뤄두고 있었는데 정말 간단한 방법으로 메일 전송이 가능했다. 우선 settings.py에서 아래 내용을 추가한다.

1
2
3
4
5
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'USER_NAME@gmail.com'
EMAIL_HOST_PASSWORD = 'USER_PASSWORD'
EMAIL_USE_TLS = True

유저와 패스워드에는 자신의 아이디와 패스워드를 입력하면 된다. 위 소스코드는 Gmail 기준이며 Daum과 같은 다른 서비스를 활용하려면 해당 서비스에서 포트 번호등을 확인하는게 좋겠다.


구글 계정 설정

구글 계정에서 약간의 설정을 거쳐줘야 한다.

2번째는 테스트를 위해서 잠시 허용하는 것으로 장기간 사용으로 해두면 보안상 결함이 있을거라 생각된다. 아래에서 SSL을 도입하면 다시 사용 안함으로 전환해도 된다.


메일 전송 테스트

1
python manage.py shell

쉘 스크립트를 실행해서 테스트를 진행할 것이다.

1
2
3
from django.core.mail import EmailMessage
mail = EmailMessage('TITLE', 'CONTENT', to=['USER_NAME@USER_DOMAIN'])
mail.send()

위와같이 전송하면 정상적으로 전송됐다는 1이 출력된다.


텍스트대신 HTML 전송

‘CONTENT’ 부분을 아래와 같이 render_to_string으로 된 오브젝트로 전송하고 컨텐츠 타입을 html로 설정한 뒤 사용할 템플릿 파일을 템플릿 디렉터리에 만들어 두면 된다. 템플릿 사용은 기존의 render와 동일하게 딕셔너리로 객체를 전달해주면 된다.

1
2
3
4
5
6
7
8
from django.template.loader import render_to_string

...

html_message = render_to_string('mail_template.html', { 'ARG1':ARG1 })
email = EmailMessage(title, html_message, to=[ MALE_LIST ])
email.content_subtype = "html"
return email.send()


SSL 인증

필자의 경우에는 Daum을 사용했는데 해당 서비스의 경우 SSL이 필수 조건이라 사용이 불가피 했다. 찾아보니 정말 정말 간단하게 적용할 수 있는 패키지가 존재했다.

1
pip install django-smtp-ssl

위 패키지를 다운로드하고 settings.py를 다음과 같이 수정한다.

1
2
3
4
5
6
EMAIL_BACKEND = 'django_smtp_ssl.SSLEmailBackend'
EMAIL_HOST = 'smtp.daum.net'
EMAIL_PORT = '465'
EMAIL_HOST_USER = 'USER_NAME@daum.net'
EMAIL_HOST_PASSWORD = 'USER_PASSWORD'
EMAIL_USE_TLS = True

위와같이 백엔드를 추가하고 포트를 465로 변경하였다.


유저 메일 인증

다른 사이트에서 찾아보니 유저 메일 인증과정을 굉장히 번거로운 과정으로 진행하기에 다른 방법이 없을까 생각했다. 이 방안 외에도 더욱 다양하고 안전한 방법이 있겠지만 필자의 경우에는 사용자가 가입하면 우선 비활성 계정으로 설정하고 사용자의 이름(last_name)에 랜덤값을 넣고 일치하는 랜덤값이 포함된 URL에 접근했을 때 사용자의 계정을 활성화 하도록 하였다. 모델에서 쓰지않는 테이블을 활용하면서 사용자가 쉽게 접근할 수 없도록 하기 위해서 이와같이 구성하였다.

1
2
3
4
5
6
7
def randstr(length):
    rstr = "0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ"
    rstr_len = len(rstr) - 1
    result = ""
    for i in range(length):
        result += rstr[random.randint(0, rstr_len)]
    return result

위와같이 랜덤값을 뽑아낼 메서드를 하나 만들었고 뷰에서 가입을 진행할때 아래와 같이 처리했다.

1
2
3
4
5
6
7
8
9
10
11
if request.method == "POST":
    if form.is_valid():
        ...
        new_user.last_name = randstr(50) # 사용자에게 랜덤 값 삽입
        new_user.is_active = False       # 사용자를 비활성 계정으로 설정
        
        # 사용자에게 랜덤 값이 포함된 링크를 메일로 전송
        # ex) https://MY_DOMAIN/active/s21a4E23RasgH5GwNJ235qg4

        message = new_user.first_name + "님께서 입력하신 메일로 인증 링크를 발송했습니다."
        return render(request, 'board/notify.html',{'message':message })

이후 URL에서 다음 내용을 추가했다.

1
path('active/<token>', views.user_active,name='user_active'),

이후 뷰에서 처리하는 로직은 다음과 같이 만들었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.shortcuts import get_object_or_404

...

def user_active(request, token):
    user = get_object_or_404(User, last_name=token)
    if user.date_joined < timezone.now() - datetime.timedelta(days=7):
        user.delete()
        message = "만료된 링크입니다. 다시 가입을 신청하세요."
    else:
        user.is_active = True
        user.last_name = ''
        user.save()
        message = "이메일이 인증되었습니다."
    return render(request, 'board/notify.html',{'message':message })

일주일이 지난 링크는 접속시 계정을 삭제하도록 하였다.

WRITTEN BY

배진오

소비적인 일보단 생산적인 일을 추구하며, 좋아하는 일을 잘하고 싶어합니다 :D
im@baejino.com

comments powered by Disqus