Flask의 전역 저장소 g와 요청 컨텍스트와의 관계 이해하기
Grace Collins
Solutions Engineer · Leapcell

소개
웹 개발의 세계에서 요청 라이프사이클의 다양한 부분에 걸쳐 애플리케이션별 데이터와 상태를 관리하는 것은 일반적인 과제입니다. 인기 있는 Python 웹 프레임워크인 Flask는 주로 애플리케이션 및 요청 컨텍스트를 통해 이를 위한 우아한 솔루션을 제공합니다. 이러한 도구 중에서 g 객체는 종종 단일 요청 범위 내에서 데이터를 전역적으로 저장하고 액세스하는 편리하지만 때로는 오해되는 메커니즘으로 나타납니다. g가 어떻게 작동하는지, 그리고 결정적으로 요청 컨텍스트와 어떻게 구별되는지를 이해하는 것은 깔끔하고 효율적이며 유지 관리 가능한 Flask 애플리케이션을 작성하는 데 매우 중요합니다. 이 글에서는 Flask의 g 객체의 복잡성을 탐구하고, 요청 컨텍스트와의 연결을 설명하며, 실제 유용성을 보여줄 것입니다.
핵심 개념
g 자체를 자세히 살펴보기 전에, 그것의 작동을 뒷받침하는 몇 가지 핵심 개념을 간략하게 정의해 보겠습니다.
컨텍스트 로컬(Context Locals)
Flask의 컨텍스트 관리의 핵심에는 "컨텍스트 로컬(context locals)"이 있습니다. 이것은 전역 변수처럼 보이지만 활성 컨텍스트마다 고유한 특수 객체입니다. 다중 스레드 웹 서버에서 여러 요청이 동시에 처리되더라도 각 스레드는 자체 고유한 컨텍스트 로컬 집합을 갖게 됩니다. 이는 한 요청의 데이터가 다른 요청으로 "유출"되는 것을 방지하여 스레드 안전성과 격리를 보장합니다.
요청 컨텍스트(Request Context)
Flask의 요청 컨텍스트는 요청별 정보를 모두 보유하는 환경입니다. 본질적으로 특정 들어오는 요청에 대한 활성화 레코드입니다. 요청이 들어오면 Flask는 요청 컨텍스트를 스택에 푸시합니다. 이 컨텍스트는 request, session 및 결정적으로 g와 같은 객체를 컨텍스트 로컬로 사용할 수 있게 합니다. 요청이 완료되면 Flask는 요청 컨텍스트를 팝하여 이러한 객체를 정리합니다. 요청 컨텍스트는 서버가 처리하는 요청 수가 얼마든 상관없이 request.path가 항상 현재 요청의 경로를 참조하도록 보장합니다.
애플리케이션 컨텍스트(Application Context)
요청 컨텍스트와 유사하게, 애플리케이션 컨텍스트는 애플리케이션 전체 데이터에 대한 액세스를 제공합니다. current_app과 같은 객체를 사용할 수 있게 합니다. 애플리케이션 컨텍스트는 애플리케이션이 시작될 때 또는 애플리케이션 시작 시 구성된 데이터베이스 연결과 같이 애플리케이션 설정이 필요한 작업이 수행될 때 푸시됩니다. 단일 애플리케이션 컨텍스트는 여러 요청 컨텍스트를 포함할 수 있습니다.
Flask의 g 전역 객체 작동 방식
g 객체( "global"의 약자)는 Flask에서 제공하는 간단한 네임스페이스 객체입니다. 주요 목적은 현재 요청에 고유한 데이터를 저장하는 특별한 장소가 되는 것입니다. 중요하게도 g는 자체적으로 컨텍스트 로컬이므로 각 요청은 자체의 새로운 g 객체를 얻습니다. 이를 통해 g에 저장된 데이터가 동시에 처리되는 다른 요청의 데이터와 간섭하지 않도록 보장합니다.
표준 속성 할당을 사용하여 g에 모든 데이터를 저장할 수 있습니다.
from flask import Flask, g, request app = Flask(__name__) @app.before_request def before_request_hook(): # 데이터베이스에서 사용자 데이터 가져오기 시뮬레이션 user_id = request.args.get('user_id', 'anonymous') g.user = f"User-{user_id}" print(f"Before request: g.user set to {g.user}") @app.route('/') def index(): print(f"Inside view: current user is {g.user}") return f"Hello, {g.user}!" @app.route('/profile') def profile(): print(f"Inside profile view: current user is {g.user}") return f"Welcome to your profile, {g.user}!" if __name__ == '__main__': app.run(debug=True)
이 예제에서:
before_request_hook함수는 모든 요청 전에 실행됩니다.- 이 훅 내부에서 사용자(URL 매개변수로 시뮬레이션됨)를 식별하고
g.user에 저장합니다. - 후속 뷰 함수(
index및profile)는g.user에 액세스하여 인수로 명시적으로 전달하지 않고도 사용자 정보를 가져올 수 있습니다.
이는 g를 요청 중에 반복적으로 필요하지만 요청당 한 번만 초기화해야 하는 리소스 또는 객체(예: 데이터베이스 연결, 사용자 객체 또는 현재 사용자에 대한 구문 분석된 구성)를 캐시하는 편리한 방법으로 보여줍니다.
g 와 요청 컨텍스트 비교
주요 차이점을 명확히 하면 오해가 해소됩니다. g는 요청 컨텍스트의 일부이지만, 요청 컨텍스트 자체는 아닙니다.
- 요청 컨텍스트: 이것은
request,session및g를 포함한 모든 요청별 데이터를 보유하는 컨테이너 또는 환경입니다. 요청이 들어오면 Flask는 이 전체 환경을 설정합니다. g객체: 이것은 개발자가 자신의 사용자 지정 요청별 데이터를 저장하기 위해 제공되는 빈 슬레이트인 해당 요청 컨텍스트 내의 특정 객체입니다.
요청 컨텍스트를 Flask가 각 새 요청에 대해 제공하는 서류 가방이라고 생각해 보세요. 해당 서류 가방 안에 Flask는 들어오는 HTTP 요청에 대한 세부 정보(request 객체) 및 사용자 세션 데이터(session 객체)와 같은 필수 항목을 이미 넣어 두었습니다. g 객체는 해당 서류 가방의 빈 메모장과 같아서 해당 특정 서류 가방의 여정에 유용하다고 생각하는 모든 것을 적어둘 수 있습니다.
g가 요청 컨텍스트를 생성하는 것이 아니라, 요청 컨텍스트가 활성 상태임을 의존한다는 점을 이해하는 것이 중요합니다. 활성 요청 컨텍스트 외부(예: Flask의 요청 처리에서 시작되지 않은 백그라운드 스레드)에서 g에 액세스하려고 하면 Flask는 RuntimeError를 발생시킵니다. g가 어떤 요청의 데이터와 연결되어야 하는지 알 수 없기 때문입니다.
데이터베이스 연결을 저장하려는 시나리오를 고려해 보세요.
from flask import Flask, g, current_app import sqlite3 app = Flask(__name__) app.config['DATABASE'] = 'my_database.db' def get_db(): if 'db' not in g: g.db = sqlite3.connect(current_app.config['DATABASE']) g.db.row_factory = sqlite3.Row # 행을 dict와 유사한 객체로 반환 return g.db @app.teardown_appcontext def close_db(exception): db = g.pop('db', None) if db is not None: db.close() @app.route('/items') def list_items(): db = get_db() cursor = db.execute('SELECT * FROM items') items = cursor.fetchall() return {'items': [dict(item) for item in items]} if __name__ == '__main__': # 데이터베이스 초기화 with app.app_context(): db = get_db() db.execute('CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)') db.execute("INSERT INTO items (name) VALUES ('Item A')") db.execute("INSERT INTO items (name) VALUES ('Item B')") db.commit() app.run(debug=True)
이 패턴에서:
get_db()함수는 데이터베이스 연결을 지연시켜 초기화합니다.g.db가 이미 존재하는지 확인합니다. 존재하지 않으면 새 연결을 만들고g에 저장합니다. 이렇게 하면 요청당 연결이 하나만 보장됩니다.teardown_appcontext데코레이터는 애플리케이션 컨텍스트(그리고 결과적으로 요청 처리 후 요청 컨텍스트)가 해체될 때close_db를 실행하도록 등록합니다. 이렇게 하면g에 저장된 데이터베이스 연결이 올바르게 닫혀 리소스 누수가 방지됩니다.
이것은 g의 역할을 리소스에 대한 임시, 요청별 저장 영역으로 명확하게 보여줍니다.
결론
Flask의 g 객체는 개발자 편의를 위해 설계된 강력한 요청 범위 네임스페이스입니다. 현재 요청에 고유한 임의 데이터를 저장하기 위한 스레드 안전한 사전과 같은 객체를 제공하여 요청 라이프사이클의 다양한 구성 요소 간에 리소스 관리 및 데이터 전달을 간단하게 만듭니다. g는 요청이 설정한 컨텍스트 내에 있지만, 그것이 요청 컨텍스트 자체는 아니며, 사용자 지정 요구 사항을 위한 전용 슬롯으로서 단일 웹 요청 내의 상태 관리를 효과적으로 단순화합니다.

