fastapi 사용 도중 예외처리를 스마트하게 할 수 있는 방법에 대해 고민해보았다.
보통 Backend 로직을 작성하다보면 MVC패턴에서 예외처리를 하게 되는데 try except를 덕지덕지 발라놓게 되면 python내에서 코드가 줄맞춤때문에 보기 힘들어지는 경우가 생깁니다.
try:
"""codes"""
except CustomException1 as e
"""exception errors"""
except Exception as e
"""errors"""
이런 코드들이 로직 중간에 듬성 듬성 들어가면 보기 싫어지고 또 어떤 상황에 특정 Exception을 걸기가 힘들어지는 현상도 일어난다.
fastapi에는 Exception을 Handling 할 수 있는 가이드가 존재합니다.
https://fastapi.tiangolo.com/tutorial/handling-errors/
가이드를 살펴보면 Exception에 해당하는 클래스를 만들어서 Handling에 사용합니다.
class InvalidUserId(Exception):
def __init__(self, user_id: str):
self.user_id: str = user_id
---------------------------------------------------------------
from .model import UserInfoRequest
from fastapi.responses import JSONResponse
from app.modules.errors import InvalidUserId
async def get_user_info(user_info_request: UserInfoRequest):
if user_info_request.user_id == "test_user_info":
return JSONResponse(user_info_request.dict())
else:
raise InvalidUserId(user_id=user_info_request.user_id)
---------------------------------------------------------------
from fastapi.responses import JSONResponse
from app.modules.errors import InvalidUserId
@app.exception_handler(InvalidUserId)
async def invalid_user_id_exception_handler(request: Request, exc: InvalidUserId):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content={
"content": "invalid user id",
"user_id": exc.user_id
}
)
해당 클래스를 만들고 raise를 이르키면 invalid_user_id_exception_handler로 넘어가게 됩니다.
test_user_info 를 user_id로 작성하게 된다면 정상적인 response를 반환하게 될 것이고 이외의 user_id를 요청하게 된다면 exception_handler에 작성 된 것처럼 나와야 할 것입니다.
fastapi에서 기본적으로 swagger를 제공하기 때문에 편리하게 테스트 해볼 수 있습니다.
위 스크린샷처럼 예측되어진 에러로 나타나게 될 것입니다. error handler를 디버깅을 해보겠습니다.
raise가 발생되었고 이 뒤는 error_handler로 넘거갈 것입니다.
예상대로 exception_handler에 넣어준 error class의 에러가 나왔을시 지정된 error handler로 넘어갔고 fast api의 작동방법에 따라 exc안에 class instance가 존재해야 합니다.
위와 같이 exc에는 error class가 존재하고 잘 전달되는 것으로 확인이 됩니다.
보통 service logic에서 error handlering이 필요하다. 이때 try: except:이나 함수에서 return으로 에러관련 class 혹은 값들을 내어주면 function과 class에서 한 가지 type의 값을 return 해주는 것이 아닌 여러 에러 값까지 내어주게 됩니다. python type hint로 return 값을 지정해주는 경우 이런 것들을 Union (3.10부터는 | 로도 가능합니다.) 으로 값을 지정해줘야 합니다. 이런경우 쓸 때 없이 코드가 길어지겠죠.
이런 경우 보통 raise를 사용하고 최외곽에서 try except으로 관리를 하게 됩니다. 하지만 이런 경우도 api를 만들때 api route 마다, controller(http method)마다 try: except:를 만들어 주어야겠죠.(다른방법이 있으면 공유 부탁드립니다!)
fastapi의 exception handler를 사용하게 되면 이런 경우들(모든 api)을 한거번에 처리가 가능하고 raise가 되는 error class instance를 생성 해서 error handlering에 반환 할 수 있고 이를 response 혹은 여러 로직들을 handler에 넣을 수 있겠습니다. 이런경우 편리해지겠죠.
예시코드 : https://github.com/seungjae08/fast-api-template
fastapi를 사용해 프로젝트를 구성할 때 더 많은 구성방법에 대해 앞으로도 계속해서 글을 작성해보겠습니다.