프로젝트에서 Flask
를 사용해 개발하고자 했던 것은 웹 애플리케이션이 아니라 API 서버이다. 물론 일반 Flask
를 사용해서도 API서버를 만들 수 있지만 Flask-RESTX
Extension을 사용하면 보다 직관적으로 API서버를 작성할 수 있고, 도움이 되는 기능들을 추가로 사용할 수 있게 된다. 특히, 추후에 소개할 swagger
와 같은 기능을 추가적인 Extension설치 없이 제공하기 때문에 Flask-RESTX
를 사용해 프로젝트를 진행하였다.
pip install Flask
pip install flask-restx
이제 직접 예시를 작성해 보면서 Flask
를 사용했을 때의 코드와 Flask-RESTX
를 사용했을 때의 코드를 비교해 보도록 하겠다. 먼저 예시 프로젝트의 구조는 다음과 같다.
app
│ app_flask.py
│ app_flask_restx.py
│
└─hello
│ hello_flask.py
│ hello_flask_restx.py
app_flask.py
와 hello_flask.py
는 Flask
로 작성된 예시 프로젝트이고, app_flask_restx.py
와 hello_flask_restx.py
는 Flask_RESTX
로 작성된 예시이다.
app_flask.py
from flask import Flask
from hello import hello_flask
app = Flask(__name__)
app.register_blueprint(hello_flask.bp) #블루프린트 등록
if __name__ == "__main__":
app.run()
hello_flask.py
from flask import Blueprint, jsonify, make_response
bp = Blueprint('hello_flask', __name__, url_prefix='/hello_flask')
@bp.route("", methods=['GET'])
def hello():
response = {"message" : "hello"}
return make_response(jsonify(response), 200)
원래 이런 예시정도의 애플리케이션은 모듈화를 하지 않아도 되지만 추후에 리뷰할 캡스톤 프로젝트는 모듈화가 되어 있으므로 이에 대해 이해하기 쉽도록 Flask
의 모듈화에 대해서 정리하고 간다.
Blueprint
는 Flask
에서 모듈화를 위해 지원하는 기능이다. 이 예시같은 작은 프로젝트는 하나의 파일로 작성하여도 가독성이나 유지 보수 측면에서 문제가 발생하지 않으나, 실제 프로젝트는 이렇게 단순하지가 않기 때문에 모듈 단위로 분리하여 개발한다.
Blueprint()
를 통해서 Blueprint객체를 생성한다. 첫 번째 인자인 'hello_flask'
는 이 Blueprint객체의 이름이다. 세 번째 인자인 url_prefix='/hello_flask'
는 아래의 @bp.route()
를 통해 연결될 url의 접두어를 등록한 것이다.
@bp.route()
을 통해서 해당 url을 통해 아래의 hello
함수를 호출할 수 있게 된다. 즉 http://BASE_URL/hello_flask
를 통해 접근하면 hello
함수의 실행 결과를 얻을 수 있는 것이라고 생각하면 된다. 두 번째 인자 methods=['GET']
은 HTTP의 method 중 GET method로 해당 url에 접근했을 때 아래의 함수를 매핑해준다는 것이다. HTTP의 method는 이외에도 'POST', 'PUT', 'DELETE'등이 있지만 여기서는 더 이상 언급하지 않는다.
def hello()
는 사용자에게 {"message" : "hello"}
라는 JSON응답과 HTTP status code 200
을 반환한다. 이를 위해서 우선 response란 변수에 {"message" : "hello"}
라는 Dictionary자료형을 저장한다. 그 다음 response를 JSON응답으로 변환하기 위해 jsonify()
함수를 사용한다. 마지막으로 응답에 status code를 추가하기 위해 make_response()
함수를 사용한다.
실제로 애플리케이션이 잘 동작하는지를 확인하기 위해 Postman을 사용해서 테스트해본다.
이 사진은 Flask
앱을 실행했을 때 터미널을 캡쳐한 것인데 맨 아래를 보면 해당 서버가 http://127.0.0.1:5000/
에서 동작한다는 것을 알 수 있다. 여기에 아까 지정한 url_prefix를 더해서 최종적으로 http://127.0.0.1:5000/hello_flask 라는 url에 postman을 사용해 GET 요청을 보내 보았다.
정상적으로 JSON응답이 반환되는 것을 확인할 수 있다. 그렇다면 Flask-RESTX를 사용해서 이를 작성하면 어떻게 될까?
app_flask_restx.py
from flask import Flask
from flask_restx import Api
from hello.hello_flask_restx import Hello_Flask_Restx
app = Flask(__name__)
api = Api(app)
api.add_namespace(Hello_Flask_Restx, '/hello_flask_restx')
if __name__ == "__main__":
app.run()
hello_flask_restx.py
from flask_restx import Namespace, Resource
Hello_Flask_Restx = Namespace('hello_flask_restx')
@Hello_Flask_Restx.route('')
class Hello(Resource):
def get(self):
return {"message" : "hello"}, 200
Flask-RESTX
에서는 Blueprint대신 Namespace를 통해서 모듈화를 제공한다. Flask객체를 감싼 Api객체를 통해 Namespace를 등록할 수 있다. add_namespace()
함수를 통해 등록하는데, 앞의 Blueprint와 달리 이 add_namespace()
함수에서 url_prefix를 등록한다. 여기서는 두 번째 인자인 '/hello_flask_restx'
를 말한다.
@Hello_Flask_Restx.route()
를 통해서 url과 함수를 매핑하는데, 여기서 일반 Flask
와 차이가 생긴다. 원래는 route()
함수의 인자로써 매핑되는 경로와 method를 지정해 주었는데, Flask-RESTX
는 Resource객체를 상속하는 class를 만들고 해당 class 내에 method함수를 override하는 것으로 매핑한다. 이 코드에서는 get()
을 override했으므로 get요청에 대해 처리하는 로직을 작성한 것이다.
JSON응답과 HTTP status code를 반환하는 것도 간단하다. jsonify()
를 사용할 필요도 없이 Dictionary 객체 그대로 return하면 된다. status code도 동일하게 반환하고자 하는 status code를 ,
로 구분하여 작성하면 된다.
실행 결과도 완전히 같다.
그 때 당시에는 진짜로 이런 웹 프레임워크를 처음 접하다 보니까 HTTP 통신, JSON, 이러한 기본 개념들이 전혀 잡혀 있지 않았었기 때문에 Flask-RESTX
가 좀 더 직관적으로 느껴졌다. 미리 공부하고 개발에 들어간 것이 아니라 공부하면서 개발에 들어갔었기 때문에 보다 직관적이고 응답 등을 쉽게 커스텀할 수 있는 Flask-RESTX
가 더 매력적이었고, 해당 Extension을 선택할 때는 알지 못했지만 이후 협업 과정을 위해 API 문서를 작성해야 했는데, 이 때 Flask-RESTX
를 선택한 것이 큰 도움이 되었다.