본문 바로가기

Python/Flask

Flask Application Context

애플리케이션 컨텍스트는 요청, CLI 명령 중 애플리케이션 레벨 데이터를 추적한다. 각 함수에 애플리케이션을 전달하는 대신 current_app, g 프록시가 대신 접근된다.

요청 중 요청 레벨 데이터를 추적하는 요청 컨텍스트와 유사하다. 요청 컨텍스트가 푸시되면 같은 애플리케이션 컨텍스트가 푸시된다.

컨텍스트의 목적

플라스크 애플리케이션 객체는 뷰, CLI 명령 내에서 접근하기 유용한 config라는 속성을 갖는다. 그러나 프로젝트 모듈 내에 app 인스턴스를 임포트하는 것은 순환 참조 문제가 발생하기 쉽다. 앱 팩토리 패턴을 사용하거나, 재사용 가능한 blueprint, 확장 프로그램을 작성할 때 앱 인스턴스를 임포트할 필요가 없다.

플라스크는 이 문제를 애플리케이션 컨텍스트로 해결했다. app을 바로 참조하는 대신, 현재 활동을 다루는 애플리케이션을 가리키는 current_app 프록시를 사용한다.

플라스크는 @app.cli.command() 를 써서 Flask.cli에 등록된 CLI 명령을 실행할 때 자동으로 앱 컨텍스트를 푸시한다.

컨텍스트의 일생

애플리케이션 컨텍스트는 필연적으로 생성되고 소멸된다. 플라스크 애플리케이션이 요청을 다루기 시작할 때, 애플리케이션 컨텍스트와 요청 컨텍스트를 푸시한다. 요청이 끝날 때 요청 컨텍스트를 팝하고 애플리케이션 컨텍스트를 팝한다. 일반적으로 애플리케이션 컨텍스트는 요청과 동일한 수명을 갖는다.

직접 컨텍스트 푸시하기

애플리케이션 컨텍스트 외부에서 current_app에 접근을 시도하거나 다른 것이 이걸 사용하려 한다면, 다음과 같은 에러 메시지를 받을 것이다.

RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that
needed to interface with the current application object in some way.
To solve this, set up an application context with app.app_context().

확장 프로그램 초기화 같은 애플리케이션을 설정하는 동안 저 에러를 본다면, app에 대한 직접 접근이 되기 때문에 컨텍스트를 직접 푸시할 수 있다. app_contextwith 블락 내에서 사용하면 블락 내에서 실행하는 모든 것은 current_app에 접근할 수 있다.

def create_app():
    app = Flask(__name__)

    with app.app_context():
        init_db()

    return app

애플리케이션 설정과 연관 없는 코드에서 저 에러를 본다면, 그 코드를 뷰 함수나 CLI 명령 안으로 옮겨야 한다.

데이터 저장

애플리케이션 컨텍스트는 요청 중, CLI 명령 중에 공통 데이터를 저장하기 좋은 곳이다. 플라스크는 이런 목적으로 g 객체를 제공한다. 이건 애플리케이션 컨텍스트와 같은 일생을 갖는 간단한 네임스페이스 객체다.

gglobal을 의미한다. 그러나 그것은 컨텍스트 내에서 데이터가 전역적임을 나타낸다. g에 속한 데이터는 컨텍스트가 끝난 후 사라지고, 이건 요청들 간 데이터를 저장하기에 적절한 곳이 아니다. 요청 간 데이터 저장을 위해 세션이나 DB를 써라.

g의 일반적 사용은 요청 중 자원 관리다.

  1. get_X()는 자원 X가 없다면 생성하고, g.X를 반환한다.
  2. teardown_X()는 종료하거나 자원이 존재한다면 할당 해제한다. 이건 teardown_appcontext() 핸들러로써 등록된다.

예를 들어, 다음 패턴처럼 DB 연결을 관리할 수 있다.

from flask import g

def get_db():
    if 'db' not in g:
        g.db = connect_to_database()

    return g.db

@app.teardown_appcontext
def teardown_db():
    db = g.pop('db', None)

    if db is not None:
        db.close()

요청 중, 모든 get_db() 호출은 같은 연결을 리턴할 것이고 요청이 끝나면 자동으로 닫힐 것이다.

get_db()로부터 새 컨텍스트 로컬을 만들기 위해 LocalProxy를 사용할 수 있다.

from werkzeug.local import LocalProxy
db = LocalProxy(get_db)

db에 접근하는 것은 get_db를 내부적으로 호출하고, 동일한 방식으로 current_app이 동작한다.


확장 프로그램을 작성할 때, g는 코드를 위해 예약돼 있어야 한다. 컨텍스트 자체에 내부 데이터를 저장할 수 있지만 충분히 유니크한 이름을 써야 한다. 현재 컨텍스트는 _app_ctx_stack.top로 접근된다.

이벤트와 시그널

애플리케이션은 애플리케이션 컨텍스트가 팝될 때 teardown_appcontext()에 등록된 함수를 호출한다.

만약 signals_available이 참이면, appcontext_pushed, appcontext_tearing_down, appcontext_popped가 보내진다.

'Python > Flask' 카테고리의 다른 글

플라스크 서버 동작 원리  (0) 2020.06.08