본문 바로가기

FastAPI

에러처리

어떤 프로그래밍 언어를 사용하던 에러가 발생합니다. try문을 이용하는 것도 한 가지 방법이지만 좋은 방법은 아닙니다. 만약 해당 에러가 발생하는 모든 코드에 대해 try문을 작성해야 하고, 이는 다시 중복 코드 사용으로 이어집니다.

 

에러 처리를 본격적으로 다루기 전에 에러를 일부러 발생시켜 봅시다.

 

아래와 같이 main.py를 작성을 해봅시다.

import uvicorn
from fastapi import FastAPI, HTTPException, status

app = FastAPI()

users = {0101
    1: {"name": "Fast"},
    2: {"name": "Campus"},
    3: {"name": "API"},
}


@app.get("/users/{user_id}")
async def get_user(user_id: int):
    if user_id not in users.keys():
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"<User: {user_id}> is not exists.",
        )
    return users[user_id]

if __name__ == "__main__":
	uvicorn.run("main:app", reload = True)

 

위의 코드는 users dict에 user_id가 없으면, 404 Not Found 에러를 내어 사용자가 없다는 에러가 발생하는 코드입니다.

 

여기서 중요한점은 무조건 에러를 발생하지 않게 try except문을 사용하는 것인데요!!

 

이는 좋은 방법이 아닙니다. try except문을 사용하는 경우 보통 프로그램이 죽지 않게 하는 습관 때문인 경우가 많습니다.

 

브라우저의 경우, 위와 같이 404 Not Found와 같은 명확한 에러가 발생하더라도 죽지 않습니다. 왜냐하면, 서버는 일종의 데몬으로 시스템의 백그라운드에서 동작함으로 에러가 발생을 하더라도 죽지 않습니다(회원 가입시 아이디를 입력하지 않고 가입하기 누를 경우에).

 

치명적인 Error일 경우라면, 이야기가 달라지긴 합니다. 그럴 경우,  SIGKILL인터럽트가 발생하여 서버가 죽습니다.

 

보통 파이썬의 경우, 표준 에러나 HTTPException등을 이용하기보다는 사용자 정의 에러를 정의해서 사용합니다.

 

사용자 정의 Error main.py 코드입니다.

import uvicorn
from fastapi import FastAPI

app = FastAPI()


class SomeError(Exception):
    def __init__(self, name: str, code: int):
        self.name = name
        self.code = code

    def __str__(self):
        return f"<{self.name}> is occured. code: <{self.code}>"


app = FastAPI()


@app.get("/error")
async def get_error():
    raise SomeError("Hello", 500)

if __name__ == "__main__":
    uvicorn.run("main:app", reload = True)

 

SomeError라는 사용자 정의 에러를 만들었습니다. 

 

하지만, 해당 코드로는 해당 에러가 어떤 에러인지를 명확하게 파악하기 힘들다는 단점이 있습니다. 그래서 다음과 같이 SomeError를 처리할 때 핸들러를 만들 수 있습니다.

 

import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()


class SomeError(Exception):
    def __init__(self, name: str, code: int):
        self.name = name
        self.code = code

    def __str__(self):
        return f"<{self.name}> is occured. code: <{self.code}>"


app = FastAPI()

@app.exception_handler(SomeError)
async def some_error_handler(request: Request, exc: SomeError):
    return JSONResponse(
        content={"message": f"error is {exc.name}"}, status_code=exc.code
    )


@app.get("/error")
async def get_error():
    raise SomeError("Hello", 500)

if __name__ == "__main__":
    uvicorn.run("main:app", reload = True)

 

HTTP 에러도 작성해서 에러를 작성해 볼 수 있습니다.

 

import uvicorn
from typing import Any, Optional, Dict
from fastapi import FastAPI, HTTPException


class SomeFastAPIError(HTTPException):
    def __init__(
        self,
        status_code: int,
        detail: Any = None,
        headers: Optional[Dict[str, Any]] = None,
    ) -> None:
        super().__init__(
            status_code=status_code, detail=detail, headers=headers
        )


app = FastAPI()


@app.get("/error")
async def get_error():
    raise SomeFastAPIError(500, "Hello")

if __name__ == "__main__":
    uvicorn.run("main:app", reload = True)

 

FastAPI의 기본 에러를 상속 받으면 자동으로 에러 코드와, 내용, 그리고 헤더를 예쁘게 응답해줍니다. 물론 이 에러도 따로 핸들러에서 처리해주는 방법 역시 가능합니다. 

'FastAPI' 카테고리의 다른 글

의존성 주입 - 2  (0) 2024.02.18
의존성 주입 - 1  (1) 2024.02.15
파일처리-2  (0) 2024.02.11
파일처리 - 1  (0) 2024.02.10
Form 데이터  (0) 2024.02.05