시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.
이전 내용(메모앱 1단계 - 3단계)
이전 내용(메모앱 4단계)
이전 내용(메모앱 5단계)
이전 내용(메모앱 6단계)
7단계: 사용자 편의성 향상
사용자 경험 개선을 위해, redirect() 와 url_for() 함수를 활용, 사용자의 행동에 따라 적절한 페이지로 리다이렉트하는 기능 구현
◆ 두 함수를 사용하기 위한 import 구문 수정
from flask import Flask, render_template, request, jsonify, abort, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
◆ 로그인 기능 개선
사용자가 로그인에 성공 ▶ 홈페이지로 리다이렉트
실패 ▶ 에러 메시지를 팝업으로 표시
★ 로그인 뷰 수정
app.py 파일의 @app.route('/login', ~ 영역 수정
# 로그인 뷰 정의
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = User.query.filter_by(username=request.form['username']).first()
if user and user.check_password(request.form['password']):
login_user(user)
return jsonify({'message': '로그인을 성공하였습니다. 메모 페이지로 이동합니다.'}), 200
# 에러 메시지를 JSON 형태로 반환
return jsonify({'error': '아이디가 없거나 패스워드가 다릅니다.'}), 401
return render_template(url_for('home'))
◆ 회원가입 기능 개선
회원가입 성공 ▶ 로그인 페이지로 리다이렉트
실패 ▶ 에러 메시지를 팝업으로 표시
★ 회원가입 뷰 수정
app.py 파일의 @app.route('/signup', ~ 영역 수정
# 회원 가입 뷰
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
username = request.form['username']
email = request.form['email']
password = request.form['password']
#회원가입 실패 시 에러 메시지를 JSON 형태로 반환(프런트엔드 페이지에서 해당 메시지를 기반으로 팝업을 띄움)
existing_user = User.query.filter((User.username == username) | (User.email == email)).first()
if existing_user:
return jsonify({'error': '사용자 이름 또는 이메일이 이미 사용 중 입니다.'}), 400
user = User(username=username, email=email)
user.set_password(password)
db.session.add(user)
db.session.commit()
return jsonify({'message': '회원가입이 성공하였습니다. 가입한 아이디와 패스워드로 로그인할 수 있습니다.'}), 201
return redirect(url_for('home')) #비정상 요청의 경우 리다이렉트
◆ 프런트엔드 페이지 수정
로그인 및 회원가입 성공 또는 실패에 따른 적절한 메시지를 팝업으로 표시하고, 성공 시에는 적절한 페이지로 리다이렉트home.html 수정
<!DOCTYPE html><html lang="ko"><head>
<meta charset="UTF-8">
<title> 마이 메모 앱 홈페이지</title>
<style>
body { font-family: Arial, sans-serif; }
.container {
width: 300px;
margin: auto;
border: 1px solid #ddd;
padding: 20px;
}
.form-group {
margin-bottom: 10px;
}
.form-group label, .form-group input {
display: block;
width: 100%;
}
.form-group input {
padding: 5px;
margin-top: 5px;
}
.buttons {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
</style></head><body>
<div class="container">
<h1> My Memo 앱에 오신 것을 환영합니다!</h1>
<p> 간단한 메모를 작성하고 관리할 수 있는 앱입니다.</p>
<!-- 로그인 폼-->
<form id="loginForm" action="/login" method="post">
<div class="form-group">
<label for="username">사용자 이름:</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">비밀번호:</label>
<input type="password" id="password" name="password" required>
</div>
<div class="buttons">
<input type="submit" value="로그인">
<button type="button" onclick="showSignupForm()">회원가입</button>
</div>
</form>
<!-- 회원가입 폼 (초기에는 숨겨져 있음)-->
<form id="signupForm" style="display:none;" action="/signup" method="post">
<div class="form-group">
<label for="signupUsername">사용자 이름:</label>
<input type="text" id="signupUsername" name="username" required>
</div>
<div class="form-group">
<label for="signupEmail">이메일:</label>
<input type="email" id="signupEmail" name="email" required>
</div>
<div class="form-group">
<label for="signupPassword">비밀번호:</label>
<input type="password" id="signupPassword" name="password" required>
</div>
<div class="buttons">
<input type="submit" value="회원가입">
</div>
</form>
</div>
<script>
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('/login', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(err.error); });
}
return response.json();
})
.then(data => {
alert(data.message);
// 성공 시 페이지 리디렉션
window.location.href = '/memos';
})
.catch(error => {
alert('로그인 실패: ' + error.message);
});
});
document.getElementById('signupForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('/signup', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(err.error); });
}
return response.json();
})
.then(data => {
alert(data.message);
// 회원가입 성공 시 로그인 페이지로 리디렉션
window.location.href = '/';
})
.catch(error => {
alert('회원가입 실패: ' + error.message);
});
});
function showSignupForm() {
// 로그인 폼 숨기기
document.getElementById('loginForm').style.display = 'none';
// 회원가입 폼 보이기
document.getElementById('signupForm').style.display = 'block';
}
</script>
</body>
</html>
이제 실행하면
<회원가입> 버튼을 누르면
◆ 로그아웃 기능 개선
사용자가 로그아웃할 때 홈페이지로 리다이렉트
★ 로그아웃 뷰 수정
app.py 파일의 @app.route('/logout', ~ 영역 수정
# 로그아웃 뷰 정의
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('home')) #로그아웃 후 메인 페이지로 리다이렉트
◆ 테스트
먼저 vscode 의 app.py 파일의 터미널에서 'flask run' 입력 후
http://127.0.0.1:5000/ 접속
★ 회원가입
★ 로그인
★ 로그인 후 /memos
메모를 확인할 수 있는 페이지에서는 상단 좌측에 로그인한 아아디가 표시되며 아이디별 메모를 추가, 수정, 삭제할 수 있다.
(저장된 메모는 리스트 형태로 표시되며, 우측 상단의 로그아웃을 통해 로그아웃 가능)
★ 로그아웃
<로그아웃> 버튼을 누르면 메인 페이지로 이동한다. (메모 페이지는 로그인한 사용자만 접속할 수 있음을 확인할 수 있다)
다음 내용
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] Fast API란? (0) | 2024.08.15 |
---|---|
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(6) (0) | 2024.08.15 |
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(4) (0) | 2024.08.13 |
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(3) (0) | 2024.08.08 |
[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(2) (0) | 2024.07.25 |