본문 바로가기
Research/Django

[Django] 점프 투 장고 튜토리얼 - 03-5. 로그인과 로그아웃

by RIEM 2021. 11. 19.
728x90

Django 점프 투 장고 정리

작성일 : 2021-11-16

문서버전 : 1.0

 

개요

이 문서는 점프 투 장고 사이트의 장고 튜토리얼 학습 내용을 정리한 내용입니다.

레퍼런스

점프 투 장고 https://wikidocs.net/72242

3-05. 로그인과 로그아웃

로그인과 로그아웃 기능을 추가해보자. 질문자와 답변자를 구분할 수 있게 해줄 것이다. 장고에서 로그인과 로그아웃을 도와주는 앱은 ‘django.contrib.auth’이다. 프로젝트 생성시에 아래와 같이 자동으로 생성된다.

> ../projects/mysite/config/settings.py

 
...
INSTALLED_APPS = [
  'pybo.apps.PyboConfig',
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
]
...



Common 앱

로그인, 로그아웃 기능은 웹 사이트 전체에서 공통적으로 사용할 수 있다. 따라서 pybo 앱 내 종속시킬 필요는 없다. 이를 위해 사이트 전체의 공통 기능을 가진 앱이라는 의미의 ‘common’앱을 만들어서 로그인, 로그아웃 기능을 구현해보자.


> app 생성

아래 명령어로 common 앱을 생성시켜 주자.

(mysite)../projects/mysite/django-admin startapp common

 




> ../projects/mysite/config/settings.py

config/settings.py 파일에 생성한 common 앱을 등록하기

 
INSTALLED_APPS = [
  'common.apps.CommonConfig',
  'pybo.apps.PyboConfig',
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
]


> ../projects/mysite/config/urls.py

Common 앱의 urls.py 파일 사용하기 위해선 config/urls.py 파일도 수정해야 한다

 
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('pybo/', include('pybo.urls')),
    path('common/', include('common.urls')),
]

 

localhost:8000/common/으로 시작하는 URL은 모두 common.urls.py 파일을 참조하라고 패턴을 지정해주었다. 그럼 common.urls.py 파일을 생성해주자.


> ../projects/mysite/common/urls.py

 
app_name = 'common'

urlpatterns = [
]

앱에 기능은 추가하지 않았으니 빈 상태로 두자.

 

로그인

본격적으로 로그인 기능을 구현해보자. 로그인의 시작은 로그인 화면이다. 로그인 화면으로 들어갈 수 있도록 아래 파일의 로그인 링크를 아래와 같이 수정하자.

> ../projects/mysite/templates/navbar.html

 
(... 생략 ...)
<ul class="navbar-nav">
    <li class="nav-item ">
        <a class="nav-link" href="{% url 'common:login' %}">로그인</a>
    </li>
</ul>
(... 생략 ...)

 

로그인 뷰

Navbar.html 파일에서 템플릿 태그로 {% url ‘common:login’ %}를 사용했으니, common/urls.py 파일에 다음과 같은 URL매핑을 추가해줘야 한다.

> ../projects/mysite/common/urls.py

 
from django.urls import path
from django.contrib.auth import views as auth_views

app_name = 'common'

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

 

로그인 뷰는 따로 만들지 않고 위와 같이 django.contrib.auth 앱의 LoginView를 사용할 수 있다.

 

로그인 템플릿

위까지 수정하고 네비게이션바의 ‘로그인’링크를 누르면 아래와 같은 페이지가 뜬다. 주소는 localhost:8000/common/login/

 

오류는 registration 디렉터리에 login.html 파일이 없다는 뜻이다. 이는 앞서 사용한 LoginView는 ‘registration’템플릿 디렉터리에서 login.html 파일을 찾는데 해당 파일을 찾지 못했기 때문에 발생한 오류다. 이를 해결하기 위해 registration/login.html 템플릿 파일을 작성해야 한다.

 

로그인은 common 앱에 구현할 것이므로 오류 메시지의 안내 문구와 같이 registration 디렉터리에 템플릿 파일을 생성하기 보다는 common 디렉터리에 템플릿을 생성하는 것이 좋다. 

> ../projects/mysite/common/urls.py

이를 위해 LoginView가 common 디렉터리의 템플릿을 참조할 수 있도록 common/urls.py 파일을 아래와 같이 수정하자.

 
from django.urls import path
from django.contrib.auth import views as auth_views

app_name = 'common'

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

 

위와 같이 수정하면 registration 디렉터리가 아닌 common 디렉터리에서 login.html 파일을 참조하게 된다. 코드 수정 후 로그인 링크 누르면 여전히 오류 메시지나 난다. 그런데 오류 메시지가 조금 달라진 것을 알 수 있다.

registration/login.html이 아닌 common/login.html이 없다는 오류로 변경되었다. 그렇다면 common/login.html 파일 생성을 위해 common 템플릿 디렉터리를 다음과 같이 생성하자.

(mysite) c:\projects\mysite>cd templates
(mysite) c:\projects\mysite\templates>mkdir common


> ../projects/mysite/templates/common/login.html

 
{% extends "base.html" %}
{% block content %}
<div class="container my-3">
    <form method="post" class="post-form" action="{% url 'common:login' %}">
        {% csrf_token %}
        {% 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>
    </form>
</div>
{% endblock %}

 

위 템플릿은 사용자의 ID와 비밀번호를 입력받아 로그인하게 도와준다. Username과 password 항목은 django.contrib.auth 앱이 요구하는 필수항목이다. 

 

그리고 {% csrf_token %} 밑에 include 태그로 포함된 form_error.html 템플릿 파일을 작성해주자.

> ../projects/mysite/templates/form_errors.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 %}

 

일반적으로 폼 오류는 두 가지 종류가 있는데, 1) 필드 오류(field.errors)와 넌필드 오류(form.non_field_errors)다. 

필드 오류(field.errors)는 사용자가 입력한 필드 값에 대한 오류를 지칭하는데, 값이 누락되거나 필드 형식이 일치하지 않는 경우 발생한다. 넌필드 오류(form.non_field_errors)는 필드값과 무관하게 다른 이유로 발생하는 오류다. 

 

로그인 수행

네비게이션바의 로그인 링크 클릭하면 다음과 같은 화면을 볼 수 있다.

 

오류메시지도 정상적으로 뜬다.

 

지금은 슈퍼유저로 만든 ‘admin’계정만 로그인할 수 있다. 사용자 ID에 admin 입력 후 비밀번호에 1111을 입력해서 로그인을 수행해보자.

오류가 발생한 이유는 로그인 성공 시 django.contrib.auth 패키지는 디폴트로 /accounts/profile/라는 URL로 이동시키기 때문이다. 

 

다만 /accounts/profile/ URL은 현재 우리가 파이보에 구성한 것과 맞지 않기 때문에 성공 시 ‘/’페이지로 이동할 수 있도록 config/settings.py 파일을 수정하자. 마지막 로그인줄에 LOGIN_REDIRECT_URL을 추가하면 된다. 여기서 ‘/’페이지는 기본 URL인 ‘http://localhost:8000/’ 페이지를 의미한다.


> ../projects/mysite/config/settings.py

 
(... 생략 ...)

# 로그인 성공후 이동하는 URL
LOGIN_REDIRECT_URL = '/'



그런데 localhost:8000 페이지로 접속하면 아래와 같은 오류가 발생한다.

이는 ‘/’를 의미하는 http://localhost:8000/ 페이지에 대한 URL 매핑을 하지 않았기 때문이다. 

> ../projects/mysite/config/urls.py

 
from django.contrib import admin
from django.urls import path, include
from pybo import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('pybo/', include('pybo.urls')),
    path('common/', include('common.urls')),
    path('', views.index, name='index'),  # '/' 에 해당되는 path
]

 

위와 같이 수정하면 ‘/’페이지 요청을 할 경우 path(‘’, views.index, name=’index’)가 작동하여 pybo/views.py 파일의 index 함수가 실행된다. 

 

로그아웃

로그인 성공 후, 네비게이션에서 로그인 링크를 로그아웃 링크로 바꾸기 위해서 navbar.html 템플릿 파일에서 로그인 링크 부분을 아래와 같이 수정한다.

> ../projects/mysite/templates/navbar.html

 
(... 생략 ...)
<li class="nav-item">
    {% if user.is_authenticated %}
    <a class="nav-link" href="{% url 'common:logout' %}">{{ user.username }} (로그아웃)</a>
    {% else %}
    <a class="nav-link" href="{% url 'common:login' %}">로그인</a>
    {% endif %}
</li>
(... 생략 ...)

 

{% if user.is_authenticated %} 은 현재 사용자가 로그인 되었는지 판별한다. 로그인 된 경우 로그아웃 링크를 추가하고, 그렇지 않은 경우(else) 로그인 링크를 표시한다. 로그인 상태일 경우 {{ user.username }}도 추가로 표시했다. 로그아웃 링크가 추가되었으므로 {% url ‘common:logout’ %}에 대응하는 URL 매핑을 common/urls.py 파일에 추가해야한다.

> ../projects/mysite/common/urls.py

 
from django.urls import path
from django.contrib.auth import views as auth_views

app_name = 'common'

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

 

Admin 계정(superman)으로 로그인 성공!



> ../projects/mysite/config/settings.py

로그아웃 이후 redirect할 위치도 추가해주자. 

(... 생략 ...)



# 로그인 성공후 이동하는 URL

LOGIN_REDIRECT_URL = '/'



# 로그아웃시 이동하는 URL

LOGOUT_REDIRECT_URL = '/'

로그아웃하니 로그인 화면으로 정상적으로 redirect 되었다.

 

728x90

댓글