FastAPI를 통하여 비즈니스 로직을 실제로 구현하고, 분석결과물을 다양한 방식으로 공유하기 위함, 또한 간단하게 Streamlit과 Tableau를 함께 활용하여 재밌는 프로젝트를 할 수 있겠다는 생각을 하게되었다.
또한 다른것과 달리 FastAPI장점으로 직관적이라는 부분과 스웨거를 따로 작성할 필요가 없어서 개인프로젝트나 POC단계에서 사용하기 적합한것같다.
기본적으로 rest기반으로 CRUD를 가지고 있다.
Create(데이터 생성) : Post
Read(데이터 조회) : Get
Update(데이터 수정) : Put
Delete(데이터 삭제) : DELETE
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message":"Hello World"}
보통 main파일 내에 다음과 같이 작성하는데 @app.get("/")을 통해서 endPoint를 지정해주게 된다. 해당 코드의 경우 추가 url이 없다고 보면된다.
또한 async는 로직을 병렬로 실행해주는 것으로 비동기처리를 해주는것이다.
병렬처리가 필요하지 않다면 async를 제외하고 def root(): 로 시작해도 된다.
uvicorn main:app -reload
실행하면 다음과같이 창이 뜨는것을 볼 수 있다.
여기서 FastAPI의 장점인 자동으로 스웨거를 짜주는 부분이 있는데 url뒤에 docs를 붙여주면된다.
이렇게 끝에 docs를 붙여주면 아래와같이 자동으로 생성되는것을 볼 수 있다.
참고
@app에서 사용한 @는 데코레이터라고 부른다.
파이썬에서 함수를 수정하지 않은 상태에서 추가 기능을 구현할 경우에 @를 붙여서 활용할 수 있는데 함수를 받아서 어떠한 로직을 처리하는것이다.
@app.get("/")의 경우에는 함수가 경로 "/" 에 해당하는 get(조회) 동작을 하라는 것이다.
즉 데코레이터 아래에 있는
async def root():
return {"message" : "Hello World"}
함수를 실행하는것이다.(async를 붙이지 않으면 비동기 아닌 상태로 사용이가능하다)
FastAPI에서 파이썬 기본 문법으로 매개변수를 경로에 삽입하여 사용할 수 있다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/itmes/{item_id}")
async def read_item(item_id):
return {"item_id" : item_id}
위의 코드를 main.py에 작성하고 실행후에 다음과 같이 엔드포인트를 구성해보면된다.
http://127.0.0.1:8000/items/안녕
이렇게 url을 치고 입력하면 아래와 같은 return값이 생긴다
{"item_id":안녕}
또한 FastAPI에서는 파이썬 표준 타입 annotations을 통해서 Path Parameter의 타입을 선언할 수 있는데 아래와 같이 item_id의 값을 정수형으로 명시해보자
from fastapi import FastAPI
app = FastAPI()
@app.get("/itmes/{item_id}")
async def read_item(item_id:int):
return {"item_id" : item_id}
해당 코드를 실행할경우 정수형만이 호출을 할 수 있다. 만약에 그렇지않다면 아래와 같은 에러가 생긴다.
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "안녕",
"url": "https://errors.pydantic.dev/2.4/v/int_parsing"
}
]
}
따라서 annotations을 통해서 특정 타입의 인자를 받게 만들 수 있다.
만약에 path파라미터를 받는 API가 있고, 미리 파라미터의 값을 정의하기를 원한다면 파이썬문법 Enum을 사용할 수 있다.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
위와 같이 함수를 구성했을때 "http://127.0.0.1:8000/models/alexnet"를 입력하게 되면 아래와 같이 결과가 떨어진다.
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
path파라미터에 해당 파라미터가 경로 즉, path임을 알려줄 수 있는 방법이있다.
만약에 이러한 과정없이 "/storage/my/myfile.txt" 같은 url을 입력한다면 에러가 난다. 따라서 path임을 지정해줘야한다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path : str):
return {"file_path":file_path}
지금까지 path파라미터를 활용해서 조회를 해봤다. path파라미터가 아닌 값을 함수의 파라미터로 선언하게 된다면, FastAPI는 자동으로 Query파라미터로 인식하게된다.
Query는 URL에서 ? 이후에 key-value쌍으로 이루어지고 &(엔드)로 분리가 된다.
코드 예시와 URL예시를 살펴보자
from fastapi import FastAPI
app = FastAPI()
fake_items_db = [{"item_name":"Foo"}, {"item_name":"Bar"}, {"item_name":"Baz"}]
@app.get("/items/")
async def read_item(skip:int = 0, limit:int = 10):
return fake_items_db[skip : skip + limit]
예를들어서 아래와 같은 URL이 있다고 하자.
http://127.0.0.1:8000/items/?skip=1&limit=1
query파라미터는 아래와 같다
만약에 파이썬 타입을 선언할 경우 해당타입으로 변환이된다.
(위와 같은 경우 query파라미터 skip과 limit에 대한 value를 int로 타입지정을 한것이다.)
query파라미터는 경로에서 고정된 부분이 아니고, 선택적일 수 있기 때문에 기본값을 가질 수 있다. 위에서 보여준 코드를 예를 들면 skip = 0, limit = 10 으로 기본값을 가지고 있다.
그렇기 때문에 아래와 같은 코드입력시
http://127.0.0.1:8000/items/
기본값으로 skip과 limit이 적용이 되어 아래와 같이 URL을 이동한것과 똑같다.
http://127.0.0.1:8000/items/?skip=0&limit=10
만약에 아래처럼 기본값의 쿼리값을 변경한다면 어떻게될까
http://127.0.0.1:8000/items/?skip=4
query값을 선택적으로 파라미터를 받을 수 있는 방법이 있다.
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: Union[str, None] = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
위의 함수의 경우 query파라미터는 q이며, optional한 파라미터입니다.
default값으로 None을 가지게 됩니다. 만약에 q의 값을 사용하기 위해서는
str타입으로 생성됩니다.
bool타입으로도 선언을 할 수 있으며, 다음과 같이 변환이 됩니다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q : str |None, short : bool = False):
item = {"item_id":item_id}
if q:
item.update({"q":q})
if not short:
item.update(
{"description":"This is an amazing item that has a long description"}
)
return item
한 API에서 다중으로 path파라미터와 query파라미터를 동시에 받을경우 FastAPI는 순서와 상관없이 이름으로 구분한다.
from typing import Union
from fastapi import FastAPI
app = FastAPI()
**@app.get("/users/{user_id}/items/{item_id}")**
async def read_user_item(
**user_id: int, item_id: str, q: Union[str, None] = None, short: bool = False**
):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
query파라미터를 선택적으로 받기 위해서는 default값을 None으로 설정하여 해결할 수 있었다. 하지만, query파라미터를 필수로 하기 위해서는 기본값을 선언해야한다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
item = {"item_id": item_id, "needy": needy}
return item
이렇게 만들게 되면 path파라미터인 item_id이후에 needy라는 query파라미터를 반드시 넣어주어야 한다 그렇지 않으면 에러가 뜬다.
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(
**item_id: str, needy: str, skip: int = 0, limit: Union[int, None] = None**
):
item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
return item
일부 query 파라미터는 필수, default값 혹은 optional하게 받을 수 있다.
위의 코드에서는 3가지의 query파라미터가 있따.