Flask - Resource

Always·2025년 8월 24일

Backend&Devops

목록 보기
15/15

도입


회사에서 사용하는 기본적인 언어가 python이고, 프레임워크는 flask기반으로 한 자체 프레임 워크이
다. 그래서 기본적인 flask예제를 통해서 공부를 해볼까 한다.

Resource

API매핑

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측면에서 낫다.

dispatch_request

위처럼 구현이 가능한 것은 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등의 요청을 매핑해서 실행하는 부분이다.

내부코드가 직관적으로 잘 구현되어있음을 이해할 수 있었다.

profile
🐶개발 블로그

0개의 댓글