지연된(deferred) 요청 콜백

플라스크의 설계 원칙 중 한가지는 응답 객체가 생성되고 그 객체를 수정하거나 대체할 수 있는 잠재적인 콜백의 호출 사슬로 그 객체를 전달하는 것이다. 요청 처리가 시작될 때, 아직은 응답 객체를 존재하지 않는다. 뷰 함수나 시스템에 있는 어떤 다른 컴포넌트에 의해 필요할 때 생성된다.

하지만 응답이 아직 존재하지 않는 시점에서 응답을 수정하려 하면 어떻게 되는가? 그에 대한 일반적인 예제는 응답 객체에 있는 쿠키를 설정하기를 원하는 before-request 함수에서 발생할 것이다.

한가지 방법은 그런 상황을 피하는 것이다. 꽤 자주 이렇게 하는게 가능하다. 예를 들면 여러분은 그런 로직을 after-request 콜백으로 대신 옮기도록 시도할 수 있다. 하지만 때때로 거기에 있는 코드를 옮기는 것은 유쾌한 일이 아니거나 코드가 대단히 부자연스럽게 보이게 된다.

다른 가능성으로서 여러분은 g 객체에 여러 콜백 함수를 추가하고 요청의 끝부분에서 그것을 호출할 수 있다. 이 방식으로 여러분은 어플리케이션의 어느 위치로도 코드 실행을 지연시킬 수 있다.

데코레이터

다음에 나올 데코레이터가 핵심이다. 그 데코레이터는 g 객체에 있는 리스트에 함수를 등록한다:

from flask import g

def after_this_request(f):
    if not hasattr(g, 'after_request_callbacks'):
        g.after_request_callbacks = []
    g.after_request_callbacks.append(f)
    return f

지연된 함수의 호출

이제 여러분은 요청의 마지막에서 호출될 함수를 표시하기 위해 after_this_request 데코레이터를 사용할 수 있다. 하지만 우리는 여전히 그 함수를 호출할 수도 있다. 이렇게 하기 위해 다음 함수가 after_request() 콜백으로 등록될 필요가 있다:

@app.after_request
def call_after_request_callbacks(response):
    for callback in getattr(g, 'after_request_callbacks', ()):
        response = callback(response)
    return response

실제적인 예제

이제 우리는 이 특정 요청의 마지막에서 호출될 함수를 적절한 어느 시점에라도 쉽게 등록할 수 있다. 예를 들면 여러분은 before-request 함수에서 쿠키에 사용자의 현재 언어를 기억하게 할 수 있다:

from flask import request

@app.before_request
def detect_user_language():
    language = request.cookies.get('user_lang')
    if language is None:
        language = guess_language_from_request()
        @after_this_request
        def remember_language(response):
            response.set_cookie('user_lang', language)
    g.language = language