회원관리 3-0. 로그인 & 로그아웃

Pywiki
Sam (토론 | 기여)님의 2022년 12월 14일 (수) 09:39 판 (→‎로그인 관련)
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)
둘러보기로 가기 검색하러 가기


유저들의 권한, 정보에 관한 것들.


1 URL작성

필요한 기능에 대해 생각하고 url을 짠다.

from django.urls import path
from django.contrib.auth import views as auth_views  #장고에서 제공하는 기능 활용!

app_name = 'membership'

urlpatterns = [
    path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]

심지어 뷰를 안만들어도 된다.

  • registration/login.html 을 기본옵션으로 하지만, 따로 디렉터리를 만드는 것은 낭비다. 때문에 template_name 변수를 따로 지정했다.
  • 로그아웃 뷰에선 기본 탬플릿이 없지만, template_name을 지정하면 로그아웃 후 redirect할 탬플릿을 지정할 수 있다.

2 네비게이션 바에 로그인 버튼 만들기

네비게이션 바 html 적당한 곳에 다음을 삽입한다.

<!--로그인, 로그아웃 기능-->
{% if user.is_authenticated %}
    <p>{{ user.username }}</p><a class="nav-link" href="{% url 'membership:logout' %}">(로그아웃)</a>
    <!--로그아웃버튼과 함께 기본적인 정보를 띄워준다.-->
{% else %}
    <a class="nav-link" href="{% url 'membership:login' %}">로그인</a>
{% endif %}

user의 속성을 이용해 로그인 상태일 때와 아닐 때의 형식이 다르게 만들었다.

3 탬플릿 만들기

3.1 login.html

기본적으로 id와 password를 입력하게 되어 있는데, 분명 모델에선 identifier를 식별필드로 지정했는데, 이 코드를 USERNAME_FIELD에 지정하기 때문인지, form 안엔 username으로 받아들이게끔 구성되어 있다. 즉, 식별필드가 무엇이든 username이라는 이름을 사용해서 로그인이 이루어지게 된다.

{% extends "base.html" %}

{% block content %}
<form method="post" action="{% url 'account:login' %}">
    {% csrf_token %}
<!-------------------------------------- 입력에 에러가 났을 때 불러올 html-->
    {% include "form_errors.html" %}
<!-------------------------------------- 입력을 위한 공간-->
    <div class="form-group">
        <label for="username">ID</label>
        <input type="text" class="form-control" name="username" id="username"
               value="{{ form.username.value|default_if_none:'' }}">
    </div>
    <div class="form-group">
        <label for="password">비밀번호</label>
        <input type="password" class="form-control" name="password" id="password"
               value="{{ form.password.value|default_if_none:'' }}">
    </div>
    <button type="submit" class="btn btn-primary">로그인</button>

    <input type="hidden" name="next" value="{{ next }}"><!--로그인 성공 후 이전 페이지로 이동하게끔 하는 기능-->
</form>
{% endblock %}

3.2 form_errors.html

account 앱과 관련한 모든 에러메시지를 관리하기 위해 에러메시지만 띄우는 탬플릿을 만든다. 일괄적, 일관적으로 모든 공간에 적용할 수 있게끔.

에러가 발생했을 때 에러메시지를 띄울 html을 불러온다.

{% if form.errors %}
    {% for field in form %}
<!------------------------- 필드 오류를 출력 -->
        {% for error in field.errors %}  
            <div class="alert alert-danger">
                <strong>{{ field.label }}</strong>
                {{ error }}
            </div>
        {% endfor %}
    {% endfor %}
 <!----------------------- 넌필드 오류를 출력 -->
    {% for error in form.non_field_errors %}  
        <div class="alert alert-danger">
            <strong>{{ error }}</strong>
        </div>
    {% endfor %}
{% endif %}

필드오류 : 사용자가 form 양식에 맞지 않는 데이터를 입력했을 때.

넌필드오류 : 기타 등등 에러

4 로그인, 로그아웃 성공 후 이동할 url 만들기

4.1 로그인 url

일반적으로 @login_required(login_url='account:login') 형태로 로그인 url을 써주어야 하지만, 모든 함수에 이걸 써넣긴 어렵고, 수정에도 불편하다.

=> settings.py에 LOGIN_URL = 'account:login' 형태로 변수를 지정하면 데코레이터의 괄호에 무언가를 써주지 않아도 잘 기능한다.

4.2 로그인 로그아웃 후 리다이렉트

settings.py에서 새로운 설정항목을 만들어주어야 한다.

관련변수 설명 예시
LOGIN_REDIRECT_URL 로그인 후 연결될 주소 설정.

next 변수를 주어 보던 페이지로 이동하게 할 수도 있다.(다음 내용 참고)

주소 대신 '앱이름:list' 형태의 인덱스도 가능하다.

LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL 로그아웃 후 연결될 주소 설정. LOGOUT_REDIRECT_URL = '/'

4.3 보던 페이지로 돌아가기

그런데, 사실 로그인 후에 보던 페이지가 안나오면 다시 찾아들어가야 하고.. 불편하다; 다행스럽게도 로그인 기능을 제대로 구현해두면 로그인 페이지로 넘어갈 때 링크가 http://127.0.0.1:8000/account/login/?next=/보고있던페이지/ 형태로 구성된다. 이전 페이지에 대한 정보를 담고 있는데, 로그인 탬플릿에 다음의 숨겨진 항목을 추가하면 된다.(폼 안에 추가해야 한다.)

<input type="hidden" name="next" value="{{ next }}">

submit은 해당 링크에 대한 정보를 그대로 form으로 보내기에 {{next}}에 있는 이전페이지 주소를 받아 기능한다. 로그인 함수에서 알아서 기능해준다.

[한 가지 의문인 것은... 이전페이지의 정보인 next를 어떻게 가져온거지;;? 이전페이지에서 보내준 것도 아닌데?? 아무래도 데코레이터에 걸리는 듯하다.

데코레이터에 걸려서 로그인화면으로 넘어가면 next 인자가 생긴다.]

네비게이션바에 구현한 로그인 버튼은 next인자를 남기지 않는데, 로그인바의 버튼의 주소를.. herf="{% url '인덱스' %}"?next={{request.path}}" 로 넣어주면 현재 경로를 넘겨줄 수 있다.

5 권한

일반 view 사용 클래스형 view 사용
로그인이 필요한 기능 로그인이 필요한 기능(함수) 위에 @login_required를 추가한다.
from django.contrib.auth.decorators import login_required

@login_required(login_url='common:login')
def 함수이름(request):
    명령...
상속받을 클래스 이전에 첫 번째로 상속받게 한다.
from django.contrib.auth.mixins import LoginRequiredMixin

def 함수이름(LoginRequiredMixin, 상속받을 클래스):
    명령...

유저관련기능...

{% if user.is_authenticated %}

위 조건이 있으면 로그인 할 때만 실행된다.

일반 뷰에선 login_url로 로그인 페이지 지정이 가능하고, 클래스형 뷰에선 settins.py의

LOGIN_REDIRECT_URL = '/article/'

LOGIN_URL = '/user/login/'

따위를 지정하든가, login_url = settings.LOGIN_URL 변수를 오버라이딩 하면 된다.


@login_required와 Mixin은 내부적으로 requests.user.is_authenticated 값을 비교한다.

별도의 인증여부를 확인해야 하는 경우는 requests.user.is_authenticated를 그대로 가져다 쓰면 오류 없이 사용할 수 있다.


@login_required 는 클래스형 뷰에선 사용이 안된다.

클래스형 뷰엔 2가지 방식으로 적용할 수 있다.

LoginRequiredMixin을 상속하거나, @method_decorator(login_required, '메소드명') 형태로 클래스형 뷰 안의 메소드에 적용할 수 있다.


데코레이터.

데코레이터 파일을 만든 후,

def 데코레이터명(func):
    def decorated(request, *args, **kwargs):
        내용
        return func(request, *args, **kwargs)
    return decorated

import 해서 @method_decorator(데코레이터명, '메소드명') 형태로 적용할 수 있다.

적용할 데코레이터가 많으면 위에 리스트를 정의한 후, 데코레이터명 대신 리스트를 넣으면 리스트 안의 데코레이터를 한번에 적용할 수 있다.

6 로그인 관련

설명 코드
로그인 from django.contrib.auth import login

login(request, 위에서의객체)

로그아웃 from django.contrib.auth import logout

logout(request)

7 뷰 직접 만들기

제공되는 로그인 뷰를 사용할 수도 있지만, 로그인 중에 특수한 작업을 해야 하는 경우 직접 만들어야만 한다.

쓸만한 모듈 설명
from django.contrib.auth.hashers import check_password 입력한 패스워드와 저장된 패스워드를 비교한다.

(입력패스워드는 raw, 저장된 패스워드는 암호화 된 것)

from django.contrib.auth import authenticate user = authenticate(username=식별자, password=쌩비밀번호)

인증에 성공하면 user객체가 담기고, 실패하면 None을 반환한다.

from django.contrib.auth import login login(request, user)

유저객체를 넣으면 로그인을 진행한다.(비밀번호가 없어도 로그인 된다.)

from django.contrib.auth import logout logout(request)