본문 바로가기

백엔드(FastAPI)

[백엔드 > FastAPI] 2장. 라우팅

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