장고의 로그인, 로그아웃을 도와주는 앱은 django.contrib.auth 이다. 이를 가지고 구현해보즈아!
하나의 웹 사이트에는 게시판 서비스, 블로그나 쇼핑몰과 같은 굵직한 단위의 앱들이 함께 있을 수 있기 때문에 공통으로 사용되는 기능인 로그인이나 로그아웃을 이 중의 하나의 앱에 종속시키는 것은 좋지 않다. 이러한 이유로 로그인 · 로그아웃을 "공통 기능을 가진 앱"이라는 의미의 common 앱에 구현하는 것이 좋다.
1. common 앱 생성하기
django-admin startapp common
위의 명령어를 치면 디렉터리와 파일들이 자동으로 생성된다.
// config\\settings.py
INSTALLED_APPS = [
'common.apps.CommonConfig',
]
그리고 나서 앱을 생성하면 해야 되는 일이 위와 같이 settings.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')),
]
서버에서도 common에 들어올 수 있게 등록해야 한다. common이라는 폴더로 따로 빼주었으므로 include로 하여 경로를 설정한다.
2. 로그인을 위한 기본 셋팅하기
1) 템플릿에 a태그 추가하기
// templates\\navbar.html
<li class="nav-item ">
<a class="nav-link" href="{% url 'common:login' %}">로그인</a>
</li>
템플릿에서 로그인태그를 만들어주도록 한다.
2) url 맵핑하기
우선 urls.py가 common에서 없으므로 파일을 생성하고 아래와 같이 써준다.
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'),
]
html에서 별칭을 써서 URL을 맵핑했으므로 오류가 뜨지 않도록 urls.py에서 이를 추가해야 한다.
그리고 위에서 말한 것처럼 django.contrib.auth을 사용하여 뷰를 따로 만들지 않고 사용하도록 LoginView를 사용하도록 설정하였다.
3) 로그인 템플릿으로 경로 등록하기
1)번에서 등록한 a태그를 클릭하면 아래와 같은 화면이 나온다.
위의 오류는 registration 디렉터리에 login.html 파일이 없어서 나오는 오류이다. 그런데 common 디렉터리를 생성한 이유는 이 디렉토리 안에서 로그인을 구현하고자 함이였다. 그래서 위의 경로가 아닌 common디렉토리 안에서 템플릿을 찾도록 설정할 것이다.
위의 오류를 고치기 위해 common/urls.py를 수정해야 한다.
// common.urls.py
urlpatterns = [
path('login/', auth_views.LoginView.as_view(template_name='common/login.html'), name='login'),
]
위와 같이 수정하면 에러 메시지가 수정됨을 알 수 있다.
4) 로그인 템플릿 생성하기
mkdir common
common 템플릿 디렉토리를 만들어 준다. 그리고 나서 디렉토리 안에 login.html 템플릿을 생성해준다.
{% extends "base.html" %}
{% block content %}
<div class="container my-3">
<form method="post" action="{% url 'common:login' %}">
{% csrf_token %}
{% include "form_errors.html" %}
<div class="mb-3">
<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="mb-3">
<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와 비밀번호를 입력으로 받아 로그인하는 간단한 템플릿을 생성하였다. 위에서 말했다싶이 django.contrib.auth 앱을 이용하여 로그인을 구현할 것이다. 여기에 필수 항목이 있다.
- username - 사용자명
- password - 비밀번호
5) form_errors.html 생성하기
// templates\\form_errors.html
<!-- 필드 오류와 넌필드 오류를 출력한다. -->
{% if form.errors %}
<div class="alert alert-danger">
{% for field in form %}
<!-- 필드 오류 -->
{% if field.errors %}
<div>
<strong>{{ field.label }}</strong>
{{ field.errors }}
</div>
{% endif %}
{% endfor %}
<!-- 넌필드 오류 -->
{% for error in form.non_field_errors %}
<div>
<strong>{{ error }}</strong>
</div>
{% endfor %}
</div>
{% endif %}
form_errors.html 템플릿은 로그인 실패시 로그인이 왜 실패했는지 알려주는 역할을 한다. 폼 오류에는 다음과 같이 두 가지 종류의 오류가 있다.
- 필드 오류 (field.errors) : 사용자가 입력한 필드 값에 대한 오류로 값이 누락되었거나 필드의 형식이 일치하지 않는 경우에 발생하는 오류이다.
- 넌필드 오류 (form.non_field_errors) : 필드의 값과는 상관없이 다른 이유로 발생하는 오류이다.
💡 form_errors.html 템플릿은 필드 오류와 넌필드 오류 모두를 표시하기 위해 삽입되는 템플릿이다.
3. 로그인 구현하기
1) 로그인 성공시 페이지이동하기
로그인이 성공하면 django.contrib.auth 패키지는 디폴트로 /accounts/profile/ 이라는 URL로 이동시키기에 내가 지정한 페이지로 보이고 싶다면 이를 수정해야 한다.
// config/settings.py
LOGIN_REDIRECT_URL = '/'
config/settings.py 파일에 들어가서 위의 코드를 맨 마지막 줄에 추가해준다.
2) URL 매핑 규칙 추가하기
// 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
]
/ 페이지 요청에 대해 pybo/views.py 파일의 index 함수가 실행된다.
3) 로그인 해보기
로그인을 다 구현했으면 로그인해보자! 로그인을 뭘로 하냐면 관리자에 등록된 아이디와 비밀번호가 있다면 그것을 로그인을 해보면 로그인이 되어 메인 화면을 이동되어지게 된다.
4. 로그아웃 구현하기
1) 템플릿에 추가하기
(... 생략 ...)
<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 %}
- 로그아웃 : {{ user.username }}
템플릿에서 User 사용하기
뷰함수에서 템플릿에 User 객체를 전달하지 않더라도 템플릿에서는 django.contrib.auth 기능으로 인해 User 객체를 사용할 수 있다.
- user.is_authenticated - 현재 사용자가 인증되었는지 여부 (로그인한 상태라면 true, 로그아웃 상태라면 false)
- user.is_anonymous - is_authenticated의 반대 경우 (로그인한 상태라면 false, 로그아웃 상태라면 true)
- user.username - 사용자명 (사용자 ID)
- user.is_superuser - 사용자가 슈퍼유저인지 여부
2) urls에 맵핑하기
// common\\urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
app_name = 'common'
urlpatterns = [
path('login/', auth_views.LoginView.as_view(template_name='common/login.html'), name='login'),
path('logout/', views.logout_view, name='logout'),
]
3) 뷰에 추가하기
// common/views.py
from django.contrib.auth import logout
from django.shortcuts import redirect
def logout_view(request):
logout(request)
return redirect('index')
logout_view 함수가 호출되면 django.contrib.auth 모듈의 logout 함수를 사용하여 로그아웃을 수행하고 'index' 페이지로 리다이렉트한다.