Flask
Flask
- Flask는 ‘micro’웹 프레임워크입니다.
- 즉 Django와 달리 최소한의 구성 요소와 요구 사항을 제공하기 때문에 시작하기 쉽고 필요에 따라 유연하게 사용할 수 있습니다.
- 하지만 완전한 기능을 갖춘 앱을 만들기에 제약이 있다는 뜻은 아니고 오히려 쉽게 확장할 수 있도록 설계되어 있습니다.
install
pip install flask
Hello Flask
from flask import Flask
app = Flask(__name__) # Flask 객체 생성
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
if __name__ == "__main__":
app.run(debug=True, port=5000)
Flask를 처음 시작할 때 생성하는 hello world 출력 코드입니다.
정말 간단한 코드지만 Flask의 기본적인 구조를 잘 보여주고 있습니다.
app은 Flask객체입니다. 원하는 기능을 추가하고 싶을 때 이 app에 기능을 추가해주면되고, 특정 url에 기능을 추가하고 싶을 경우에도 app을 기준으로 url을 추가해준 뒤 기능을 구현해주면 됩니다.
아무튼 이렇게 실행을 하면 app.run()
을 통해 http://127.0.0.1:5000/
주소로 app이 배포가 시작됩니다.
5000번 port는 flask에서 실행한 서버의 포트이며, debug=True
개발용으로 실행한다는 뜻으로 프로젝트에서 변경된 코드가 생길 시 자동으로 변경된 코드를 적용해서 서버를 다시 시작해주는 기능을 갖고 있습니다.
Route
Flask를 사용시 어쩌면 가장 중요하다고 할 수 있는 route기능입니다.
from flask import Flask
app = Flask(__name__)
@app.route('/')
@app.route('/home')
def home():
return 'Hello, World!'
@app.route('/user')
def user():
return 'Hello, User!'
if __name__ == '__main__':
app.run(debug=True)
route는 말 그대로 경로를 지정해주는 기능입니다.
이게 무슨말이냐. 보통 웹에 배포를 시작할 경우 다양한 기능들(로그인, 검색, 메뉴 등등)을 제공해야 하는데 http://127.0.0.1:5000/
이라는 주소 하나만으로는 모든 기능을 처리할 수 없습니다.
그렇다고 저 주소안에 여러기능들을 다 쑤셔넣을 수도 없으니까요.
이 app.route
데코레이터를 이용해 url의 suffix를 추가해줄 수 있습니다.
위에 코드를 예시로 들어보면 home 함수에는 /home
, /
이라는 문자가 route 데코레이터를 통해 추가된 것을 확인할 수 있습니다.
이 말은 http://127.0.0.1:5000/
또는 http://127.0.0.1:5000/home
에 접속할 경우 home 함수를 실행하라는 뜻입니다.
마찬가지로 http://127.0.0.1:5000/user
에 접속 시 user함수가 실행되는 것을 알 수 있습니다.
Blueprint
굉장히 중요한 기능인 이 route는 기능이 필요할 때마다 계속 추가되어야 하기 때문에, 함수가 많을 경우 번거로워질 수 있습니다.
예를 들면 동일하게 /menu
로 시작하는 url이 있는 경우 2~3개면 상관없겠지만 실제 프로젝트에선 10개가 넘어갈 수도 있는데 전부 하나씩 menu를 달아주는 것은 비효율적입니다.
이런 상황에서 blueprint를 이용하면 route함수들을 보다 구조적으로 관리할 수 있게 됩니다.
Blueprint 객체
# app.py
from flask import Flask
app = Flask(__name__)
from .views import main_views
app.register_blueprint(main_views.bp)
# views\main_views.py
from flask import Blueprint
bp = Blueprint('main_views', __name__, url_prefix='/view')
@bp.route('/hello')
def main_view():
return 'main view'
- blueprint 객체 생성
- Flask 객체 app에서 blueprint객체를 register해준다.
- controller route에서 blueprint 객체를 써준다.
최종적으로 hello_pybo함수의 url은 127.0.0.1:5000/view/hello
가 됩니다.
이런 식으로 blueprint를 사용하여 url을 구조적으로 관리해줄 수 있습니다.
app teardown appcontext
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
app.teardown_appcontext
데코레이터는 클라이언트의 요청이 종료됐을 경우에 자동으로 실행되도록 하는 함수입니다.
위 함수를 읽기만해도 알 수 있듯이 클라이언트의 요청이 종료될 경우 해당함수가 실행돼 db가 종료되도록 한다는 뜻입니다.
매우 간단하지만 중요한 개념이기 때문에 언급하고 넘어가겠습니다.
Database
이번엔 flask에 database를 연동하는 방법을 알아보겠습니다.
흔히들 python에 database에 연동할 때 각 sql을 지원하는 라이브러리의 connect()함수를 사용하여 연동했을겁니다.
# ex) mysql 연결
import pymysql
conn = pymysql.connect(
user='root',
passwd='{설정한 비밀번호}',
host='127.0.0.1',
db='juso-db',
charset='utf8'
)
이런 식으로 간단하게 연동한 뒤 원하는 sql명령을 처리해 db에 적용했습니다.
하지만 flask는 보통 배포를 위해 사용되고 또한 배포되는 서버는 다수의 사용자가 사용하게 됩니다. 위와 같은 방식으로 구현한다면 1만명의 사용자가 접속했을 때 1만번의 db연결을 해줘야 됩니다.
즉 매우매우 비효율적이고, 속도도 느립니다.
이를 해결하기 위해 미리 db session을 저장해두는 pooling방식을 사용합니다.
flask-sqlalchemy
하지만 저희가 굳이 pooling을 구현할 필요는 없습니다. 이미 pooling방식으로 간단하게 데이터베이스를 연동할 수 있는 sqlalchemy 라이브러리가 존재하고,
더 나아가 flask를 위해 구현되어 편리하게 app객체에 db옵션을 추가해줄 수 있고, 클라이언트 요청이 종료되면 자동으로 db_session을 pool에서 삭제시켜주기까지 하는 flask-sqlalchemy라는 라이브러리가 존재합니다.
config
https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/#configuration-keys
flask-sqlalchemy 라이브러리를 사용하면 flask 객체에 sqlalchemy에 대한 설정을 해줄 수 있습니다.
전체 config속성은 위 링크에 접속하셔서 확인해주시면 되고, 그 중 중요하다고 생각되는 기능들을 알아보겠습니다.
from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://{user_name}:{password}@{host}:{port}/{database_name}"
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_POOL_SIZE'] = 30
app.config['SQLALCHEMY_MAX_OVERFLOW'] = 20
app.config['SQLALCHEMY_POOL_RECYCLE'] = 60
사용법은 간단합니다. 생성된 flask객체의 config함수에서 원하는 속성값을 지정해주면 됩니다.
SQLALCHEMY_DATABASE_URI
: 연결에 사용되는 데이터베이스 URI (어떤 sql을 사용하느냐에 따라 주소의 형태가 다릅니다.)SQLALCHEMY_COMMIT_ON_TEARDOWN
: 클라이언트의 request 마지막단에서 데이터베이스의 변경사항을 자동으로 커밋하는 기능SQLALCHEMY_POOL_SIZE
: pool 크기를 지정합니다.SQLALCHEMY_MAX_OVERFLOW
: pool overflow 크기를 지정합니다.SQLALCHEMY_POOL_RECYCLE
: pool에 존재하는 session이 자동으로 recycle되는 시간을 지정합니다.
db연결
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
db.init_app(app)
flask_sqlalchemy의 SQLAlchemy 클래스 객체를 생성해주고 init_app을 앞서 설정했던 flask app과 연결해주면 매우 쉽게 연동이 완료됩니다.
자 이제 SQLAlchemy를 이용해 새로운 table을 생성해보겠습니다.
class Test(db.Model):
__tablename__ = 'test'
user_id = db.Column(db.String(20), primary_key=True)
name = db.Column(db.String(20))
def __init__(self, user_id, name):
self.user_id = user_id
self.name = name
db.Model 클래스를 상속받는 Test라는 클래스를 생성해봤습니다.
이 Test클래스는 하나의 table입니다.
__tablename__
: 이름에서 알 수 있듯이 테이블명입니다.
user_id = db.Column(db.String(20), primary_key=True)
: user_id라는 컬럼을 만드는데 String type에 길이는 20이며, primary_key로 지정하겠다는 의미입니다.
이런 식으로 원하는 테이블과 column들을 원하는 대로 만들어줄 수 있으며 자유롭게 코드 내부에서 객체로 생성해 사용가능합니다.
다만 실제로 sql내부에 생성하려면 별도의 작업이 필요합니다.
아까 db를 연결했던 코드에서 단 한줄만 추가해주면 됩니다.
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
db.init_app(app)
db.create_all()
db.create_all()
을 사용하면 db.Model을 상속받은 클래스들이 실제로 sql내부에 존재하는지 여부를 확인하고 존재한다면 자동으로 생성해줍니다.
기본적인 db응용
SELECT
db.session.query(Test).all() #SELECT * FROM test
db.session.query(Test.user_id).all() #SELECT user_id FROM test
WHERE
filter
를 사용합니다.
db.session.query(Test).filter(Test.user_id == 'sooftware').all()
db.session.query(Test).filter(Test.user_id == 'sooftware', Test.name == 'Soohwan Kim').all()
INSERT
test = Test('sooftware', 'Soohwan Kim')
db.session.add(test)
db.session.commit()
sql에 대해 자세히 모르더라도 객체의 함수로 편리하고 쉽게 처리가 가능합니다.
Subscribe to SOOFTWARE
Get the latest posts delivered right to your inbox