[Python] Flask 에서의 context 이해하기

김지환·2023년 2월 26일
0

TL;DR

Django, FastAPI 등에서 직접 HTTP Request object의 정보를 가져오기 위해서는

from fastapi import FastAPI, Request

app = FastAPI()


@app.get("/items/{item_id}")
def read_root(item_id: str, request: Request):
    client_host = request.client.host
    return {"client_host": client_host, "item_id": item_id}
from django.http import HttpResponse

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

일반적으로 위와 같이 view layer ( Controller ) 에서 endpoint 별로 request 형태로 받아서 사용하게 된다. 따라서 request에 대한 조작이 필요하다면 매개변수에 항상 넣어줘야하는 불편함이 있다.

이에 Flask 는

from flask import request
def index():
    if request.method == 'OPTIONS':
        # custom options handling here
        ...
    return 'Hello World!'

request 라는 전역변수를 사용하게 된다. ( 뒤에 서술하겠지만 완전한 전역변수는 아니고 werkzeug 에 있는 구현체인 LocalStack 타입의 변수이다.

따라서 view func 마다 request를 써주거나 할 필요가 없어진다.

그렇다면 이러한 전역변수 관리는 어떻게 이루어지길래 thread, process, coroutine 에 따라 safe하게 관리가 될 수 있을까?

두 개의 context

Application Context, Request Context

Flask 에 request가 들어오게 되면 global 하게 Application Context, Request Context가 형성되게 된다.

두 context모두 전반적으로 두루 사용되는 데이터를 담고 있게 되는데 그 성질에 따라서 두 개의 context로 나눠지게 된다.

Application Context

application context 는 주로 logger, db configuration 과 같이 시스템 전반적으로 공유되는 data 정보를 담고 있게 된다.
실제 사용자 ( 개발자 )는 application context의 데이터에 접근하기 위해서 proxy 객체를 이용하게 되는데 그것이 바로 우리가 사용하는 current_app, g 이다.

from werkzeug.local import LocalProxy
from werkzeug.local import LocalStack

def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app

_app_ctx_stack = LocalStack()

current_app = LocalProxy(_find_app)
g = LocalProxy(partial(_lookup_app_object, "g"))

실제 코드를 보면 위와 같이 werkzeug(밸저그)에서 가져온 LocalStack, LocalProxy를 이용함을 볼 수 있다.

  • current_app: 현재 request를 핸들링하고 있는 app_instance를 참조하고 있다.
  • g: request를 처리할 때 임시 저장소로 사용한다. user 정보 같은 것 혹은 configuration 정보등을 담아 두고 사용하기에 좋다.

Request Context

request context는 url, http method, request header etc... 같은 request level의 정보를 담고있다.

from functools import partial

from werkzeug.local import LocalProxy
from werkzeug.local import LocalStack

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    return getattr(top, name)
    
_request_ctx_stack = LocalStack()
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
  • request: 현재 핸들링 하고 있는 요청에 대한 정보만을 담고 있다.
  • session: 세션은 플라스크 서버를 구동하는 동안에 영구히 참조할 수 있는 값이다. 즉, 요청하는 URL이 바뀌어도 이 세션값을 이용할 수 있다. 단, 세션은 시간제한이 있어 일정 시간 접속하지 않으면 자동으로 삭제 된다.

Context를 접근하기 위해서는 view 혹은 context context?를 만들어야한다

view function 내에서 사용을 하거나 혹은 아래와 같이 임의로 app_context, request_context를 만들어서 접근할 수 있도록 context를 만들어줘야한다.

from app import app
from flask import request, current_app

with app.app_context():
	current_app.config

with app.test_request_context():
	request.method

with app.request_context(environ): # <-- WSGI environ 정보가 들어가 있다.
	request.method
    request.path
profile
Developer

0개의 댓글