이제 블로그를 업그레이드 해보자. 첫 번째로 로그인 기능을 구현한다.
아무나 글을 쓰거나 하면 안되니, 로그인으로 인증기능을 구현해야한다.
accounts App 만들기
app 만들기
python manage.py startapp accounts
Bash
복사
urls.py 만들기
from django.urls import path
from . import views
urlpatterns = [
path('signup/', views.signup, name='signup'),
path('signin/', views.signup, name='signin'),
]
Python
복사
accounts\urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
import blog.views
import portfolio.views
import accounts.views
urlpatterns = [
path('admin/', admin.site.urls),
path('', blog.views.home, name="home"),
path('blog/', include('blog.urls')),
path('portfolio/', include('portfolio.urls')),
path('accounts/', include('accounts.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Python
복사
secondproject\urls.py
settings.py에 추가하는 것 잊지말기!
template 만들기
signin 기능을 위해서는 총 2개의 페이지를 만들어줘야한다. 1. 회원가입, 2. 입력페이지
그래서 templates/accounts 안에 signup.html과 signin.html 두개의 페이지를 만들어 줬다. 기본 base.html을 상속해올꺼니까 extends 블럭도 추가한다.
{% extends 'base.html' %}
{% block content %}
<h1>Sign Up</h1>
<form>
Username:
<br>
<input name="username" type="text" value="">
<br>
Password:
<br>
<input name="password1" type="password" value="">
<br>
Confirm Password:
<br>
<input name="password2" type="password" value="">
<br>
<br>
<input class="btn btn-primary" type="submit" value="Sign Up!">
</form>
{% endblock %}
HTML
복사
templates\accounts\signup.html
{% extends 'base.html' %}
{% block content %}
<h1>Sign In</h1>
<form>
Username:
<br>
<input name="username" type="text" value="">
<br>
Password:
<br>
<input name="password" type="password" value="">
<br>
<br>
<input class="btn btn-primary" type="submit" value="Login">
</form>
{% endblock %}
HTML
복사
templates\accounts\signin.html
view 만들기
간단하게 페이지만 연결해준다.
def signup(request):
return render(request, 'accounts/signup.html')
def signin(request):
return render(request, 'accounts/signin.html')
Python
복사
accounts\views.py
/accounts/signin
/accounts/signup
대충 navbar에 연결 링크를 만들어준다.
회원가입 기능 구현
template 수정하기
{% extends 'base.html' %}
{% block content %}
{% if error %}
{{ error }}
<br>
<br>
{% endif %}
<h1>Sign Up</h1>
<form method="POST" action="{% url 'signup' %}">
{% csrf_token %}
Username:
<br>
<input name="username" type="text" value="">
<br>
Password:
<br>
<input name="password1" type="password" value="">
<br>
Confirm Password:
<br>
<input name="password2" type="password" value="">
<br>
<br>
<input class="btn btn-primary" type="submit" value="Sign Up!">
</form>
{% endblock %}
HTML
복사
signup.html로 가서 form태그에 method와 action을 추가해주자.
그리고 {% csrf_token %}도 한줄 적어준다.
•
method = "POST"
“GET”방식은 url을 통해서 데이터가 넘어가는 방식이다. 따라서 회원정보와 같은 중요한 정보를 전송하면 유출되어 큰일난다. 그래서 사용하는게 “POST”방식이고, 이건 url을 통해 데이터가 이동하지 않는다.
아래는 상황별 사용되는 방식을 정리한 것이다.
RESTful - HTTP method
CRUD와 비슷하다.
•
action = {% url 'signup' %}
앞서 method를 "POST"로 지정해줬으므로 views.py에서 정의한 signup 함수를 고쳐 다른 방식으로 작동하게 할 것이다. 이 부분에 대해 비교해보고 싶으면 앞서 만들었던 wordcount 예제를 살펴보자.
•
{% csrf_token %}
이건 보안을 위해 사용하는 내용이다. django가 기본적으로 제공해주는 기능이며 form 태그 안에 적어주어야 보안을 위한 저 정보가 submit 할 때 같이 전송된다.
view 수정하기
html로 회원가입 창을 만들었으니 view로 진짜 처리를 해보자. django는 기본적으로 user와 관련된 model을 제공한다.
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib import auth
# Create your views here.
def signup(request):
if request.method == 'POST':
if request.POST['password1'] == request.POST['password2']: # 패스워드 확인
try: # 아이디 중복 여부 체크
user = User.objects.get(username=request.POST['username'])
return render(request, 'accounts/signup.html', {'error': 'Username has already been taken'})
except User.DoesNotExist:
user = User.objects.create_user(
request.POST['username'], password=request.POST['password1']
)
auth.login(request, user)
return redirect('blog')
else:
return render(request, 'accounts/signup.html', {'error': 'Passwords must match'})
else:
return render(request, 'accounts/signup.html')
Python
복사
accounts\views.py
비밀번호와 비밀번호 확인이 같은 경우 + 아이디 중복여부 체크 후 가입이 되도록 한다.
user = User.objects.create_user(username, password)
User 클래스를 기반으로 한 쿼리셋에서 유저를 만드는 함수이다. username과 password에는 아까 html에서 전달받은 정보들을 넣어주고, 회원가입 완료 후 바로 로그인 되게끔 auth.login 함수를 작성한다. 정상적으로 끝난다면 home함수로 redirect시켜준다.
로그인 기능 구현
template 수정하기
{% extends 'base.html' %}
{% block content %}
{% if error %}
{{ error }}
<br>
<br>
{% endif %}
<h1>Sign In</h1>
<form method="POST" action="{% url 'signin' %}">
{% csrf_token %}
Username:
<br>
<input name="username" type="text" value="">
<br>
Password:
<br>
<input name="password" type="password" value="">
<br>
<br>
<input class="btn btn-primary" type="submit" value="Login">
</form>
{% endblock %}
HTML
복사
templates\accounts\signin.html
view 수정하기
def signin(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(request, username=username, password=password)
if user is not None:
auth.login(request, user)
return redirect('home')
else:
return render(request, 'accounts/signin.html', {'error': 'username or password is incorrect.'})
else:
return render(request, 'accounts/signin.html')
Python
복사
accounts\views.py
login 함수를 위와 같이 수정해줍니다. 회원가입을 한 회원만 로그인이 가능한 기능을 구현하기 위해
user = auth.authenticate(request, username=username, password=password) 구문을 넣어준다. 이는 데이터베이스에서 전달받은 username과 password로 사용자가 존재하는지 판단한다. 만약 사용자가 존재한다면 로그인을 하고, 존재하지 않는다면 에러를 출력한다.
그리고 아까 signup을 테스트하며 만든 계정으로 로그인을 시도해보자. home으로 redirect 된다면 성공.
Navbar 수정
로그인을 하면 로그인 여부에 따라 sign up이나 글쓰기 버튼이 나오지 않게 해야한다. base.html로 가서 수정해준다.
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'home' %}">Blog Project</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" aria-current="page" href="{% url 'home' %}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'portfolio' %}">포트폴리오</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> 환영합니다. {{ user.username }} 님! </a>
<ul class="dropdown-menu">
<a class="dropdown-item" href="javascript:{document.getElementById('logout').submit()}">Logout</a>
<form id="logout" method="POST" action="">
{% csrf_token %} <input type="hidden" />
</form>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
글쓰기
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'write' %}">일기</a></li>
<li><a class="dropdown-item" href="{% url 'writepf' %}">사진</a></li>
<li><hr class="dropdown-divider"></li>
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'signup' %}">Sign Up</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'signin' %}">Sign In</a>
</li>
{% endif %}
</ul>
<form class="d-flex" role="search">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
HTML
복사
secondproject\templates\base.html
bootstrap을 이용해 위와 같이 수정해준다.
•
{% if user.is_authenticated %} : 로그인이 되어있는지 물어보는 코드이다.
•
logout : 버튼을 누르면 POST방식으로, csrf_token도 같이 전송해야한다. 하지만 a태그를 가지고만 해결할 방법이 없어, JavaScript의 힘을 조금 빌려 form 태그를 통해 전송하는 방법을 사용했다.
•
{% else %} , {% endif %}
로그아웃 구현
로그아웃과 관련된 view를 정의해보자.
view 추가
def signout(request):
if request.method == 'POST':
auth.logout(request)
return redirect('home')
return render(request, 'accounts/signup.html')
Python
복사
POST 방식으로 요청이 들어오면 로그아웃을 하고, 그게 아니라면 signup페이지로 보내버리는 함수
url 추가
urlpatterns = [
path('signup/', views.signup, name='signup'),
path('signin/', views.signin, name='signin'),
path('signout/', views.signout, name='signout'),
]
Python
복사
template 수정
다시 base.html로 가서 signout form 태그에 action을 추가해준다.
<form id="signout" method="POST" action="{% url 'signout' %}">
{% csrf_token %} <input type="hidden" />
</form>
HTML
복사