Django 프로젝트 - 2주차_1

Red_Panda·2021년 8월 4일
0

Python & Django

목록 보기
4/7
post-thumbnail

이번주는 크게 2가지 기능을 추가했다.

1. 로그인, 로그아웃

먼저 장고는 여러 기능의 앱을 따로 만들어 기능별로 분리해 이리저리 조합할 수 있다.

로그인, 로그아웃, 회원가입 기능은 현재 진행중인 프로젝트가 아니여도 충분히 공통적으로 사용할 수 있기에 보통 앱을 따로 만든다고 한다.

그래서 나도 따로 만들어보았다. 먼저 처음 앱을 만들었을때와 같이 common 앱을 만들어 준다.


그러면 위 사진과 같이 common 앱이 생성된다.
생성한 common 앱을 연결? 시키는 과정은 이렇다.
1. config/setting.py - INSTALLED_APPS에 common앱을 등록해준다.

2. config/urls.py에서 url패턴에 common을 아래와 같이 추가해준다.

그리고 서버를 열어 실행하면 아무 변화는 없지만 페이지가 정상적으로 뜬다.
물론.. 나는 계속 아래와 같은 오류로 막혔었다. 코드를 다시봐도 문제가 없는데 서버가 안열린다

구글링으로 해결방법을 찾다가 이러한 답변을 보았다.

그렇다.. common/urls.py(config/urls.py 아님!!)에서 urlpattern's' s를 쓰지않아서 그런것이였따... s를 넣으니까 잘 실행됐다. 오타 멈춰!!!

그렇게 common앱을 연결하고나서야 기능 추가 작업이 진행됐다.


로그인, 로그아웃은 django에서 제공하는

from django.contrib.auth import views as auth_views

를 이용해 매우 간단하게 만들 수 있다.
(기존의 views와 충돌하지 않기위해 as로 auth_views 라는 별칭을 주었다.)

그리고 auth_views.LoginView, auth_views.LogoutView 를 이용하면 views.py에 코드를 따로 추가하지않아도 로그인, 로그아웃이 가능하다.

다음으로 위 코드에서 보이듯 로그인 화면 tamplate를 만들어서 연결시켜줘야한다.

<login.html>


이렇게 나온다.


다음으로 user.is_autenticated를 이용해 로그인 여부를 확인 후 나타내는 버튼을 달리했다.
1. 로그인 상태라면 로그아웃기능 버튼만
2. 로그인 상태가 아니라면 회원가입, 로그인 버튼만


그리고 로그인, 로그아웃 성공시 기본경로는 /accounts/profile/ 인데 내가 설정한 경로와 다르니 setting.py에서 '/' (메인페이지)로 따로 설정해줬다.

LOGIN_REDIRECT_URL = '/' # 로그인 성공시 이동
LOGOUT_REDIRECT_URL = '/' # 로그아웃시 이동

로그인, 로그아웃 기능을 완료 했으니 이제 회원가입만 남았다.

2. 회원가입

사실 회원가입 기능도 위에서 언급한 django에서 제공하는 기능을 이용하면 쉽게 만들 수 있다.

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

User객체는 다음과 같이 5개의 기본 속성을 가진다.

  • username, password, email, first_name, last_name

그리고 UserCreationForm은

  • username, password1, password2
    를 가지는데 비밀번호1과 2이 같은지, 모든 필드가 채워져있는지 등 규칙을 검사한다.
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class UserForm(UserCreationForm): # UserCreationForm 상속해서 email 속성 추가
    email = forms.EmailField(label="이메일 주소")

    class Meta:
        model = User
        # 앞 3개 속성은 UserCreationForm이 기본적으로 지닌 속성
        # 사용자이름, 비밀번호1, 비밀번호2 --> 비밀번호 2개가 같은지 규칙검사
        fields = ("username","password1","password2","email")

그래서 UserCreationForm을 상속한 Class UserForm을 만들어 email 속성을 추가해줬다.

email 속성을 추가한 이유는.. 바로 인증메일 기능을 만들어보고 싶어서다.

Django 메일 인증 관련 정보는 한글 자료도 많아, 한글+영어 자료를 취합해 성공할 수 있었다.

EMAIL_BACKEND = EMAIL['EMAIL_BACKEND'] # 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = EMAIL['EMAIL_USE_TLS'] # True
EMAIL_PORT = EMAIL['EMAIL_PORT'] # 587
EMAIL_HOST = EMAIL['EMAIL_HOST'] # 'smtp.gmail.com' 구글 메일로 작업
EMAIL_HOST_USER = EMAIL['EMAIL_HOST_USER'] # 메일 발송 아이디
EMAIL_HOST_PASSWORD = EMAIL['EMAIL_HOST_PASSWORD'] # 메일 발송 아이디 비밀번호
  1. 메일 발송용 구글 아이디 설정 및 setting.py
    발송할 구글 계정에서 IMAP 사용함 (1단계), 보안수준 낮은 앱 허용을 설정해야한다.
    그리고 각각의 값은(특히 아이디, 비밀번호) git에 올리지 않는 파일인 key.py에 입력해주고 위코드를 setting.py에 추가했다.

  2. token을 생성할 함수

token.py 파일을 따로 만들어 작성하였다.

import six
from django.contrib.auth.tokens import PasswordResetTokenGenerator

class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (six.text_type(user.pk) + six.text_type(timestamp)) + six.text_type(user.is_active)

account_activation_token = AccountActivationTokenGenerator()

django.utils.six가 기본으로 있었으나 장고 3.x부터 기본에서 빠졌다고 하여 six모듈을 따로 설치해 사용했다.

pip install six

six.text_type은 Py3(34제외)일때는 괄호안의 수를 str형태로, 이외 버전일때는 unicode로 바꿔준다. 그래서 six.text_type대신 str()을 써봤는데 나같은경우 Py38을 사용중이기 때문에 똑같은것 같다.

six.text_type과 str를 섞어가며 인증메일을 발송했을때 별 차이를 못찾았다.

아무튼, PasswordResetTokenGenerator의 _make_hash_value를 이용해
user.pk, timestamp, is_active를 해쉬화 시킨값을 토큰으로 만들었다.

  1. email_text.py를 만들어 메일내용을 작성했다.
def message(domain, uidb64, token):
    return f"다음 링크를 클릭해야 회원가입 인증이 완료됩니다. " \
           f"http://{domain}/common/activate/{uidb64}/{token}"
           
  1. common/views.py 수정
def signup(request):
    """계정생성"""
    if request.method == 'POST':
        form = UserForm(request.POST)
        if form.is_valid():
            user = form.save() # form값을 저장하여 user라는 변수에 넣어준다.
            user.is_active=False # 활성화 상태를 False 바꾼다.
            user.save() # 그리고 저장한다.
            # 비밀번호 8자리이상
            username = form.cleaned_data.get('username')
            raw_password = form.cleaned_data.get('password1')
            check = authenticate(username=username, password=raw_password)  # 검증
            #login(request,check) 회원가입 후 바로 로그인

            current_site = get_current_site(request) # 현재 site 객체 얻기
            domain = current_site.domain # 현재 사이트의 도메인
            uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
            # force_bytes로 자연수 user.pk를 bytes로 변환 후, urlsafe_base64_encode로 인코딩
            token = account_activation_token.make_token(user) # 토큰 생성
            message_data = message(domain,uidb64,token) # 보낼 메세지 내용

            mail_title = "인증을 완료해주세요"
            mail_to = user.email # 회원가입시 입력한 이메일주소
            email = EmailMessage(mail_title,message_data,to=[mail_to])
            email.send() # 메세지 발송
            return render(request,'common/signup_mail_check_plz.html')
    else:
        form = UserForm()
    context = {'form':form}
    return render(request, 'common/signup.html', context)
    
def activate(request, uidb64, token): # 링크 누르면 계정 활성화
    try:
        uid = force_text(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except(TypeError, ValueError, OverflowError, User.DoesNotExsit):
        user = None

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True # 링크가 눌리면 계정 활성화
        user.save()
        return redirect(EMAIL['REDIRECT_PAGE']) # 설정해논 url주소 띄움.
    else:
        return HttpResponse('비정상적인 접근입니다.')

    return

코드 블록 테마 설정이 있다고하는데 못찾겠다.

대부분 자료에서 user = authenticate(request, username = username, password = password) 이런식으로 생성했길래 user.pk 등 user값이 들어가는 변수에 form.pk 이런식으로 넣었더니 실행이 안됐다.

form으로 입력값을 받았을때에는 어떻게 해야할지 몰라서 좀 헤맸었다.

이러한 경우 user = form.save()으로 user를 설정하고 진행하면 된다.

  1. common/urls.py 내용 추가
path('activate/<str:uidb64>/<str:token>',views.activate, name='activate'),

많은 시도 끝에 성공한것같다 ㅎㅎ

이제 계정을 만들면 아래와 같이 입력한 메일로 링크를 보내준다.

아래는 계정 만들기 과정이다.

  1. 회원가입을 하면 아래와 같은 문구를 띄운다.
  2. 인증메일을 누르지 않으면 아래와 같이 로그인이 불가능하다.


참고로, 일반적인 논필드 오류로 에러를 출력하면 에러문구가 아래와 같이 나오는데 이경우에, ID나 비밀번호가 틀린건지, 비활성화 계정인지 구별할 수 없다.
이를 구별하기위해 방법을 찾아보니 is_active를 확인하는 방법이 있다.
setting.py에 아래 코드를 넣어주면 위와같이 비활성화 계정인경우 유효하지 않다는 에러 문구를 띄울 수 있다.

AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend'] # is_active계정 유효성 에러문구

어드민 계정으로 해당 계정을 보면 활성칸에 체크가 없음을 확인할 수 있다.

이 활성칸을 체크하려면 메일로 받은 링크를 눌러야한다.

메일 링크를 누르면 메인페이지를 띄우고, 해당 계정은 활성화 된다.

그리고 기본 토큰 유효시간은 PasswordResetTokenGenerator를 살펴보니 24시간이다.


이번주에 크게 2가지 기능을 추가했다고 했는데 1. 로그인,로그아웃,메일인증 2. 검색기능 추가 였다.

1번에서 내가 겪은 내용들을 최대한 기록하려니 내용이 길어졌다. 2번은 새 글로 작성해야겠다.

profile
신입 개발자

0개의 댓글