1.drf 팀 프로젝트 - 이메일 인증
문제점
유저 회원가입을 만드는 중, 이메일 인증의 필요성이 생김
시도해 본 것들
dj-rest-auth 패키지 사용 시도
이메일을 받을 수 있지만, 기존에 작성한 회원 가입이 아닌 패키지에서 제공하는 회원 가입을 사용하면서 필수적인 값을 넘길 방법을 찾지 못함
settings.py에서 시크릿 키처럼 관리해야 할 항목 추가
EMAIL_HOST_USER = "이메일"
EMAIL_HOST_PASSWORD = "비밀번호"
인증 이메일을 전송하는 이메일과, 비밀번호
# views.py
# 이메일 인증 view
class ConfirmEmailView(APIView):
permission_classes = [AllowAny]
def get(self, *args, **kwargs):
self.object = confirmation = self.get_object()
confirmation.confirm(self.request)
# A React Router Route will handle the failure scenario
return HttpResponseRedirect('/') # 인증성공
def get_object(self, queryset=None):
key = self.kwargs['key']
email_confirmation = EmailConfirmationHMAC.from_key(key)
if not email_confirmation:
if queryset is None:
queryset = self.get_queryset()
try:
email_confirmation = queryset.get(key=key.lower())
except EmailConfirmation.DoesNotExist:
# A React Router Route will handle the failure scenario
return HttpResponseRedirect('/') # 인증실패
return email_confirmation
def get_queryset(self):
qs = EmailConfirmation.objects.all_valid()
qs = qs.select_related("email_address__user")
return qs
# urls.py
# dj-reset-auth 패키지 활용하기
path('dj-rest-auth/', include('dj_rest_auth.urls')),
path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')),
re_path(r'^account-confirm-email/$', VerifyEmailView.as_view(), name='account_email_verification_sent'),
# 유저가 클릭한 이메일(=링크) 확인
re_path(r'^account-confirm-email/(?P<key>[-:\\w]+)/$', views.ConfirmEmailView.as_view(), name='account_confirm_email'),
이해한 코드를 사용한 게 아니라서 커스텀의 어려움이 생김
포기 후 다른 방법 찾기
django의 EmailMessage 사용하기
# settings.py
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST = "smtp.gmail.com"
EMAIL_HOST_USER = "이메일"
EMAIL_HOST_PASSWORD = "비밀번호"
DEFAULT_FROM_MAIL = EMAIL_HOST_USER
이메일을 사용하기 위한 기본적인 설정
manage.py shell 에서 이메일 테스트
from django.core.mail import EmailMessage
email = EmailMessage('title', 'content', to=['이메일'])
email.send()
shell 에서 이 코드를 실행했을 때 1이 나오면 정상적으로 email이 전송 된 상태다.
from django.core.mail import EmailMessage
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
extra_kwargs = {
"followings": {
"read_only": True,
},
}
def create(self, validated_data):
password = validated_data.pop("password")
user = User(**validated_data)
user.set_password(password)
user.save()
**to_email = user.email
email = EmailMessage("title", "content", to=[to_email])
email.send()**
return user
회원 가입을 진행하는 UserSerializer에서 email 전송 테스트
tokens.py 생성
from django.contrib.auth.tokens import PasswordResetTokenGenerator
class AccountActivationToken(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return user.pk + timestamp + user.is_active
account_activation_token = AccountActivationToken()
이메일 인증에서 핵심이 되는 인증키를 만드는 함수
serializer 수정하기
from django.core.mail import EmailMessage
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from .tokens import account_activation_token
class UserSerializer(serializers.ModelSerializer):
# 추가 내용
# url에 포함될 user.id 에러 방지용 encoding하기
uidb64 = urlsafe_base64_encode(force_bytes(user.id))
# tokens.py에서 함수 호출
token = account_activation_token.make_token(user)
to_email = user.email
email = EmailMessage(
"AOA 술술술 이메일 인증",
f"<http://127.0.0.1:8000/users/activate/{uidb64}/{token}>",
to=[to_email],
)
email.send()
urls 작성
urlpatterns = [
path(
"activate/<str:uidb64>/<str:token>/",
views.ActivateView.as_view(),
name="activate_view",
),
]
이메일 인증 view 작성
from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_decode
class ActivateView(APIView):
def get(self, request, uidb64, token):
print("activate")
try:
uid = force_str(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
if account_activation_token.check_token(user, token):
# 최초 회원가입 시 is_active=False, 인증 시 True로 변경
User.objects.filter(pk=uid).update(is_active=True)
return Response({"인증 완료!"})
return Response({"error": "AUTH_FAIL"}, status=400)
except KeyError:
return Response({"error": "KEY_ERROR"}, status=400)
참고했던 자료가 옛날 자료여서 force_text 사용 ← django 4.0 이상부터 force_str로 사용
해결 방법
EmailMessage 사용하기
from django.core.mail import EmailMessage
알게 된 점
잘 모르는 내용을 시도할 때는 가장 기초적인 방법을 시도하는 게 난이도가 쉬운 건 당연하고 , 작성하고 문제를 해결하면서 배울 수 있는 점이 좋다는 것을 체감했다.
코드 자체가 어렵고 패키지에 너무 많은 내용이 담겨있어 어디서 문제가 발생하는지 알 수가 없었다.
하지만 EmailMessage는 참고하던 코드가 six나 force_text를 사용해도 중요한 코드가 어렵지 않아서 하나하나 찾아보고 직접 실행하며 수정할 수 있었고 코드의 흐름을 파악할 수 있었다.
1.drf 팀 프로젝트 - secrets.json
문제점
이메일 인증 기능을 구현한 후 github에 push하기 전 이메일과 비밀번호를 보호해야함
시도해 본 것들
secrets.json 활용하기
secrets.json
{
"SECRET_KEY": "시크릿 키",
"EMAIL": "이메일",
"PASSWORD": "비밀번호"
}
EMAIL_HOST_USER = get_secret("EMAIL")
EMAIL_HOST_PASSWORD = get_secret("PASSWORD")
SECRET_KEY를 보호하는 과정에서
secret_file = os.path.join(BASE_DIR, "secrets.json")
with open(secret_file) as f:
secrets = json.loads(f.read())
def get_secret(setting, secrets=secrets):
try:
return secrets[setting]
except KeyError:
error_msg = "Set the {} environment variable".format(setting)
raise ImproperlyConfigured(error_msg)
이 코드들이 존재해서 처음에 설정하고 난 뒤는 get_secret을 통해서 간단하게 호출할 수 있다.
알게 된 점
이번 프로젝트를 시작하면서 처음 SECRET_KEY를 관리해서 아직 막연한 걱정이 있었는데 보호할 데이터를 추가하는 것은 훨씬 쉽게 가능했다.
'개발일지 > TIL' 카테고리의 다른 글
TIL 23-05-12 drf 팀프로젝트 - serializer update(), ininstance (0) | 2023.05.12 |
---|---|
TIL 23-05-11 drf 팀 프로젝트 - git - PR후 작업, push취소, branch명 변경, commit내용 변경 (0) | 2023.05.11 |
TIL-23-05-09 drf 팀 프로젝트 - validation (0) | 2023.05.09 |
TIL 23-05-08 DRF 팀 프로젝트 (0) | 2023.05.08 |
TIL 23-05-07 유클리드 호제법 (1) | 2023.05.07 |