일기장 앱을 만드는 프로젝트를 예시로 들어보려고 한다.
프로젝트 구조는 다음과 같다
main.py : uvicorn 서버 실행 메소드가 담겨있는 메인 파일 , python3 app/main.py 로 실행하면 서버가 실행된다.
server 폴더
routes 폴더 : 라우터 파일들이 담겨있다. django의 urls.py 와 views.py 같은 느낌이라고 생각하면 될 것 같다
app.py : main.py 실행시 실행된다. FastAPI 앱 인스턴스 생성과 라우터 연결 등의 역할을 맡고 있다.
database.py : database와 연결된 부분을 관리한다. db에 정보를 저장하거나 불러오는 함수들을 가지고 있다.
exceptions.py : 예외처리 클래스들이 구현되어있다.
models.py : model 들이 구현되어있다. 응답양식, 데이터 모델 등등
자세한 코드를 알아보자
#main.py
import uvicorn
if __name__ == "__main__":
uvicorn.run("server.app:app",host = "0.0.0.0", port = 8000, reload = True)
main.py 실행시 uvicorn을 이용해 app.py를 실행하는 서버를 띄운다.
#app.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from .routes import diary_router,search_router
from app.server.exceptions import APIException
app = FastAPI()
app.include_router(diary_router.router)
app.include_router(search_router.router)
@app.exception_handler(APIException)
async def api_exception_handler(request: Request, exc: APIException):
return JSONResponse(
status_code = exc.status_code,
content = exc.content
)
app.py에선 FastAPI객체를 만들고 router를 추가한다.
예외처리 세팅도 여기서 한다.
#diary_router.py
from fastapi import APIRouter
from fastapi.encoders import jsonable_encoder
from app.server import database
from app.server.models import responseModel,Diary,UpdateDiary
router = APIRouter(prefix = "/diaries")
@router.get("",response_description="get all diaries")
async def get_all_diaries():
response_message = "all diaries"
diaries = await database.get_diaries()
return responseModel(response_message, diaries)
@router.get("/{date}", response_description="read a diary")
async def get_a_diary(date: str):
response_message = "read a diary"
diary = await database.get_diary(date)
return responseModel(response_message,diary)
/diaries 로 시작하는 url이 들어왔을때 app.py 에서 diary_router로 연결을 해준다.
diary_router에선 database.py의 db조회 함수를 사용해 db에서 일기장 정보들을 조회해 받아온다.
받아온 정보를 responseModel을 이용해 client에 반환한다.
#database.py
from motor import motor_asyncio
from .models import Diary
from app.server.exceptions import *
host = 'localhost'
port = 27017
#database settings
def get_db():
client = motor_asyncio.AsyncIOMotorClient(host,port)
db = client.iaryda
return db
diary_collection = get_db().get_collection("diary")
#helpers
def diary_helper(diary) -> dict:
diary.pop("_id")
return diary
#db 조회, 관리 함수
async def get_diaries() -> list:
result_data = []
async for diary in diary_collection.find():
result_data.append(diary_helper(diary))
return result_data
async def get_diary(date: str) -> dict:
diary = await diary_collection.find_one({"date" : date})
if diary != None:
return diary_helper(diary)
else:
raise DiaryDoseNotExistExecption()
database.py파일에서 db연결 및 조회 함수를 만들었다.
motor 를 이용해 몽고디비와 파이썬을 연결했다.
몽고디비에서 document를 저장할때 _id 항목을 생성해주는데
_id 항목은 굳이 client에 전달할 필요가 없으므로 _id를 제외해주는 diary_helper 함수를 만들어 사용했다.
오류가 발생하는 경우 exception 클래스를 이용해 오류 메세지를 전달한다.
#models.py
def responseModel(message, data) -> dict:
if type(data) == list:
return {
"status" : 200,
"message" : message,
"data" : data
}
else:
return {
"status" : 200,
"message" : message,
"data" : [data]
}
responseModel 함수는 models.py에 구현돼있다.
일정한 양식으로 응답을 할 수 있도록 해준다.
#exceptions.py
class StatusCode:
HTTP_500 = 500
HTTP_200 = 200
def make_error_content(message) -> dict:
return {
"status" : StatusCode.HTTP_500,
"message" : message,
"data" : []
}
class APIException(Exception):
status_code : int
content: dict
class DiaryDoseNotExistExecption(APIException):
def __init__(self,):
self.status_code = StatusCode.HTTP_500
self.content = make_error_content("Unvalid date. There is no diary in this date")
exceptions.py에선 오류 메세지 전달을 구현했다.
Exception객체를 상속하는 APIException 클래스를 먼저 만든 후 app.py에 exception_handler를 설정해준다.
그 이후 APIException 을 상속하는 exception 클래스들을 만든 후 사용하면 된다.
결론은 사용자에게서 요청이 오면
app.py -> router -> database -> router -> 응답 반환 순으로 이루어진다.
router 파일들을 좀 더 손볼 필요가 있을 것 같긴한데
일단 이렇게 만들어놨다.
'FastAPI' 카테고리의 다른 글
사지방에서 FastAPI 공부하기 #1 - 개발환경 설정 (0) | 2023.03.19 |
---|