Flask 앱 팩토리 — 애플리케이션 생성 함수
Flask 앱 팩토리 — 애플리케이션 생성 함수
이미 패키지와 블루프린트를 쓰고 있다면(Modular Applications with Blueprints), 경험을 더 좋게 만드는 몇 가지 방법이 있어요. 흔한 패턴은 블루프린트가 import 될 때 앱 객체를 만드는 것이에요. 하지만 이 객체 생성을 함수 안으로 옮기면, 나중에 이 앱의 여러 인스턴스를 만들 수 있어요.
왜 이런 방식을 쓰고 싶을까요? 두 가지 이유가 있어요.
- 테스팅 — 서로 다른 설정을 가진 앱 인스턴스를 만들어 모든 경우를 테스트할 수 있어요.
- 여러 인스턴스 — 같은 앱의 다른 버전을 실행하고 싶다면, 웹서버에서 여러 config를 가진 인스턴스를 만들 수도 있지만, 팩토리를 쓰면 같은 앱 프로세스 안에서 같은 애플리케이션의 여러 인스턴스를 띄울 수 있어요.
그럼 어떻게 구현할까요?
기본 팩토리
아이디어는 함수 안에서 앱을 셋업하는 거예요.
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from yourapplication.model import db
db.init_app(app)
from yourapplication.views.admin import admin
from yourapplication.views.frontend import frontend
app.register_blueprint(admin)
app.register_blueprint(frontend)
return app
단점은 블루프린트를 import 할 때 앱 객체를 쓸 수 없다는 것이에요. 하지만 요청 안에서는 쓸 수 있어요. 설정이 있는 앱에 어떻게 접근할까요? current_app을 쓰면 돼요.
from flask import current_app, Blueprint, render_template
admin = Blueprint('admin', __name__, url_prefix='/admin')
@admin.route('/')
def index():
return render_template(current_app.config['INDEX_TEMPLATE'])
여기서 설정에서 템플릿 이름을 찾아와요.
팩토리와 확장
확장과 앱 팩토리를 만들 때, 확장 객체가 처음부터 앱에 바인딩되지 않도록 하는 것이 좋아요. Flask-SQLAlchemy를 예로 들면, 이런 방식은 피해야 해요.
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
db = SQLAlchemy(app)
대신, model.py(또는 그에 준하는)에서는 이렇게 두고:
db = SQLAlchemy()
application.py(또는 그에 준하는)의 create_app에서는 db.init_app(app)으로 연결해요.
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from yourapplication.model import db
db.init_app(app)
이 디자인 패턴을 쓰면 확장 객체에 앱별 상태가 저장되지 않아서, 하나의 확장 객체를 여러 앱에 쓸 수 있어요.
앱 실행하기
팩토리로 만든 앱은 flask 명령으로 실행할 수 있어요.
$ flask --app hello run
Flask는 팩토리 이름이 hello 안의 create_app이나 make_app이면 자동으로 감지해요. 팩토리에 인자를 전달할 수도 있어요.
$ flask --app 'hello:create_app(local_auth=True)' run
그러면 hello의 create_app 팩토리가 local_auth=True 키워드 인자로 호출돼요.
정리하면, 앱 팩토리는 "앱 생성을 함수로 만들어 여러 설정·여러 인스턴스로 재사용"하는 패턴이에요. 확장은 미리 바인딩하지 않고 init_app(app)으로 연결하고, 블루프린트 안에서는 current_app으로 앱에 접근해요.