django.contrib.auth 앱을 사용해서 로그인, 로그아웃 기능을 구현
로그인 기능은 일반적으로 하나의 웹 사이트 전체에서 동작하는 기능이다. 예를 들어 네이버에 한번 로그인을 하면 메일 서비스, 블로그 서비스 등 네이버의 서비스를 모두 사용할 수 있으며 서비스를 사용할 때 각 서비스 별로 로그인을 다시 요구하는 경우는 없다. 따라서 로그인 기능을 pybo 앱에 구현하는 것보다 별도의 앱으로 구현하는 것이 바람직하다. 로그인 기능은 다른 앱에서도 공통적으로 동작하는 기능이므로 common 앱에 구현한다.
common 앱 생성
(django_projects) sleepyowl ~/Study/django_projects/test_site
django-admin startapp common
settings.py에 앱 등록 및 urls.py에 url 등록
...
# Application definition
INSTALLED_APPS = [
...
'common.apps.CommonConfig',
]
......
urlpatterns = [
path('admin/', admin.site.urls),
path('pybo/', include('pybo.urls')),
path('common/', include('common.urls')),
]
...app_name = 'common'
urlpatterns = [
]<!-- 로그인 버튼 Start -->
<div class="collapse navbar-collapse flex-grow-0" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'common:login' %}">로그인</a>
</li>
</ul>
</div>
<!-- 로그인 버튼 End -->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'),
]...
urlpatterns = [
path('login/', auth_views.LoginView.as_view(template_name='common/login.html'), name='login'),
]
...{% 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" %}
<!-- 사용자 ID 입력 -->
<div class="form-group my-3">
<label for="username">사용자ID</label>
<input type="text" class="form-control my-1" name="username" id="username" value="{{ form.username.value|default_if_none:'' }}">
</div>
<!-- 비밀번호 입력 -->
<div class="form-group my-3">
<label for="password">비밀번호</label>
<input type="password" class="form-control my-1" name="password" id="password" value="{{ form.password.value|default_if_none:'' }}">
</div>
<!-- 로그인 버튼 -->
<button type="submit" class="btn btn-primary my-2">로그인</button>
</form>
</div>
{% endblock %}로그인 뿐만 아니라 form의 필드에 적절한 값을 넣지 않는 모든 오류에 대해 원인을 표시하기 위해 프로젝트 디렉토리의 templates 디렉토리에 생성
{% 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 %}
<!-- None 필드 오류 출력 -->
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error }}</strong>
</div>
{% endfor %}
{% endif %}
필드에 잘못된 형식의 값이 입력되거나 누락되었을 때 필드오류가 발생
입력값과 관계 없이 발생하는 오류일 때 넌필드 오류 발생


...
# 로그인 성공 시 자동으로 이동할 URL
LOGIN_REDIRECT_URL = '/'...
urlpatterns = [
...
path('', views.index, name='index'), # / 페이지에 해당하는 urlpatterns
]
...로그인 구현 이후 내비게이션 바에 로그인한 계정과 로그아웃 버튼이 활성화되지 않고 그대로 로그인 버튼이 활성화된 문제가 있다. 따라서 내비게이션 바 수정이 필요하다.
templates/navbar.html 수정
...
<!-- 로그아웃 버튼 표시 -->
{% if user.is_authenticated %}
<form action="{% url 'common:logout' %}" method="post">
{% csrf_token %}
<input type="submit" value="{{ user.username }} (로그아웃)">
</form>
<!-- 로그인 버튼 표시 -->
{% else %}
<form action="{% url 'common:login' %}" method="post">
{% csrf_token %}
<input type="submit" value="로그인">
</form>
{% endif %}
...
로그아웃 URL 매핑 - common/urls.py
...
urlpatterns = [
path('login/', auth_views.LoginView.as_view(template_name='common/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
...
로그아웃 성공시 index로 이동하도록 페이지 등록 - config/settings.py
...
# 로그인, 로그아웃 성공 시 자동으로 이동할 URL
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
로그아웃 링크 출력 확인

테스트 도중 bootstrap.min.css.map과 bootstrap.min.js.map을 찾을 수 없다는 오류가 발생하여 static에 두 파일을 추가
django 5부터는 LogoutView가 POST 방식으로 동작해서 HTML 문서에 post method를 사용하도록 변경해야 한다.
장고 5 부터 LogoutView로의 요청은 POST 방식만 허용 | 파이썬 사랑방