0. 강의 소개
이번 강의에서는 백엔드 프로그래밍에 사용되는 Fast API에 대해 다룰 예정입니다.
1. FastAPI 기본지식
1.0 Path Parameter? Query Parameter?
- 웹에서 GET Method를 사용해 데이터를 전송할 수 있음
- E.g. ID가 402인 사용자 정보를 가져오고 싶은 경우 방식
- Path Parameter
- /users/402
- 서버에 402라는 값을 전달하고 변수로 사용
- Query Parameter
- /users?id=402
- Query String
- API 뒤에 입력 데이터를 함께 제공하는 방식으로 사용
- Query String은 Key, Value의 쌍으로 이루어지며 &로 연결해 여러 데이터를 넘길 수 있음
- Path Parameter
- 언제 어떤 방식을 사용해야 할까?
- 상황마다 다르다!!
- Resource를 식별해야 하는 경우 : Path Parameter가 더 적합
- 정렬, 필터링을 해야 하는 경우 : Query Parameter가 더 적합
- 차이점!!
- Path Parameter : 저 경로에 존재하는 내용이 없으므로 404 Error 발생
- Query Parameter : 데이터가 없는 경우 빈 리스트가 나옴 => 추가로 Error Handling이 필요
1.1 Path Parameter
- GET Method : 정보를 READ하기 위해 사용
-
유저 정보에 접근하는 API 만들기
from fastapi import FastAPI import uvicorn app = FastAPI() @app.get("/users/{user_id}") def get_user(user_id): return {"user_id": user_id} if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
1.2 Query Parameter
-
아이템 리스트를 반환하는 API 만들기
from fastapi import FastAPI import uvicorn app = FastAPI() fake_items_db = [{"item_name":"Foo"}, {"item_name":"Bar"}, {"item_name":"Baz"}] @app.get("/items/") def read_item(skip: int=0, limit: int=10): return fake_items_db[skip: skip + limit] if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
1.3 Optional Parameter
-
있어도 되고 없어도 되는 변수 사용해보기
from typing import Optional from fastapi import FastAPI import uvicorn app = FastAPI() fake_items_db = [{"item_name":"Foo"}, {"item_name":"Bar"}, {"item_name":"Baz"}] @app.get("/items/{item_id}") def read_item(item_id: str, q: Optional[str]=None): if q: return {"item_id": item_id, "q": q} return {"item_id": item_id} if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
1.4 Request Body
- 클라이언트에서 API에 데이터를 보낼 때, Request Body를 사용함
- 클라이언트 => API : Request Body
- API의 Response => 클라이언트 : Response Body
- Request Body에 데이터가 항상 포함되어야 하는 것은 아님
- Request Body에 데이터를 보내고 싶다면 POST Method를 사용
- (참고) GET Method는 URL, Request Header로 데이터 전달
- POST Method는 Request Body에 데이터를 넣어 보냄
- Body의 데이터를 설명하는 Content-Type이란 Header 필드가 존재하고, 어떤 데이터 타입인지 명시해야 함
- 대표적인 컨텐츠 타입
- application/x-www-form-urlencoded : BODY에 Key, Value 사용. & 구분자 사용
- text/plain : 단순 txt 파일
- multipartform-data : 데이터를 바이너리 데이터로 전송
-
POST 요청으로 item 생성해보기
from typing import Optional from fastapi import FastAPI import uvicorn from pydantic import BaseModel class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None app = FastAPI() @app.post("/items/") def creat_item(item: Item): return item if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
1.5 Response Body
- API의 Response => 클라이언트 : Response Body
- Decorator의 response_model 인자로 주입 가능
- 역할
- Output Data를 해당 정의에 맞게 변형
- 데이터 Validation
- Response에 대한 Json Schema 추가
- 자동으로 문서화
-
확인해보자!!
from typing import Optional from fastapi import FastAPI import uvicorn from pydantic import BaseModel class ItemIn(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None class ItemOut(BaseModel): name: str price: float tax: Optional[float] = None app = FastAPI() @app.post("/items/", response_model=ItemOut) def creat_item(item: ItemIn): return item if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
1.6 Form, File
- python-multipart를 설치해야 함
- 프론트도 간단히 만들기 위해 Jinja2 설치
pip install python-multipart pip install Jinja2
-
사용해보기
from fastapi import FastAPI, Form from fastapi.templating import Jinja2Templates import uvicorn app = FastAPI() templates = Jinja2Templates(directory='./') # 주의!! url으로 접근 get method를 요청하기 때문에 없으면 오류가 난다 @app.get("/login/") def get_login_form(request: Request): return templates.TemplateResponse('login_form.html', context={'request': request}) # ...은 Python ellipsis : Required(꼭 필수 요소)를 의미 @app.post("/login/") def login(username: str = Form(...), passwrod: str = Form(...)): return {"username": username} if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
-
파일 업로드를 위한 기능 만들어 보기
from typing import List from fastapi import FastAPI, File, UploadFile from fastapi.response import HTMLResponse import uvicorn app = FastAPI() @app.post("files/") def create_files(files: List[bytes] = File(...)): return {'file_size': [len(file) for file in files]} @app.post("uploadfiles/") def create_upload_files(files: List[UploadFile] = File(...)): return {'filenames': [file.filename for file in files]} # “/”로 접근할 때 보여줄 HTML 코드 @app.get("/") def main(): content = """ <body> <form action="/files/" enctype="multipart/form-data" method="post"> <input name="files" type="file" multiple> <input type="submit"> </form> <form action="/uploadfiles/" enctype="multipart/form-data" method="post"> <input name="files" type="file" multiple> <input type="submit"> </form> </body> """ return HTMLResponse(content=content) if __name__ == '__main__': uvicorn.run(app, host="0.0.0.0", port=8000)
2. Pydantic
2.1 Pydantic
- FastAPI에서 Class 사용할 때 보이던 Pydantic
- Data Validation / Settings Management 라이브러리
- Type Hint를 런타임에서 강제해 안전하게 데이터 핸들링
- 파이썬 기본 타입(String, Int 등) + List, Dict, Tuple에 대한 Validation 지원
- 기존 Validation 라이브러리보다 빠름 Benchmark
- Config를 효과적으로 관리하도록 도와줌
- 머신러닝 Feature Data Validation으로도 활용 가능
2.2 Pydantic Validation
- Machine Learning Model Input Validation
- Online Serving에서 Input 데이터를 Validation하는 Case
- Validation Check Logic
- 조건 1: 올바른 url을 입력 받음 (url)
- 조건 2: 1-10 사이의 정수 입력 받음 (rate)
- 조건 3: 올바른 폴더 이름을 입력 받음(target_dir)
- 사용할 수 있는 방법
- 1) 일반 Python Class를 활용한 Input Definition 및 Validation
- Python Class로 Input Definition 및 Validation => 의미 없는 코드가 많아짐
- 복잡한 검증 로직엔 Class Method가 복잡해지기 쉬움
- Exception Handling을 어떻게 할지 등 커스텀하게 제어할 수 있는 있지만 메인 로직(Input을 받아서 Inference를 수행하는)에 집중하기 어려워짐
- 2) Dataclass를(python 3.7 이상 필요) 활용한 Input Definition 및 Validation
- 인스턴스 생성 시점에서 Validation을 수행하기 쉬움
- 여전히 Validation 로직들을 직접 작성해야 함
- Validation 로직을 따로 작성하지 않으면, 런타임에서 type checking을 지원하지 않음
- 3) Pydantic을 활용한 Input Definition 및 Validation
- 훨씬 간결해진 코드 (6라인)(vs 52라인 Python Class, vs 50라인 dataclass)
- 주로 쓰이는 타입들(http url, db url, enum 등)에 대한 Validation이 만들어져 있음
- 런타임에서 Type Hint에 따라서 Validation Error 발생
- Custom Type에 대한 Validation도 쉽게 사용 가능
- 1) 일반 Python Class를 활용한 Input Definition 및 Validation
- 참고
2.3 Pydantic Config
- Pydantic은 Config을 체계적으로 관리할 방법을 제공
- 기존에 다른 라이브러리들은 어떻게 Config를 설정하고 있을까?
- 애플리케이션은 종종 설정을 상수로 코드에 저장함
- 이것은 Twelve-Factor를 위반
- Twelve-Factor는 설정을 코드에서 엄격하게 분리하는 것을 요구함
- Twelve-Factor App은 설정을 환경 변수(envvars나 env라고도 불림)에 저장함
- 환경 변수는 코드 변경 없이 쉽게 배포 때마다 쉽게 변경할 수 있음
- 참고
- 관리하기!
- 1) .ini, .yaml 파일 등으로 config 설정하기
- 2) flask-style config.py
- 3) pydantic base settings
- 1) .ini, .yaml 파일 등으로 config 설정하기
Reference
- AI boot camp 2기 서빙 강의