가장 빠른 풀스택을 위한 Flask & FaskAPI 예제 연습 기록
우선 전체 코드는 아래와 같다.
from flask import Flask, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import (
LoginManager,
UserMixin,
login_required,
login_user,
logout_user,
current_user,
)
app = Flask(__name__)
# 데이터베이스 설정
app.config["SQLALCHEMY_DATABASE_URI"] = (
"mysql+pymysql://username:password@localhost:3306/db_name"
)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SECRET_KEY"] = "mysecretkey" # Flask 애플리케이션을 위한 비밀 키 설정
db = SQLAlchemy(app) # SQLAlchemy 인스턴스 생성
login_manager = LoginManager() # LoginManager 인스턴스 생성
login_manager.init_app(app) # Flask 애플리케이션과 LoginManager 인스턴스 연결
# 사용자 모델 정의
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) # 사용자의 ID, 고유 식별자
username = db.Column(
db.String(90), unique=True, nullable=False
) # 사용자 이름, 고유해야 함
email = db.Column(
db.String(120), unique=True, nullable=False
) # 사용자 이메일, 고유해야 함
password = db.Column(db.String(128)) # 사용자 비밀번호
def __repr__(self):
return f"<User {self.username}>"
# 애플리케이션 컨텍스트 안에서 데이터베이스 생성
with app.app_context():
db.create_all()
@login_manager.user_loader
def load_user(user_id):
return User.query.get(
int(user_id)
) # 주어진 사용자 ID에 해당하는 사용자 객체를 반환
@app.route("/")
def index():
return "Home Page"
@app.route("/protected")
@login_required # 로그인한 사용자만 엑세스 가능
def protected():
return f"Logged in as {current_user.username}" # 현재 로그인한 사용자의 이름을 표시
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
user = User.query.filter_by(
username=username
).first() # 데이터베이스에서 사용자 조회
if user and user.password == password:
login_user(user) # 사용자가 존재하고 비밀번호가 맞다면 로그인 처리
return redirect(url_for("protected")) # 보호된 페이지로 리디렉션
return "사용자 없음"
return """
<form method="post">
Username: <input type='text' name="username"><br>
Password: <input type='password' name="password"><br>
<input type='submit' value='login'>
</form>
"""
@app.route("/logout")
@login_required # 로그인한 사용자만 엑세스 가능
def logout():
logout_user() # 현재 사용자 로그아웃 처리
return redirect(url_for("index")) # 홈페이지로 리디렉션
@app.route("/create_test_user")
def create_test_user():
test_user = User(
username="testuser", email="test@example.com", password="testpassword"
) # 테스트 사용자 생성
db.session.add(test_user)
db.session.commit() # 데이터베이스에 테스트 사용자 추가
return "Test user created" # 사용자 생성 완료 메세지 반환
이제 테스트를 해보자.
홈페이지 ( / 라우트 )
@app.route("/")
def index():
return "Home Page"
루트 경로로 들어가면 Home Page 라고 쓰여진 홈페이지가 나온다.
테스트 사용자 생성 ( /create_test_user 라우트 )
@app.route("/create_test_user")
def create_test_user():
test_user = User(
username="testuser", email="test@example.com", password="testpassword"
) # 테스트 사용자 생성
db.session.add(test_user)
db.session.commit() # 데이터베이스에 테스트 사용자 추가
return "Test user created" # 사용자 생성 완료 메세지 반환
DB에 테스트 유저 정보가 저장이 되고 "Test user created" 메세지가 화면에 출력된다. DB를 확인해보면 유저 정보가 잘 들어간 것을 볼 수 있다.
보호된 페이지 ( /protected 라우트 )
@app.route("/protected")
@login_required # 로그인한 사용자만 엑세스 가능
def protected():
return f"Logged in as {current_user.username}" # 현재 로그인한 사용자의 이름을 표시
이 페이지는 @login_required 데코레이터로 인해 로그인된 사용자만 접근 가능하다.
로그인 된 사용자가 이 페이지에 접근하면 아래와 같이 사용자의 이름이 화면에 표시된다.
만약 로그인하지 않은 사용자가 이 페이지에 접근하면 위에서 설정한 로그인 페이지 뷰 함수를 따라 login() 함수가 실행된다. 즉 login_view = "login"을 따라 /login로 리다이렉트 되는 것이다. /login으로 GET 요청을 보내는 것이라고도 볼 수 있다.
login_manager.login_view = "login" # 로그인 페이지의 뷰 함수 이름 설정
def login():
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
user = User.query.filter_by(
username=username
).first() # 데이터베이스에서 사용자 조회
if user and user.password == password:
login_user(user) # 사용자가 존재하고 비밀번호가 맞다면 로그인 처리
return redirect(url_for("protected")) # 보호된 페이지로 리디렉션
return """
<form method="post">
Username: <input type='text' name="username"><br>
Password: <input type='password' name="password"><br>
<input type='submit' value='login'>
</form>
"""
이때 POST 요청을 보낸 것이 아니므로 form 태그에 대한 HTML 문자열이 반환되고, 로그인 폼이 화면에 나타난다.
로그인 기능 ( /login 라우트 )
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
user = User.query.filter_by(
username=username
).first() # 데이터베이스에서 사용자 조회
if user and user.password == password:
login_user(user) # 사용자가 존재하고 비밀번호가 맞다면 로그인 처리
return redirect(url_for("protected")) # 보호된 페이지로 리디렉션
return """
<form method="post">
Username: <input type='text' name="username"><br>
Password: <input type='password' name="password"><br>
<input type='submit' value='login'>
</form>
"""
로그인되지 않은 상태로 /protected 라우트에 접근해도 결국 로그인 화면이 나타나지만 /login 라우트를 직접 주소창에 입력하고 들어가면 해당 라우트로 GET 요청이 발생한 것이기 때문에 if 문이 실행되지 않고 form 태그 내용이 리턴되어 로그인 폼이 똑같이 화면에 나타난다.
폼에 사용자 정보를 입력한 후 로그인 버튼을 클릭하면 동일한 라우트로 POST 요청이 이루어지고, 사용자 정보를 DB의 내용과 비교해 존재하는 사용자인지 확인 후 로그인을 진행한다. 인증이 잘 완료되었다면 protected 페이지로 리다이렉트된다.
로그아웃 기능 ( /logout 라우트 )
@app.route("/logout")
@login_required # 로그인한 사용자만 엑세스 가능
def logout():
logout_user() # 현재 사용자 로그아웃 처리
return redirect(url_for("index")) # 홈페이지로 리디렉션
마찬가지로 @login_required 데코레이터로 인해 로그인하지 않은 사용자라면 login 폼이 나타날 것이고, 로그인된 사용자가 로그아웃을 시도하는 것이라면 로그아웃 처리 후 바로 홈페이지로 리다이렉트된다.
'Backend > Flask' 카테고리의 다른 글
[Flask] Flask-Caching을 사용해서 캐싱 구현하기 (0) | 2025.02.12 |
---|---|
[Flask] SQLAlchemy를 사용하여 회원가입 폼 DB에 저장하기 (0) | 2025.02.11 |