2.1 FastAPI 라우팅
라우팅 이란
- 클라이언트가 서버로 보내는 HTTP 요청을 처리하는 프로세스
- HTTP 요청이 지정한 라우트로 전송되면 미리 정의된 로직이 해당 요청을 처리해서 반환(응답)한다.
FastAPI 특장점
- 쉽고 유연하며 소규모는 물론 대규모 애플리케이션 개발 시에도 사용 가능
HTTP 요청 메서드 란
- HTTP 메서드 처리 유형을 정의하는 식별자
- 메서드 종류에는 GET, POST, PUT, PATCH, DELETE 등이 있다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def welcome() -> dict:
return {
"message": "Hello World"
}
$ uvicorn api:app --port 8000 --reload
FastAPI() 인스턴스를 라우팅 작업에 사용할 수 있음
단점)
- uvicorn이 하나의 엔트리 포인트만 실행할 수 있기 때문에 라우팅 중에 단일 경로만 고려하는 애플리케이션에서만 사용됨
여러 함수를 사용하는 연속적인 라우트 처리
- APIRouter 클래스를 사용하면 다중 라우팅을 허용하여 문제 해결
2.2 APIRouter 클래스를 사용한 라우팅
APIRouter 클래스
- 다중 라우팅을 위한 경로 처리 클래스
- fastapi 패키지에 포함되어 있음
from fastapi import APIRouter
router = APIRouter()
@router.get("/hello")
async def say_hello() -> dict:
return {
"message": "Hello!"
}
< 코드 설명 >
- APIRouter 클래스를 임포트한 후 APIRouter() 인스턴스를 생성
- 라우팅 메서드는 위와 같이 APIRouter() 인스턴스를 사용해 생성
from fastapi import APIRouter
todo_router = APIRouter()
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: dict) -> dict:
todo_list.append(todo)
return {
"message": "Todo added successfully."
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
< 코드 설명 >
- APIRouter 클래스를 임포트한 후 APIRouter() 인스턴스를 생성
- todo 처리를 위해 두 개의 라우트를 추가
- todo_list에 todo를 추가하는 POST 메서드
- 모든 todo 아이템을 todo_list에서 조회하는 GET 메서드
주의사항
- APIRouter 클래스를 사용해 정의한 라우트를 FastAPI() 인스턴스에 추가해야 외부에서 접근 간으
- todo 라우트를 외부로 공개하기 위해 include_router() 메서드를 사용하여 todo_router를 FastAPI() 인스턴스에 추가한다.
include_router()
- include_router(router1, router2, ... ) 메서드는 APIRouter 클래스로 정의한 라우트를 메인 애플리케이션의 인스턴스로
추가해야함.
- 이렇게 하면 라우트를 전체 애플리케이션에서 사용 가능
api.py
- todo.py 파일에서 생성한 todo_router를 임포트
from fastapi impoort FastAPI
from todo import todo_router
app = FastAPI()
@app.get("/")
async def welcome() -> dict:
return {
"message": "Hello World"
}
app.include_router(todo_router)
todo 라우트 실행을 위한 POST 요청 전달 및 아이템을 todo_list에 추가
curl -X 'POST' \
'http://127.0.0.1:8000/todo' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"id": 1,
"item": "First Todo is to finish this book!"
}
{
"message": "Todo added successfully."
}
2.3 pydantic 모델을 사용한 요청 바디 검증
중요 기능
- 데이터만 전송되도록 요청 바디를 검증할 수 있음
- 요청 데이터가 적절한지 확인하고 악의적인 공격의 위험을 줄여줌
모델
- 데이터가 어떻게 전달되고 처리돼야 하는지를 정의하는 구조화된 클래스
- 모델은 pydantic의 BaseModel 클래스의 하위 클래스로 생성됨
- 요청 바디 객체와 요청 응답 (request-response) 객체 유형에 관한 힌트 제공
pydantic
- pydantic은 파이썬의 타입 어노테이션을 사용해서 데이터를 검증하는 파이썬 라이브러리
pydantic을 사용해 요청 바디를 검증하는 부분만 살펴본다.
from pydantic import BaseModel
class PacktBook(BaseModel):
id: int
Name: str
Publishers: str
Isbn: str
- PacktBook 모델은 pydantic의 BaseModel 클래스의 하위 클래스로 정의되며, 네 개의 필드만 가짐
다른 형태의 요청 바디를 보낼 수도 있다.
- 필요한 요청 바디 구조를 모델로 만듬
- 요청 바디의 유형(type)에 할당하면 모델에 정의된 데이터 필드만 처리함
예)
from pydantic improt BaseModel
class Todo(BaseModel):
id: int
item: str
두 개의 필드만 허용하는 pydantic 모델을 만듬
- 정수형(int)인 id
- 문자열형(str)인 item
모델을 POST 라우트에 사용한 사례
from model import Todo
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: Todo) -> dict:
todo_list.append(todo)
return {
"message": "Todo added successfully."
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
빈 딕셔너리를 요청 바디로 보내서 모델이 제대로 검증되는지 확인
curl -X 'POST' \
'http://127.0.0.1:8000/todo' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{}'
명령 실행하면,
- 요청 바디에 id와 item이 없다는 오류 메시지가 반환됨
- "msg": "field required",
- "type": "value_error.missing"
모델과 일치하는 데이터를 보내면,
curl -X 'POST' \
'http://127.0.0.1:8000/todo' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"id": 2,
"item": "Validation models help with input types"
}
모델과 일치하는 데이터를 보내면 다음과 같이 정상 응답을 받게됨
{
"message": "Todo added successfully."
}
중첩 모델
pydantic 모델은 다음과 같이 중첩해서 정의 가능
class Item(BaseModel)
item: str
status: str
class Todo(BaseModel)
id: int
item: Item
중첩 결과: Todo 형의 데이터는 다음과 같이 표현됨
{
"id": 1,
"item": {
"item": "Nested models",
"status": "completed"
}
}
2.4 경로 매개변수와 쿼리 매개변수
경로 매개변수
- (리소스를 식별하기 위해) API 라우팅에 사용됨
- 식별자 역할을 함
- 웹 애플리케이션이 추가 처리를 할 수 있도록 연결 고리가 되기도 함
하나의 todo 작업만 추출하는 새로운 라우트를 만들어보자.
from model import Todo
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: Todo) -> dict:
todo_list.append(todo)
return {
"message": "Todo added successfully."
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(todo_id: int) -> dict:
for todo in todo_list:
if todo.id == todo_id:
return {
"todo": todo
}
return {
"message": "Todo with supplied ID doesn't exist."
}
< 코드 설명 >
- {todo_id}가 경로 매개변수
- 매개변수를 통해 애플리케이션이 지정한 ID와 일치하는 todo 작업을 반환 가능
추가한 라우트를 테스트해보자.
curl -X 'POST' \
'http://127.0.0.1:8000/todo/1' \
-H 'accept: application/json'
명령을 실행하면 다음과 같이 ID가 1인 todo가 반환된다.
{
"todo": {
"id": 1,
"item": "First Todo is to finish this book!"
}
}
Path 클래스
- 라우트 함수에 있는 다른 인수와 경로 매개변수를 구분하는 역할
- 스웨거(Swagger)와 ReDoc 등으로 OpenAPI 기반 문서를 자동 생성할 때 라우트 관련 정보를 함께 문서화하도록 도움
from model import Todo
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: Todo) -> dict:
todo_list.append(todo)
return {
"message": "Todo added successfully."
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(todo_id: int = Path(..., title="The ID of the todo to retrieve.")) -> dict:
for todo in todo_list:
if todo.id == todo_id:
return {
"todo": todo
}
return {
"message": "Todo with supplied ID doesn't exist."
}
Path(..., kwargs)
- Path 클래스는 첫 인수로 None 또는 ... 을 받을 수 있다.
- 첫번째 인수가 ...이면 경로 매개변수를 반드시 지정해야한다.
- 경로 매개변수가 숫자이면 수치 검증을 위한 인수를 지정할 수 있다.
- (예) gt(greater than, ~보다 큰), le(less than, ~보다 작은)와 같은 검증 기호를 사용할 수 있다.
- 이를 통해 매개변수에 사용된 값이 특정 범위에 있는 숫자인지 검증 가능
쿼리 매개변수
- 선택 사항
- 보통 URL에서 ? (물음표) 뒤에 온다.
- 제공된 쿼리를 기반으로 특정한 값을 반환 또는 요청을 필터링할 때 사용됨
- 쿼리는 라우트 처리기의 인수로 사용되지만 경로 매개변수와 다른 형태로 정의됨
- (예) Query 클래스의 인스턴스를 만들어서 라우트 처리기의 인수로 쿼리 정의 가능
async query_route(query: str = Query(None):
return query
2.5 요청 바디
- 요청 바디는 POST와 UPDATE 등 라우팅 메서드를 사용해 API로 전달되는 데이터.
POST와 UPDATE
- POST 메서드는 새로운 데이터를 서버에 추가할 때 사용됨
- UPDATE 메서드는 서버상에 있는 기존 데이터를 변경할 때 사용됨
curl -X 'POST' \
'http://127.0.0.1:8000/todo' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"id": 2,
"item": "Validation models help with input types"
}
위 요청에서 요청 바디는 다음과 같다.
{
"id": 2,
"item": "Validation models help with input types"
}
FastAPI는 추가 검증을 할 수 있는 Body 클래스를 제공함
자동 문서화
- 모델은 API 라우트와 요청 바디의 유형을 자동으로 문서화 할 때 사용됨
FastAPI 자동 문서화
- 모델의 JSON 스키마 정의를 생성하고 라우트, 요청 바디의 유형, 경로 및 쿼리 매개변수,
응답 모델 등을 자동으로 문서화함.
- 문서는 다음 두 가지 유형으로 제공됨.
- 스웨거
- ReDoc
스웨거
- 스웨거는 인터랙티브 문서(사용자가 실행할 수 있는 문서)를 제공하여 API를 테스트할 수 있도록 돕는다.
- 스웨거 문서를 보려면 애플리케이션 주소의 끝에 /docs를 붙인다.
- http://127.0.0.1:8000/docs
ReDoc
- ReDoc 문서는 모델, 라우트, API에 관한 정보를 더 직관적이고 상세하게 전달한다.
- 애플리케이션 주소의 끝에 /redoc을 추가하면 ReDoc 문서를 볼 수 있다.
- http://127.0.0.1:8000/redoc
데이터의 샘플 설정
- 사용자가 입력해야할 데이터 샘플 설정 가능
- 사용자가 JSON 스키마를 올바르게 생성하기 위함
- 샘플 데이터는 모델 클래스 안에 Config 클래스로 정의하면 된다.
Todo 모델 클래스에 샘플 데이터를 추가해보자.
class Todo(BaseModel):
id: int
item: str
class Config:
schema_extra = {
"example": {
"id": 1,
"item": "Example Schema!"
}
}
- 문서화 페이지를 새로고침 -> [Add Todo] 클릭 -> 오른쪽 패널에 추가한 샘플이 표시됨
FastAPI 자동 문서화 이점
- 문서를 기반으로 API에 요청을 보내거나 애플리케이션 테스트 가능
- 업데이트된 문서는 API 사용법을 알려주는 안내서 역할을 함
p61.
'백엔드(FastAPI)' 카테고리의 다른 글
[백엔드 > FastAPI] 8장. 테스트 (0) | 2024.01.31 |
---|---|
[백엔드 > FastAPI] 7장. 보안 (0) | 2024.01.20 |
[백엔드 > FastAPI] 5장. 구조화 (0) | 2024.01.06 |