애플리케이션 디스패칭¶
애플리케이션 디스패칭은 WSGI 수준에서 여러 Flask 애플리케이션을 결합하는 과정을 말합니다. Flask 애플리케이션뿐만 아니라 모든 WSGI 애플리케이션을 결합할 수 있습니다. 예를 들어, Django와 Flask 애플리케이션을 동일한 인터프리터에서 나란히 실행할 수도 있습니다. 이러한 방식이 가지는 유용성은, 애플리케이션이 내부적으로 어떻게 작동하는지에 따라 달라집니다.
이 기능은 Large Applications as Packages 방식과 근본적으로 다릅니다. 애플리케이션 디스패칭은 서로 완전히 독립된 동일하거나 다른 Flask 애플리케이션들을 실행하는 데 사용됩니다. 이러한 Flask 애플리케이션들은 각각 고유한 설정을 가지며, WSGI 수준에서 요청이 디스패치됩니다.
이 문서와 함께 작업하기¶
아래의 기법과 예제는 모두 WSGI 서버로 실행할 수 있는 application 객체를 생성합니다. 개발 중에는 flask run 명령을 사용하여 개발 서버를 시작할 수 있습니다. 프로덕션 환경에 대한 자세한 내용은 :doc:`/deploying/index`를 참고하세요.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
애플리케이션 결합하기¶
완전히 분리된 애플리케이션을 동일한 Python 인터프리터 프로세스에서 함께 작동하게 하려면 :class:`werkzeug.wsgi.DispatcherMiddleware`를 사용할 수 있습니다. 이 접근법의 핵심은 각각의 Flask 애플리케이션들이 유효한 WSGI 애플리케이션들이며, 디스패치를 담당하는 미들웨어가 접두사를 기반으로 각각의 Flask 애플리케이션들을 더 큰 애플리케이션으로 디스패치한다는 점입니다.
예를 들어, 메인 애플리케이션은 / 경로에서 실행하고, 백엔드 인터페이스는 /backend 경로에서 실행할 수 있습니다.
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
서브도메인에 의한 디스패칭¶
때로는 동일한 애플리케이션을 여러 개의 다른 설정으로 실행하고 싶을 때가 있습니다. 애플리케이션이 함수 내에서 생성되고, 해당 함수를 호출하여 새로운 인스턴스를 만들 수 있다면, 이를 구현하는 것은 매우 간단합니다. 이를 위해 애플리케이션을 함수에서 새 인스턴스를 생성할 수 있도록 설계하려면 Application Factories 패턴을 참고하세요.
가장 일반적인 예로는 서브도메인별로 애플리케이션을 생성하는 경우가 있습니다. 예를 들어, 웹 서버를 설정하여 모든 서브도메인 요청을 하나의 애플리케이션으로 디스패칭한 다음, 서브도메인 정보를 사용하여 사용자별 애플리케이션 인스턴스를 생성할 수 있습니다. 서버가 모든 서브도메인을 수신하도록 설정한 후에는 간단한 WSGI 애플리케이션을 사용하여 동적으로 애플리케이션을 생성할 수 있습니다.
이와 같은 작업을 추상화하는 데 적합한 수준은 WSGI 계층입니다. 요청을 검사하고 이를 Flask 애플리케이션에 위임하는 WSGI 애플리케이션을 작성합니다. 해당 애플리케이션이 아직 존재하지 않을 경우, 동적으로 생성하고 이를 저장합니다.
from threading import Lock
class SubdomainDispatcher:
def __init__(self, domain, create_app):
self.domain = domain
self.create_app = create_app
self.lock = Lock()
self.instances = {}
def get_application(self, host):
host = host.split(':')[0]
assert host.endswith(self.domain), 'Configuration error'
subdomain = host[:-len(self.domain)].rstrip('.')
with self.lock:
app = self.instances.get(subdomain)
if app is None:
app = self.create_app(subdomain)
self.instances[subdomain] = app
return app
def __call__(self, environ, start_response):
app = self.get_application(environ['HTTP_HOST'])
return app(environ, start_response)
이 디스패처는 다음과 같이 사용할 수 있습니다:
from myapplication import create_app, get_user_for_subdomain
from werkzeug.exceptions import NotFound
def make_app(subdomain):
user = get_user_for_subdomain(subdomain)
if user is None:
# if there is no user for that subdomain we still have
# to return a WSGI application that handles that request.
# We can then just return the NotFound() exception as
# application which will render a default 404 page.
# You might also redirect the user to the main page then
return NotFound()
# otherwise create the application for the specific user
return create_app(user)
application = SubdomainDispatcher('example.com', make_app)
경로에 의한 디스패칭¶
URL의 경로를 기반으로 디스패칭하는 방식도 매우 유사합니다. 서브도메인을 판별하기 위해 Host 헤더를 참조하는 대신, 요청 경로를 첫 번째 슬래시(/)까지 검사합니다.
from threading import Lock
from wsgiref.util import shift_path_info
class PathDispatcher:
def __init__(self, default_app, create_app):
self.default_app = default_app
self.create_app = create_app
self.lock = Lock()
self.instances = {}
def get_application(self, prefix):
with self.lock:
app = self.instances.get(prefix)
if app is None:
app = self.create_app(prefix)
if app is not None:
self.instances[prefix] = app
return app
def __call__(self, environ, start_response):
app = self.get_application(_peek_path_info(environ))
if app is not None:
shift_path_info(environ)
else:
app = self.default_app
return app(environ, start_response)
def _peek_path_info(environ):
segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
if segments:
return segments[0]
return None
서브도메인에 의한 디스패칭과 이 방식의 큰 차이점은, 경로에 의한 디스패칭 방식에서는 생성 함수가 ``None``을 반환할 경우 다른 애플리케이션으로 요청을 넘긴다는 점입니다.
from myapplication import create_app, default_app, get_user_for_prefix
def make_app(prefix):
user = get_user_for_prefix(prefix)
if user is not None:
return create_app(user)
application = PathDispatcher(default_app, make_app)