2025. 1. 16. 03:12ㆍCalender Website
<HTML>
기본적인 템플릿은 이전에 만들었던 register-form을 참고하였다.
<div class="form-container">
<form id="login-form" class="login-form" method="post">
<div class="form-group">
<label for="user-email">이메일</label>
<input type="email" id="user-email" class="login-input" name="user-email" placeholder="Email" required>
</div>
<div class="form-group">
<label for="user-pwd">비밀번호</label>
<input type="password" id="user-pwd" class="login-input" name="user-pwd" placeholder="Password" required>
</div>
<div class="button-div">
<button type="submit">로그인</button>
</div>
</form>
</div>
<Flask>
1. flask-login 라이브러리를 가상머신 활성화 후 설치
source venv/Scripts/activate # 가상머신 활성화
pip install flask-login # flask-login 설치
pip list --format=freeze > requirements.txt # 설치된 라이브러리 requirements.txt에 저장 (배포용)
deactivate # 가상머신 비활성화
2. app/__init__.py
LoginManager()객체를 불러와 초기화시키고, load_user 콜백함수를 등록한다.
- load_user(user_id)의 핵심 역할은 사용자 ID로 데이터베이스에서 사용자 객체를 검색하여 반환하는 것이다.
- Flask-Login은 이 함수에서 반환된 객체를 기반으로 current_user를 설정한다. 이 객체를 템플릿에 넘겨주면 해당 페이지에서 사용자 정보를 열람, 수정할 수 있다.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config
from flask_login import LoginManager
db = SQLAlchemy()
login_manager = LoginManager()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
# 데이터 베이스 연결
db.init_app(app)
# 로그인 관리
login_manager.init_app(app)
# load_user 콜백 함수
from app.models import User
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# 블루프린트 연결
from app.routes.views import views
from app.routes.api import api
app.register_blueprint(views)
app.register_blueprint(api)
return app
+ 추가
login_required에서 로그인이 되어있지 않으면 welcome 페이지로 넘어갈 수 있도록 해준다.
이제 profile_setting과 같은 페이지를 로그인이 되지 않은 상태로 접근할 수 없다.
# login_requried에서 로그인이 되어 있지 않을 경우 핸들링
@login_manager.unauthorized_handler
def unauthorized_callback():
return redirect("/welcome")
3. models/model.py
데이터베이스와 연결할 User 모델을 수정한다. UserMixin은 Flask-login에서 요구하는 사용자 속성과 기능을 제공하는 역할을 하며 is_authenticated(), is_active(), is_anonymous(), get_id() 등 Flask-Login이 필요로 하는 메서드들을 자동으로 구현한다.
from app import db
from datetime import datetime
from flask_login import UserMixin
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
phone = db.Column(db.String(20))
birth = db.Column(db.Date)
interest = db.Column(db.String(200))
work = db.Column(db.String(100))
location = db.Column(db.String(100))
image_path = db.Column(db.String(200))
website = db.Column(db.String(200))
description = db.Column(db.String(500))
4. routes/api.py
이제 로그인 기능을 구현한다. HTML에서 이메일, 비밀번호를 받아와 email이 동일한 유저를 찾는다.
- 유저 o, 비밀번호 일치 o : 로그인 성공, 메인 페이지로 이동
- 유저 o, 비밀번호 일지 x : 비밀번호 불일치, 페이지 이동 x
- 유저 x : 이메일 불일치, 페이지 이동 x
*****
만약 HTML에서 form actione='/login'으로 하여 바로 Flask와 통신하면 비동기 통신이 아닌 동기 방식으로 이루어져 redirect, render_template과 같이 페이지를 새로고침해야 한다.
HTML -> Javascript -> Flask -> Javascript 처럼 비동기 통신인 AJAX 통신을 사용하면 페이지를 새로 고침하지 않아도 되어 로그인 실패 알람과 같은 기능을 구현하기 좋다.
*****
@api.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
email = request.form.get('user-email')
password = request.form.get('user-pwd')
user = User.query.filter_by(email=email).first()
if user:
if check_password_hash(user.password, password):
login_user(user)
return jsonify({
'flag' : True,
'message' : 'Timely에 오신걸 환영합니다!',
})
else:
return jsonify({
'flag' : False,
'message' : '비밀번호를 다시 확인해주세요.',
})
else:
return jsonify({
'flag' : False,
'message' : '이메일을 다시 확인해주세요.',
})
return render_template('sign_in.html')
<Javascript>
로그인 결과 알람을 위하여 Toastify를 사용했다. 로그인 성공 시 알람을 띄우고 프로필 세팅 페이지로 이동하고, 로그인 실패 시 알람을 띄우고 해당 페이지를 그대로 유지한다.
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('.login-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
axios.post('/login', formData)
.then(function(response) {
if (response.data.flag) {
Toastify({
text: response.data.message,
duration: 1000,
gravity: "top",
position: "top",
style: {
background: "black",
}
}).showToast();
setTimeout(() => {
window.location.href = '/main';
}, 1000);
}
else {
Toastify({
text: response.data.message,
duration: 2000,
gravity: "top",
position: "top",
style: {
background: "black",
}
}).showToast();
}
})
.catch(function(error) {
console.error('Error', error)
});
});
});
로그인 흐름
1. flask-login을 이용하여 로그인하면 세션에 사용자의 아이디가 저장되어 session['user_id'] = user_id와 같이 관리된다.
2. 클라이언트가 서버에 요청할 때, flask-login을 session['user_id']를 이용해 load_user 콜백 함수를 호출한다.
3. load_user는 user_id를 검색해 데이터베이스에서 User 객체를 반환한다.
4. 반환된 User 객체는 current_user로 설정된다.
5. current_user를 템플릿에 넘기면 해당 페이지에서 사용자의 정보를 열람할 수 있다.
1. 'app/routes/views.py'
메인 페이지는 로그인된 유저만 접근 가능하도록 @login_required 데코레이터를 붙여 접속하기전 로그인이 되었는가 확인하고 user=current_user로 현재 로그인된 유저를 함꼐 넘겨 준다.
@views.route('/main')
@login_required
def main():
return render_template('calender_v2.html', user=current_user)
2. calender_v2.html
Jinja2 템플릿을 활용하여 사용자 정보를 표시할 수 있다. 만약 프로필 이미지가 저장되어 있지 않으면 기본 프로필을 적용할 수도 있다.
<div class="user-info">
<img src="{{ url_for('static', filename='images/profile/woman.png' if user.image_path == ''
else user.image_path) }}" class="profile-picture">
<div>
<p class="user-name">{{ user.username }}</p>
<p class="description">{{ user.description }}</p>
</div>
</div>
결과 화면
로그인 페이지

이메일 불일치

비밀번호 불일치

로그인 성공

메인 페이지 프로필 (프로필 x / 프로필 o)


'Calender Website' 카테고리의 다른 글
| [Javascript] 기본 캘린더 구현 (0) | 2025.01.25 |
|---|---|
| [Flask] 회원 정보 수정, 탈퇴, 로그아웃 (0) | 2025.01.23 |
| [Flask] Blueprint로 웹 애플리케이션 구조화 (0) | 2025.01.14 |
| [Flask] 플라스크로 회원가입 구현하기 (0) | 2025.01.14 |
| [Flask] 프로필 이미지 미리보기 및 데이터 베이스에 저장하기 (0) | 2025.01.14 |