코드를 작성하다 보면 특정 상황에 대한 예외처리를 만들고 싶을 때가 있다.
FastAPI는 일반적으로 HTTPException을 이용해 예외를 처리하는데,
유사한 예외를 반복적으로 처리해야 한다면 우리가 직접 Exception을 커스텀해서 사용할 수 있다.
이번 글에서는 FastAPI에서 Custom Exception을 어떻게 구현하는지 코드로 설명해보겠다.
구현 시작
먼저 아주 간단한 api를 작성해보았다.
from fastapi import FastAPI
app = FastAPI()
@app.get("/{name}")
def home(name:str):
return {"detail": f"Hello, {name}"}
name을 쿼리 스트링으로 넘겨주면 "Hello, {name}"으로 반환해주는 api다.
하지만 name이 다섯 글자를 초과해서는 안된다고 가정해보자.
그럼 다음처럼 구현할 수 있을 것이다.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/{name}")
def home(name:str):
if len(name) > 5:
raise HTTPException(status_code=400, detail="invalid name")
else:
return {"detail": f"Hello, {name}"}
현재는 이것만으로도 충분하지만 만약 유사한 예외들이 많아진다면 하나로 묶는 게 더 편할 수 있다.
FastAPI에서는 클래스를 만들어 개발자가 유연하게 예외처리를 할 수 있도록 도와준다.
Exception 클래스 구현
방법은 아주 간단하다.
class InvalidName(Exception):
pass
새롭게 만들고자 하는 예외처리 클래스에 Exception을 상속받는다.
그리고,
끝이다.
이제 해당 클래스를 raise로 호출하면 except로 잡을 수 있다.
@app.get("/{name}")
def home(name:str):
try:
if len(name) > 5:
raise InvalidName
else:
return {"detail": f"Hello, {name}"}
except InvalidName:
raise HTTPException(status_code=400, detail=f"{name} is too long")
이것으로 나만의 예외 클래스를 만들었다! 야호~
기본값 메시지 지정
예외처리 클래스에 기본 메시지를 지정해 줄 수도 있다.
다음처럼 클래스 내에 생성자를 두면, 이후 속성을 호출하여 사용할 수 있다.
class InvalidName(Exception):
def __init__(self, detail: str = "invalid name"):
self.detail = detail
@app.get("/{name}")
def home(name:str):
try:
if len(name) > 5:
raise InvalidName
else:
return {"detail": f"Hello, {name}"}
except InvalidName as error:
raise HTTPException(status_code=400, detail=error.detail)
위 코드는 raise InvalidName 호출과 동시에 "invalid name"이라는 기본 메시지가 detail 변수에 할당되고
except에서 error.detail처럼 속성으로 불러와 사용할 수 있다.
사용자 메시지 지정
또한 내가 원하는 메시지를 직접 지정할 수도 있다.
@app.get("/{name}")
def home(name:str):
try:
if len(name) > 5:
raise InvalidName(f"{name} invalid name")
else:
return {"detail": f"Hello, {name}"}
except InvalidName as error:
raise HTTPException(status_code=400, detail=error.detail)
이제 메시지의 기본값은 f"{name} invalid name"로 대체되어 클라이언트로 반환한다.
마무리
예외처리를 커스텀할 수 있는 건 알겠는데 코드는 오히려 더 복잡해졌다?
사실 위의 코드는 사용자 예외처리가 적합하지 않다.
처음에 언급한 것처럼 단일 케이스보다는
유사한 예외 케이스가 많고, 유연하게 컨트롤할 수 있어야 할 때,
그때 Custom Exception는 큰 위력을 발휘한다.
아래 코드를 보자.
실습 코드에서 유효성 검사 함수를 추가했다.
@app.get("/{name}")
def home(name:str):
try:
validation_name(name)
return {"detail": f"Hello, {name}"}
except InvalidName as error:
raise HTTPException(status_code=400, detail=error.detail)
def validation_name(name):
if len(name) > 5:
raise InvalidName
if len(name) < 2:
raise InvalidName
if not name.isalpha():
raise InvalidName("not a string")
세 가지 항목에 대해 검사를 하는데 단순히 InvalidName를 raise 하는 것만으로 반복되는 예외 케이스들을 처리할 수 있다.
메시지는 기본값으로 반환되고, 필요한 경우에만 직접 메시지를 지정할 수도 있다.
덧붙이자면, 메시지 기본값 외에도 다양한 조건과 연산들을 클래스 내에 추가하여 예외를 처리할 수 있다.
앞으로는 Custom Exception으로 즐겁게 예외 핸들링을 해보자 :)
'나는 이렇게 학습한다 > Framework' 카테고리의 다른 글
Django 에서 middleware 추가하기 (0) | 2023.09.26 |
---|---|
FastAPI _ BaseSettings 을 lru_cache 할 때, unhashable type 에러 해결방법 (0) | 2022.10.26 |
DRF 궁금한 것 모음 (0) | 2022.02.03 |
'ManyToManyField' 또는 '중간테이블'로 데이터 가져오는 방법 (0) | 2021.11.19 |
장고 용어 정리 (0) | 2021.11.11 |