
app.py 하나로 api를 만들었음 <- 코드가 복잡해질 수록 "파일 분리"가 필요성은 커지게된다.app.py, to_do.py 파일 2개로 api를 만들어보고자 함app.py : API정보 및 기능이 구현된 파일을 가져와 기능별로 namespace를 부여해줌 => API 전체적인 그림을 그려주는 부분to_do.py : 메소드 등 기능이 구현된 파일 => API 세부 동작(기능)을 구현하는 부분app.py 코드from flask import Flask
from flask_restx import Resource, Api
from to_do import Todo # 기능구현한 파일이 여기에서 import된다
app = Flask(__name__)
# api = Api(app) # 이전 실습에서는 여기까지만 구현했음
# 아래와 같이, version, title, description... 등의 내용들을 추가해서 넣어줄 수 있다.
api = Api(
app,
version='0.1',
title="Scene's API Server",
description="Scene's Todo API Server!",
terms_url="/",
contact="oneofakindscene@gmail.com",
license="MIT"
)
# 이 파일이 아닌 외부에 있는 파일(to_do.py)에 클래스를 구현하고 여기서 import한 다음 add_resource()를 통해 클래스를 등록
api.add_namespace(Todo, '/todos') # namespace에서 url 들어갈 부분 머로 들어가는지 확인해야함 => 여기선 /todos
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=80)
api.add_namespace(Todo, '/todos') <- api 객체를 특정 경로('/todos')에 등록 할 수 있게함http://localhost/todos <- 이런식으로 api 호출할 때 사용http://localhost 이렇게만 접속해보면 아래 스샷과 같은 flask-RESTX의 기본 기능으로 제공하는 Swagger 기반의 홈페이지에서 api의 정보들을 확인할 수 있다.
version: API Server의 버전을 명시합니다.title: API Server의 이름을 명시합니다.description: API Server의 설명을 명시합니다.terms_url: API Server의 Base Url을 명시합니다.contact: 제작자 E-Mail 등을 삽입합니다.license: API Server의 라이센스를 명시 합니다.license_url: API Server의 라이센스 링크를 명시 합니다app.py에는 아래와 같이 구현됨
api = Api(
app,
version='0.1',
title="Scene's API Server",
description="Scene's Todo API Server!",
terms_url="/",
contact="onoofakindscene@gmail.com",
license="MIT"
)
"""Comment""")를 이용하여 Document에 설명을 추가 할 수 있습니다.@Todo.route('')
class TodoPost(Resource):
@Todo.expect(todo_fields)
@Todo.response(201, 'Success', todo_fields_with_id)
def post(self):
"""post comment : Todo 리스트에 할 일을 등록 합니다."""
global count
global todos
idx = count
count += 1
todos[idx] = request.json.get('data')
return {
'todo_id': idx,
'data': todos[idx]
}, 201

Namespace 객체도 생성자 파라미터를 조정하여, 내용을 수정 할 수 있습니다.
title: Namespace의 이름을 명시합니다.description: Namespace의 설명을 명시합니다.todo.py 에 있는 Namespace 객체의 생성자 파라미터를 수정하면 아래와 같다.
Todo = Namespace(
name="Namespace의 name : Todos",
description="Namespace의 Description : Todo 리스트를 작성하기 위해 사용하는 API.",
)
name, description이 적용된 모습
Namespace.model()에 대해 설명 하자면, Namespace.model()은 입력, 출력에 대한 스키마를 나타내는 객체flask_restx 내의 field 클래스를 이용하여, 설명(description), 필수 여부(required), 예시(example)를 넣을 수 있습니다.Namespace.inherit()을 이용하여, Namespace.model() 을 상속 받을 수 있습니다.to_do.py 에 있는
Namespace.model(),Namespace.inhert()를 활용하여 입력, 출력에 대한 스키마 정의해주기
Todo = Namespace(
name="Namespace의 name : Todos",
description="Namespace의 Description : Todo 리스트를 작성하기 위해 사용하는 API.",
)
todo_fields = Todo.model('Todo', { # Model 객체 생성
'data': fields.String(description='a Todo', required=True, example="what to do")
})
todo_fields_with_id = Todo.inherit('Todo With ID', todo_fields, { # todo_fields 상속 받음
'todo_id': fields.Integer(description='a Todo ID')
})
to_do.py파일에서 Namespace.model(), Namespace.inherit()을 사용해서 example 및 schema가 들어간 것을 확인할 수 있다.todo_fields, todo_fields_with_id 부분임
Namespace.doc() 데코레이터를 이용하여, Status Code 마다 설명을 추가하거나, 쿼리 파라미터에 대한 설명을 추가 할 수 있습니다.
params: dict 객체를 받으며, 키로는 파라미터 변수명, 값으로는 설명을 적을 수 있습니다.responses: dict 객체를 받으며, 키로는 Status Code(202, 500... 같은거), 값으로는 설명을 적을 수 있습니다.to_do.py 에서
Namespace.doc(params={dict}),Namespace.doc(responses={dict})적용된 부분
@Todo.route('/<int:todo_id>')
@Todo.doc(params={'todo_id': 'An ID'})
class TodoSimple(Resource):
[...]
@Todo.doc(responses={202: 'Success'})
@Todo.doc(responses={500: 'Failed'})
def delete(self, todo_id):
"""delete comment : To_do 리스트에 todo_id와 일치하는 ID를 가진 할 일을 삭제합니다."""
del todos[todo_id]
return {
"delete": "success"
}, 202

Namespace.Model 객체를 등록하면 됩니다.to_do.py 에서
Namespace.expect(),Namespace.response()된 부분
@Todo.route('')
class TodoPost(Resource):
@Todo.expect(todo_fields)
@Todo.response(201, 'Success', todo_fields_with_id)
def post(self):
"""post comment : To_do 리스트에 할 일을 등록 합니다."""
global count
global todos
idx = count
count += 1
todos[idx] = request.json.get('data')
return {
'todo_id': idx,
'data': todos[idx]
}, 201
Namespace.expect(), Namespace.response() 적용된 스샷
def와 달리 return {dict}, 200 이런식으로 안들어가는 이유@Todo.route('/<int:todo_id>')
@Todo.doc(params={'todo_id': 'An ID'})
class TodoSimple(Resource):
@Todo.response(200, 'Success', todo_fields_with_id)
@Todo.response(500, 'Failed')
def get(self, todo_id):
"""Todo 리스트에 todo_id와 일치하는 ID를 가진 할 일을 가져옵니다."""
return {
'todo_id': todo_id,
'data': todos[todo_id]
}