
회사에서 사용하는 기본적인 언어가 python이고, 프레임워크는 flask기반으로 한 자체 프레임 워크이
다. 그래서 기본적인 flask예제를 통해서 공부를 해볼까 한다.
flask_restful패키지는 Api 클래스와 Resource클래스를 통해서 Restful환경에서의 Api매핑을 지원한다.
이 패키지를 이용하지 않는다면 아래와 같이 일일히 api매핑을 해야한다.
from flask import Flask
app=Flask(__name__)
@app.route("/home")
def home():
return "comeback home"
@app.route("/cafe")
def cafe():
return "comeback cafe"
@app.route("/bus")
def bus():
return "comeback bus"
if __name__=='__main__':
app.run(debug=False)
각 api에 대해서 일일히 매핑해줘야 하며, 들어오는 요청에 대해서 get,post 등등을 일일히 매핑해야한다.
하지만 flask_restful을 이용하면 아래와 같이 추상화가 가능하다.
from flask import Flask
from flask_restful import Api,Resource
app=Flask(__name__)
api=Api(app)
class HomeResource(Resource):
def get(self):
return "comeback home"
class CafeResource(Resource):
def get(self):
return "comeback cafe"
class BusResource(Resource):
def get(self):
return "comeback bus"
api.add_resource(HomeResource,"/home")
api.add_resource(CafeResource,"/cafe")
api.add_resource(BusResource,"/bus")
if __name__=='__main__':
app.run(debug=False)
물론 각 Resource들은 다른 파일로 구성하는게 srp측면에서 낫다.
위처럼 구현이 가능한 것은 Resource객체가 가지고 있는 dispatch_request 메서드 덕분이다.
Flask는 내부적으로 등록된 요청이 들어오게 되면 dispatch_request를 호출한다.
내부적으로는 아래와 같은 구조를 가진다.
요청-> Resource의 조부모 객체가 as_view를 통해서 새로운 객체이면
resource를 등록하고,아니라면 dispatch_request를 실행->
dispatch_request`내에서 각 요청의 메서드를 매핑하고,
사용자가 정의한 메서드를 데코레이터로 감싸서 실행.
Resource객체의 조부모 객체인 View의 as_view 메서드이다.
@classmethod
def as_view(
cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
) -> ft.RouteCallable:
"""Convert the class into a view function that can be registered
for a route.
By default, the generated view will create a new instance of the
view class for every request and call its
:meth:`dispatch_request` method. If the view class sets
:attr:`init_every_request` to ``False``, the same instance will
be used for every request.
Except for ``name``, all other arguments passed to this method
are forwarded to the view class ``__init__`` method.
.. versionchanged:: 2.2
Added the ``init_every_request`` class attribute.
"""
if cls.init_every_request:
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
self = view.view_class( # type: ignore[attr-defined]
*class_args, **class_kwargs
)
return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return]
else:
self = cls(*class_args, **class_kwargs) # pyright: ignore
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return]
위에 구조로 적은 것 처럼 첫 요청이면, 즉 add_resource이면, view를 새로 정의하고, 아니라면, dispatch_reuqest를 호출한다.
def dispatch_request(self, *args, **kwargs):
# Taken from flask
#noinspection PyUnresolvedReferences
meth = getattr(self, request.method.lower(), None)
if meth is None and request.method == 'HEAD':
meth = getattr(self, 'get', None)
assert meth is not None, 'Unimplemented method %r' % request.method
if isinstance(self.method_decorators, Mapping):
decorators = self.method_decorators.get(request.method.lower(), [])
else:
decorators = self.method_decorators
for decorator in decorators:
meth = decorator(meth)
resp = meth(*args, **kwargs)
if isinstance(resp, ResponseBase): # There may be a better way to test
return resp
위의 dispatch_request는 메서드는 Resousrce에 있으며, 실제적으로 post,get등의 요청을 매핑해서 실행하는 부분이다.
내부코드가 직관적으로 잘 구현되어있음을 이해할 수 있었다.