우선 회원가입과 로그인의 html 코드를 보면 둘 다 method 방식이 POST인 것을 확인할 수 있다.
그래서 로그인과 관련된 내용을 다루는 auth.py 파일에서 로그인과 회원가입 부분을 수정해준다.
method = ['GET' , 'POST']를 써줌으로써
POST 요청을 처리할 수 있도록 한다.
Request로 폼에서 데이터 받아오기
위 코드에서 name 속성은 폼이 제출된 후 서버에서 데이터를 참조하기 위해서 사용된다.
플라스크의 request해준 뒤 데이터를 받아오는 것을 확인해 보기 위해 auth.py를 수정해준다.
여기서 쓰인 request.form.get은 request 한 데이터를 가져오는 기능을 수행한다. 여기서 파라미터가 GET인 경우에는 request.args로 접근하고 POST인 경우는 request.form으로 접근한다. request.values로 둘 다 접근하는 방식도 가능하다.
이제 회원가입 페이지에서 임의의 값을 입력하게 되면
터미널에 왼쪽과 같이 데이터를 받아온 것을 확인할 수 있다.
Flask-SQLAlchemy를 이용한 데이터베이스 처리
우리는 이전 Ep02에서 SQLite3을 이용해서 데이터베이스를 만들고 명령어를 이용하여 테이블을 만든 뒤 데이터를 넣어주는 식으로 데이터베이스에 접근하는 방식을 사용한 적이 있다. 그러나 이러한 방식은 복잡하다는 단점이 있다. 그래서 이번에는 다른 방식을 사용할 것이다.
이번에는 SQLAlchemy를 사용할 것이다. SQLAlchemy를 알기 전에 먼저 ORM이란 것을 알아야 하는데 간단하게 말하면 객체와 데이터베이스의 데이터를 매핑해주는 것을 의미하는데 SQLAlchemy가 이러한 ORM의 한 종류이다.
두 용어에 대한 자세한 내용은 Ep03 앞부분에 정리되어 있다.
데이터베이스 설정을 해주기 위해 __init__.py 파일에 코드를 추가해준다.
다음으로는 파이썬 클래스로 테이블을 만드는 작업을 할 것이다. 우선 models.py파일을 만들어 아래 코드를 작성해준다.
코드를 보면 id는 primary_key = True로 기본키로 설정해주고, email과 username는 unique=True를 사용하여 중복이 없도록 한 것을 알 수 있다. 다음으로 데이터베이스가 만들어지기 전에 models.py에서 작성한 User 모델을 먼저 등록해야 되기 때문에 __init__.py에서 코드를 추가해준다.
Flask-LoginManager() 이용해서 로그인 준비하기
Flask-Login은 로그인 기능을 쉽게 구현할 수 있도록 도와주는 라이브러리이고, LoginManager는 Flask-Login 라이브러리의 로그인 관련 기능을 담고 있는 로그인 객체이다. Flask 객체로 생성한 어플리케이션을 연결하는 역할을 수행한다.
__init__.py 파일에서 아래 코드를 추가해 준다.
login_view 설정을 통해서 로그인하지 않고 로그인이 필요한 곳에 접근하려 하면 auth.login 즉, 로그인 페이지로 리디렉션 하게 된다. 만약 설정이 되어있지 않으면 401 오류 발생과 함께 중단된다. 이로써 로그인을 위한 준비는 다 되었다.
회원가입 처리
회원가입을 처리할 때 이메일이나 유저 이름의 중복 등 몇 가지 신경 써야 할 조건들이 있다. 그래서 폼에서 받아온 데이터가 유효한지 검사가 필요하다. 그래서 폼 모듈을 이용하여 데이터 검증을 조금 더 쉽게 할 수 있는 작업을 할 것이다.
우선 pip install flask-wtf.pip install email-validator를 터미널에 입력해 준 뒤, forms.py 파일을 만들어 아래 코드를 작성해준다.
WTForms는 Python 웹 개발을 위한 유연한 양식 유효성 검사 및 렌더링 라이브러리이다. forms.py에 작성한 코드를 통해서 회원가입할 때의 조건들을 설정할 수 있다. 다음으로 auth.py 파일에 코드를 아래와 같이 수정해준다.
수정한 코드로 인해서 POST로 전송된 데이터와 기존의 데이터를 비교하여 존재하는지 확인하고 존재하지 않을 경우 입력받은 데이터를 추가하여 저장한 뒤 메인 페이지로 리다이렉트 하게 되어 회원가입이 되게 되는 것이다.
폼 코드 아래에 위의 코드를 입력해 주어야 폼이 작동한다.
비밀번호 해싱하기
폼에서 받아온 데이터를 데이터베이스에 저장하는 것까진 가능해졌다. 하지만 데이터베이스에 접근을 하게 되면 회원 가입한 모든 유저의 비밀번호가 어떤 것인지 알게 된다. 그래서 회원가입 시 암호화하여 데이터베이스에 저장하는 기능을 추가해야 한다. 이것을 hashing이라고 한다.
먼저, auth.py 파일 위에 from werkzeug.security import generate_password_hash, check password_hash를 입력해준다.
그다음 위와 같이 코드를 수정해준다. generate_password_hash는 입력받은 비밀번호를 값 그대로 저장하지 않고 암호화하여 저장할 때 쓰는 함수이다. 암호화한 데이터는 복호화할 수 없기 때문에 입력받은 비밀번호를 암호화하여 기존 값과 비교하기 위해서 사용한 것이다. 실행결과는 다음과 같다.
password 부분에 성공적으로 암호화가 된 것을 확인해 볼 수 있다.
로그인 처리하기
forms.py에 로그인폼 코드를 넣어준다.
다음으로 auth.py 파일도 아래와 같이 수정해준다. 이때 from flask_login import login_user를 써주어야 한다.
로그인 기능도 회원가입과 마찬가지로 밑에 코드를 폼에 작성해 주어야 한다.
로그아웃 처리하기
auth.py 파일에서 logout부분을 아래처럼 수정해준다. 이때 from flask_login import login_required, logout_user를 써주어야 한다.
수정된 코드를 통해서 로그아웃 하기 위해서 로그인이 되어있는지 login_required를 통해 확인이 가능하고 logout_user()를 통해 로그아웃 후 블로그 홈으로 리디렉트 할 수 있다.
오류 메세지 나타내기
base.html에서 </nav> 밑에 아래 코드 추가하기
실행하게 되면 오류 메세지가 위에 뜨는데 그 내용이 오류 났을 경우 출력되도록 설정한 내용과 같은데 코드를 보면 카테고리로 분류를 한 것을 볼 수 있다.
오류 메세지가 뜨는 것을 확인했으니 이제 디자인에 맞추어 메세지가 뜨도록 {% block header %} {% endblock% } 밑에
아래와 같이 코드를 수정한다.
실행을 해보면 출력되는 메세지는 같지만 디자인이 달라진 것을 확인해 볼 수 있다.
__init__.py 와 views.py를 아래처럼 수정해준다.
동적으로 변하는 navbar 만들기
로그인을 했을 경우 "Welcome! 닉네임" 처럼 사용자 이름이 뜨도록 하거나, 로그인 링크가 보이도록 설정을 해보자
바뀌는 부분은 위에 두 개이다. 로그인을 했을 경우에는 Sign Up 대신 "Welcome! username "이 표시가 되고 Login 대신 Logout이 표시가 되어야 한다,
일단 username이 표시가 되기 위해서는 auth.py 파일에서 로그인했을 때 현재 로그인한 유저의 정보를 넘겨주어야 한다.
그래서 아래처럼 코드를 수정해 준다. 이때 from flask_login import current_user를 써주어야 한다.
네비게이션 바는 base.html에 포함되어 있으므로 모든 템플릿에 user가 변수로 전달되어야 한다. 그래서 auth.py 와 views.py에 아래와 같이 코드를 수정해 주어야 한다.
from flask import Blueprint, render_template
from flask_login import current_user
views = Blueprint("views", __name__)
@views.route('/')
@views.route('/home')
def blog_home():
return render_template("index.html", user=current_user)
@views.route('/about')
def about():
return render_template("about.html", user=current_user)
@views.route('/contact')
def contact():
return render_template("contact.html", user=current_user)
@views.route('/category')
def category():
return render_template("category.html", user=current_user)
from flask_login import login_user, logout_user, current_user, login_required
from flask import Blueprint, render_template, redirect, request, flash, url_for
from . import db
from blog.forms import SignupForm, LoginForm
from blog.models import User
from werkzeug.security import generate_password_hash, check_password_hash
auth = Blueprint("auth", __name__)
@auth.route('/login', methods = ['GET', 'POST'])
def login():
form = LoginForm()
if request.method == "POST" and form.validate_on_submit():
password = form.password.data
user = User.query.filter_by(email=form.email.data).first()
if user:
if check_password_hash(user.password, password):
#check_password_hash : 입력받은 password와 기존 password를 비교하여 참 거짓 반환
flash("Logged in!", category='success')
login_user(user, remember=True) #사용자 정보를 session에 저장
return redirect(url_for('views.blog_home'))
else:
flash("Password is incorrect!", category='error')
else:
flash("Email does not exist...", category='error')
return render_template("login.html", form=form, user=current_user)
@auth.route('/logout')
@login_required
#특정 요청을 실행하기 전 로그인이 필요한 기능에서 요청을한 사용자가 로그인된 사용자인지 확인한다.
def logout():
logout_user()
#flask_login의 함수로 session 정보를 삭제한다.
return redirect(url_for("views.blog_home"))
@auth.route('/sign-up', methods = ['GET', 'POST'])
def signup():
form = SignupForm()
if request.method == "POST" and form.validate_on_submit(): #전송된 폼 데이터의 정합성을 점검
signup_user = User(
email = form.email.data, #입력받은 데이터를 변수에 저장
username = form.username.data,
password = generate_password_hash(form.password1.data),
) #폼으로부터 검증된 데이터 받아오기
email_exists = User.query.filter_by(email=form.email.data).first()
username_exists = User.query.filter_by(username=form.username.data).first()
#폼에서 받아온 데이터가 데이터베이스에 이미 존재하는지 확인
#filter_by : 함수에서 ()안에 있는 조건에 맞는 데이터를 반환
#first()인 경우 첫번째에 매칭되는 데이터만 반환/전부 받아오려면 all() 사용
if email_exists:
flash('Email is already in use...', category = 'error')
elif username_exists:
flash('Username is already in use...', category = 'error')
#이메일과 유저이름 중복 검사
else:
db.session.add(signup_user)
db.session.commit()
flash("User Created!")
return redirect(url_for("views.blog_home"))
#중복이 아니면 유저의 정보를 추가후 저장, 그리고 home으로 리다이렉트
return render_template("signup.html", form=form, user=current_user) #GET요청을 보내면 회원가입 템플릿으로 넘어감
그 다음 base.html에서 아래처럼 코드를 수정해 준다. 여기서 사용한 is_authenticated는 로그인한 사용자인지 확인하는 기능을 수행한다. 그래서 로그인을 했으면 유저이름과 로그아웃이 표시되고 아니면 회원가입과 로그인이 표시되는 것이다.
'Flask-Study' 카테고리의 다른 글
Ep06 : 블로그 웹 애플리케이션 개발(3) - 댓글 CRUD, 게시물 삭제 처리, 간단한 contact form 구현하기 (0) | 2022.08.13 |
---|---|
Ep05 : 블로그 웹 애플리케이션 개발(2) - 테스트 코드 도입 / 관리자 페이지 / 카테고리 / 게시물 / 다루기 (0) | 2022.08.01 |
Ep03 : 블로그 웹 애플리케이션 개발(0) - 프로젝트 생성, 패키지 설치, 기본작업 (0) | 2022.07.16 |
Flask란? (0) | 2022.07.12 |
Ep02-2 : Python에서 데이터 베이스 접근하기 (0) | 2022.07.10 |