코드로 우주평화
FastAPI _ Custom Exception 만드는 방법 본문
코드를 작성하다 보면 특정 상황에 대한 예외처리를 만들고 싶을 때가 있다.
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 |