<converter:variable_name>
컨텍스트 전역 변수
수신한 HTTP request 정보에 접근하기 위해서 컨텍스트 전역 변수를 사용한다.
이런 정보들을 리퀘스트를 수신할 때 마다 뷰 함수의 인자로 넘기도록 해도 되는데, 쓸데없이 인자를 많이 가지고 있게 되는 것을 피하기 위해 플라스크는 이를 전역 변수 형식으로 제공한다.
근데 실제 전역 변수는 아니다. 멀티스레드 환경에서 못쓰니까. `` request`` 변수가 전역변수 처럼 동작하기는 하지만 실제로는 스레드들은 각각 다른 오브젝트를 처리하게 된다.
컨텍스트는 어플리케이션 컨텍스트/리퀘스트 컨텍스트로 나눌 수 있다.
|
|
|
current_app |
어플리케이션 컨텍스트 |
활성화된 어플리케이션을 위한 어플리케이션 인스턴스 |
g |
어플리케이션 컨텍스트 |
리퀘스트를 처리하는 동안 어플리케이션이 임시 스토리지로 사용할 수 있는 오브젝트. 리퀘스트 마다 리셋됨. 주로 request hooks와 뷰 함수가 데이터를 주고 받을 때 사용한다. 예를 들어 `` before_request`` 훅이 DB에서 정보를 받아와서 g변수에 넣어두면 뷰 함수가 이를 사용하는 식. |
request |
리퀘스트 컨텍스트 |
HTTP request의 컨텐츠를 캡슐화하는 리퀘스트 오브젝트. |
session |
리퀘스트 컨텍스트 |
사용자 세션. "remembered"인 값들을 저장하는데 사용하는 딕셔너리. |
플라스크는 리퀘스트를 디스패치(처리)하기 전에 어플리케이션과 리퀘스트 컨텍스트를 활성화(push)하며, 리퀘스트가 처리된 후 컨텍스트를 삭제한다. current_app, g는 어플리케이션 컨텍스트가 push되어야 사용 가능하고, request, session도 마찬가지로 리퀘스트 컨텍스트가 push 되어야 사용 가능하기 때문에 그냥 이 변수에 접근하기 위해서는 먼저 push해주어야 한다.
리퀘스트 훅 (request hooks)
```python
before_first_request : 첫 번째 리퀘스트가 처리되기 전에 실행
before_request : 각 리퀘스트 전에 실행됨.
after_request : 처리되지 않는 예외가 발생하지 않는다면 리퀘스트 이후 실행
teardown_request : 처리되지 않는 예외가 발생하더라도 리퀘스트 이후 실행
```
Jinja2의 유용한 기능
- macro - import : 함수같은.
- include : inline같은거라고 이해하면 된다.
- extends : 상속.
웹 소켓 Flask-SocketIO
Flask-SocketIO 라이브러리를 사용하게 되는데... 이게 좀 문제가 있다.
웹소켓은 이벤트로 이루어져 있다. 클라이언트 측에서 서버 측의 이벤트 함수가 동작하도록 요청할 수 있고, 그 반대도 가능하다.
Flask-Socket는 이런 웹소켓 이벤트 함수를 처리하기 위해서 요청이 들어오면 (eventlet | gevent | threading)를 사용해 이벤트 핸들러 함수를 실행하게 되며 eventlet과 gevent는 코루틴을 사용하는 방식이므로 이벤트 핸들러 함수를 코루틴을 이용해 실행하게 된다.
문제는 이런 경우 별의별 방법을 다 동원해서 뭔 지랄을 해도 해당 함수(이벤트 핸들러 함수)가 끝날 때 까지는 절대 buffer flush가 안된다!!
개발자 말로는 ``py eventlet.sleep()``하면 된다는데, 안되는 이슈가 있다. 왜 안되냐니까 원래 슬립하면 flush 되어야 하는게 맞는데 자기도 놀랍다고 그냥 쓰레드 쓰란다.
그래서 주기적인 서버 푸시가 필요하거나, 단발성 이벤트가 아닌 지속되어야 하는 이벤트 함수의 경우는
``py async_mode="threading"``으로 주어야 제대로 버퍼가 flush된다.
이 밖에도 여러모로 이슈가 좀 있다... 자꾸 어디선가 에러가 나고... 스택오버플로우에도 자료가 부족하고... 플라스크로 SocketIO쓰려면 스트레스에 대비하는 것이 좋을 듯.
room
이게 엄청 헷갈리는데, room은 클라이언트와 연관이 큰 속성이다. 서버에서 join_room(room_name) 하게 되면 그 웹소켓 객체가 room에 속하게 되는거라고 생각하면 안되고, 그 요청을 보낸 클라이언트가 room에 속하게 되는거라고 생각해야 한다.
그래서 emit할 때 room_name을 적어주는건, 그 room에 속한 클라이언트 브라우저에만 데이터를 보내겠다는 거다.
flask-bootstrap
이름은 거창하지만 다음 두 가지 기능.
1. bootstrap CDN에서 가져와 로딩하는걸 자동으로 해주는 기능 (`` BOOTSTRAP_SERVE_LOCAL`` 주면 local에서 하도록 설정 되고.)
2. 상속 받아서 재정의 할 수 있는 base 템플릿을 제공.
안써도 크게 상관 없음. 아니, 오히려 안쓰는게 나을 수도 있음. 어차피 테마 가져다 쓰든, 페이지를 만들든 base.html 을 정의해야 하는 경우가 많은데 미리 정의된거 extends해서 재정의하려다가 꼬이는 경우도 있어서.
json 데이터 리턴할 때 ( ajax )
```python
json_data = json.dumps(dict_data)
# json_data(str, byte 타입)가 이미 있는 경우
return flask.Response(response=json_data, status=200, mimetype="application/json")
# dict 데이터를 json으로 만들어서 전송하는걸 자동으로 처리
return flask.jsonify(dict_data)
```
보통 객체를 갖다 넣으면 다음과 같은 에러가 발생할텐데,
```
TypeError: Object of type 'ClsName' is not JSON serializable
```
객체를 dict로 반환해주는 ``py obj.__dict__`` 속성을 이용한다. 내부적으로는 똑같은데 Encoder를 정의하는 방법과 lambda를 사용하는 방법이 있음.
```python
json.dumps(log_records, default= lambda o: o.__dict__)
```