pip3 install flask-smorest
OpenAPI는 RESTAPI를 더 쉽고 이뻐보이게 하는 툴
(1) app.py
✅ flask app 진입점
from flask import Flask
from flask_smorest import Api
from api import blp
app = Flask(__name__)
# OpenAPI 관련 설정
app.config["API_TITLE"] = "My API"
app.config["API_VERSION"] = "v1"
app.config["OPENAPI_VERSION"] = "3.1.3"
app.config["OPENAPI_URL_PREFIX"] = "/"
app.config["OPENAPI_SWAGGER_UI_PATH"] = "/swagger-ui"
# 이 swagger 가 페이지를 이뻐보이게 하는 것
app.config["OPENAPI_SWAGGER_UI_URL"] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist/"
api = Api(app)
api.register_blueprint(blp)
if __name__ == "__main__":
app.run(debug=True)
(2) schemas.py
✅ 데이터 검증과 직렬화에 사용되는 Marshmallow 스키마를 정의(데이터 스키마 검증)
from marshmallow import Schema, fields
# marshmallow 라는 모듈에서 Schema와 fields를 불러옴
class ItemSchema(Schema)
# 클래스명을 ItemSchema 라고 하고 marshmallo 에서의 Schema를 상속받음
id = fields.Int(dump_only=True)
name = fields.Str(required=True)
description = fields.Str()
# 위 세 줄의 형태를 객체라고 하며, 이 객체를 나중에 직렬화하거나
역직렬화할 때 실제로 거기에 데이터에서 뭔가 잘못된 것이 있는지 체크해줌
데이터가 몇개 없을 때는 스키마를 만드는 것이 귀찮겠지만 나중에 데이터가 많고 구조가 복잡해질 경우에 스키마를 사용하면 안심하게 데이터를 프론트로 보내던 받던 할 수 있다.
<마시멜로우 설치>
pip3 install marshmallow
:블루프린트는 애플리케이션의 특정 기능 별로 라우팅, 뷰,함수, 정적 파일 등의 관리가 가능하다.
✅ API가 복잡해질수록 관리의 필요성이 증가함
🔔 주요기능
모듈화 : 블루프린트를 사용하면 애플리케이션의 서로 다른 부분을 별도의 모듈로 나누어 관리할 수 있다. 이는 코드의 재사용성을 높이고, 유지보수를 용이하게 한다.
블루프린트는 자체 URL 규칙을 가지고있으며, 이를 통해 애플리케이션의 라우팅을 체계적으로 관리할 수 있다.
기능별 분리 : 블루프린트를 사용하면 특정 기능에 대한 라우팅, 뷰, 함수, 에러 핸들러, 템플릿 등을 그룹화할 수 있다.
from flask import Flask, Blueprint, render_template, request
app = Flask(__name__)
# 첫 번째 블루프린트
my_blueprint = Blueprint('my_blueprint', __name__)
@my_blueprint.route('/hello')
def hello():
return "Hello from my blueprint!"
@my_blueprint.route('/greet/<name>')
def greet(name):
return f"Hello, {name}!"
# 두 번째 블루프린트
another_blueprint = Blueprint('another_blueprint', __name__, url_prefix='/another')
# /another/world
@another_blueprint.route('/world')
def world():
return "Hello, world, from another blueprint!"
# /another/echo
@another_blueprint.route('/echo', methods=['POST'])
def echo():
data = request.json
return f"Received: {data}"
# 블루프린트에 템플릿을 사용하는 예제
@another_blueprint.route('/template')
def using_template():
return render_template('example.html')
# 세 번째 블루프린트
third_blueprint = Blueprint('third_blueprint', __name__, url_prefix='/third')
@third_blueprint.route('/bye')
def goodbye():
return "Goodbye from the third blueprint!"
# 애플리케이션에 블루프린트 등록
app.register_blueprint(my_blueprint)
app.register_blueprint(another_blueprint)
app.register_blueprint(third_blueprint)
if __name__ == "__main__":
app.run(debug=True)
✅ Flask 와 Flask-smorest 에서 abort 함수는 요청 처리 중에 오류가 발생했을 때 사용된다. 이 함수를 호출하면 특정 HTTP 상태와 함께 응답이 클라이언트한테 전송되며, 선택적으로 오류메시지나 추가정보를 포함시킬 수 있다.
from flask_smorest import abort
# 오류 상황에서 abort 호출
abort(404, message = "Resource not found")
✅ abort를 사용한 오류 처리
from flask import Flask, abort
app = Flask(__name__)
@app.route('/example')
def example():
error_condition = True
# 어떠한 조건에서 오류를 발생시키고 처리
if error_condition:
abort(500, description = "An error occurred while processing the request.")
# 정상적인 응답
return "Success"
✅ abort를 사용하지 않은 오류 처리
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/example')
def example():
# 어떠한 조건에서 오류를 발생시키고 처리
error_condition = True
if error_condition:
error_response = jsonify({"error": "An error occurred while processing the request."})
error_response.status_code = 500
return error_response
# 정상적인 응답
return "Success!"
⚠️ 주요 차이점 및 이유
1) HTTP 상태 코드 전송:
2) 의미있는 오류 메시지 전달:
3) RESTful API 표준화:
4) 가독성과 유지보수성:
abort를 사용하면 오류 처리 부분이 더 간결하고 가독성이 높아진다.
코드가 명확하게 오류 상황을 처리하고 해당 상태 코드를 클라이언트에게 반환한다.
❗️ abort 함수 사용 시 주의사항
오류 메시지를 명확하게 제공하여 클라이언트가 오류의 원인을 쉽게 이해할 수 있도록 한다.
안전하지 않은 정보는 오류 메시지에 포함하지 않도록 주의한다.
(ex. 시스템 경로, 개인정보 등)
(3) api.py
: api 생성 및 블루프린트 정의
from flask.view import MethodView
from flask_smorest import Blueprint, abort
from schemas import ItemSchema
# 블루프린트 생성: 'items'라는 이름으로, URL 접두사는 '/items'
blp = Blueprint("items", "items", url_prefix = "/items", description = "Operation on items")
# 간단한 데이터 저장소 역할을 하는 리스트
items = []
# 'ItemList' 클래스 - GET 및 POST 요청을 처리
@blp.route("/")
class ItemList(MethodView):
@blp.response(200)
def get(self):
# 모든 아이템을 반환하는 GET 요청 처리
return items
@blp.arguments(ItemSchema)
@blp.response(201, description = "Item added")
def post(self, new_data):
# 새 아이템을 추가하는 POST 요청 처리
items.append(new_data)
return new_data
# 'Item' 클래스 - GET, PUT, DELETE 요청을 처리
@blp.route("/<int:item_id>")
class Item(MethodView):
@blp.response(200)
def get(self, item_id):
# 특정 ID를 가진 아이템을 반환하는 GET 요청 처리
# next() => 반복문에서 값이 있으면 값을 반환하고, 없으면 None 을 반환함
# next 는 조건을 만족하는 첫번째 아이템을 반환하고, 그 이후의 아이템은 무시한다.
item = next((item for item in items if item["id"] == item_id), None)
if item is None:
abort(404, message= "Item not found")
return item
@blp.arguments(ItemSchema)
@blp.response(200, description = "Item updated")
def put(self, new_data, item_id):
# 특정 ID를 가진 아이템을 업데이트 하는 PUT 요청 처리
item = next((item for item in items if item["id"] == item_id), None)
item.update(new_data)
return item
@blp.response(204, description = "Item deleted")
def delete(self, item_id):
# 특정 ID 를 가진 아이템을 삭제하는 DELETE 요청 처리
global items
if not any(item for item in items if item["id"] == item_id):
abort(404, message = "Item not found")
items = [item for item in items if item["id"] != item_id]
return ''