Flask로 REST API 구현하기 - 2. 파일 분리, 문서화
파일 분리
- 1편에서는
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="[email protected]",
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)
add_namespace()
api.add_namespace(Todo, '/todos')
<- api 객체를 특정 경로('/todos'
)에 등록 할 수 있게함http://localhost/todos
<- 이런식으로 api 호출할 때 사용
문서화
- api를 효율적으로 프론트앤드 개발자에게 전달 할 수 있는 방안이 문서화
http://localhost
이렇게만 접속해보면 아래 스샷과 같은flask-RESTX
의 기본 기능으로 제공하는 Swagger 기반의 홈페이지에서 api의 정보들을 확인할 수 있다.
Api() 활용해서 API 정보 적어주기
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="[email protected]",
license="MIT"
)
"""메소드설명"""
- 메소드를 정의한 후 바로 아랫줄에 Python의 Comment(
"""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()
Namespace 객체도 생성자 파라미터를 조정하여, 내용을 수정 할 수 있습니다.
title
: Namespace의 이름을 명시합니다.description
: Namespace의 설명을 명시합니다.todo.py 에 있는 Namespace 객체의 생성자 파라미터를 수정하면 아래와 같다.
Todo = Namespace(
name="Namespace의 name : Todos",
description="Namespace의 Description : Todo 리스트를 작성하기 위해 사용하는 API.",
)
- Namespace의
name
,description
이 적용된 모습
Namespace.model()
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()
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
- 적용된 스샷 : 파라미터와 Status Code에 대한 설명이 추가된 것을 알 수 있다.
Namespace.expect(), Namespace.response()
Namespace.expect()
- 말 그대로, 특정 스키마가 들어 올것을 기대 한다. 라고 보면 됩니다.
Namespace.Model
객체를 등록하면 됩니다.
Namespace.response()
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()
적용된 스샷
질문사항
- 아래 코드에서 status code가 200인 이유와 다른
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]
}
- 1) 202가 아닌 200인 이유는?
- 이는 HTTP Status code에 따릅니다. 200 Code는 요청 성공을 뜻하여, 202는 해당 요청이 허용 되었음을 알리는 코드입니다. https://ko.wikipedia.org/wi...
- 2) 그리고 다른 함수와 달리 def get()만 return에 콤마(,) 찍고 200이 안들어간 이유가 멀까요?
- 200은 default 값이기 때문에, 다른 코드는 status code를 명시 하여야 하지만, 200은 넣지 않아도 됩니다.
References
Author And Source
이 문제에 관하여(Flask로 REST API 구현하기 - 2. 파일 분리, 문서화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@oneofakindscene/Flask로-REST-API-구현하기-2.-파일-분리-문서화저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)