쿼카러버의 기술 블로그

[Flask] context-local 구현체 살펴보기 (flask application context, request context) 본문

[Python]

[Flask] context-local 구현체 살펴보기 (flask application context, request context)

quokkalover 2022. 1. 31. 00:56

이 글은 Flask의 context-local의 구현체가 어떻게 생겼는지에 대해 알아본다.

 

따라서, Context Local에 대해 알아보는 독자는 Flask의 Application Context와 Request Context가 무엇인지, 그리고 Flask가 어떻게 request를 처리하는지 알고 있다는 전제하에 작성했다. 만약 모른다면, 아래 글들을 먼저 읽어보는 것을 추천한다.

 

1) context 간단 정리(https://etloveguitar.tistory.com/91)

2) flask의 요청 처리와 context 심층 이해 (https://etloveguitar.tistory.com/92)

 

Local

Context Local은 werzeug의 LocalStack 객체로 구현돼있다.

LocalStack을 알아보기 전에 먼저 Local객체를 알아보자. Local객체는 아래의 방법으로 생성할 수 있다.

from werkzeug.local import Local
data = Local()
data.user = 'etloveguitar@gmail.com'

위처럼 context를 생성하고 나면, context-unique한 data 저장소에 접근할 수 있게 된다.

 

LocalStack

LocalStack도 위 Local객체와 마찬가지다. 여기서 다른점은 stack의 기본적인 operation인 push()pop()을 지원한다는 점이다. Flask의 Application Context와 Request Context Stack은 이 LocalStack으로 구현돼 있다.

이 Context-local이 어떤식으로 동작하는지 예제를 통해 살펴보자.

위는 각각의 thread들이 어떻게 LocalStack에 접근하는지 보여준다. 코드 예제로 한번 살펴보자.

"""
여러개의 스레드를 사용할 때, 전역 객체인 LocalStack이 어떤식으로 활용되는지 살펴보자.
"""

import random
import threading
import time

from werkzeug.local import LocalStack

# Create a global LocalStack object for storing data about each thread
thread_data_stack = LocalStack()

def long_running_function(thread_index):
    """Simulates a long-running function by using time.sleep()."""

    thread_data_stack.push({'index': thread_index, thread_id': threading.get_native_id()})
    print(f'Starting thread #{thread_index}... {thread_data_stack}')

    time.sleep(random.randrange(1, 11))

    print(f'LocalStack contains: {thread_data_stack.top}')
    print(f'Finished thread #{thread_index}!')
    thread_data_stack.pop()

if __name__ == "__main__":
    threads = []

    # Create and start 3 threads that each run long_running_function()
    for index in range(3):
        thread = threading.Thread(target=long_running_function, args=(index,))
        threads.append(thread)
        thread.start()

    # Wait until each thread terminates before the script exits by
    # 'join'ing each thread
    for thread in threads:
        thread.join()

    print('Done!')

위 코드를 실행하면 LocalStack객체를 생성한다. (thread_data_stack) 이 LocalStack은 각각의 thread마다 고유한 저장소를 만들어준다.

long_running_function은 thread와 관련된 정보를 thread_data_stack에 push한다. 그 뒤에 thread_data_stack의 데이터에 접근하는데, 이렇게 함을 통해서 다수의 thread가 data를 push했는데, 다른 thread에서 이 정보에 접근할 수 있는지 확인해볼 수 있다. 함수가 끝나고나면 thread_data_stack에서 정보를 pop한다.

 

위 코드를 다 실행시키고 나면 놀랍게도 모든 thread가 동일한 LocalStack에 접근하고 있지만, 각각의 unique한 정보에만 접근할 수 있는걸 확인할 수 있다. 그리고 이 stack은 다른 전역 변수들과는 다르게 thread-safe하다.

 

매우 놀랍다. Flask의 Application Context Stack과 Request Context Stack은 바로 이 특성을 활용해서 controller function에 각각의 context를 넘길 필요 없이 요청과 관련된 정보를 처리할 수 있다.

 

참고자료:

https://www.codestudyblog.com/cnb11/1124181719.html

https://testdriven.io/blog/flask-contexts/

https://testdriven.io/blog/flask-contexts-advanced/

https://flask.palletsprojects.com/en/1.1.x/reqcontext/#notes-on-proxies

https://speakerdeck.com/mitsuhiko/advanced-flask-patterns-1

https://en.wikipedia.org/wiki/Context_(computing)

https://www.youtube.com/watch?v=fq8y-9UHjyk

https://www.linkedin.com/pulse/application-context-request-flask-shruthi-sagar-cr/?trk=articles_directory

Comments