FastAPI 서버개발, 스키마 – 요청, 요청의 정의

오늘 우리는 FastAPI 서버 개발 과정에서 매우 중요한 개념인 요청(Requests)과 스키마(Schema)에 대해 깊이 있게 탐구할 것입니다. FastAPI는 Python으로 작성된 웹 프레임워크로, API를 신속하게 구축하고 문서화하는데 최적화되어 있습니다. 우리의 목표는 기본적인 요청 처리부터 시작하여 요청 스키마를 정의하고 활용하는 방법까지 광범위하게 알아보는 것입니다.

1. FastAPI란?

FastAPI는 Python으로 작성된 현대적이고 빠른 웹 프레임워크로, RESTful API를 만드는 데 아주 적합합니다. Pydantic 최적화를 사용하여 신뢰성과 속도에서 매우 우수합니다. FastAPI는 자동으로 API 문서를 생성하는 Swagger UI와 ReDoc와 같은 도구들을 제공하여 개발자들이 보다 쉽게 작업할 수 있도록 돕습니다.

FastAPI의 주요 특징은 다음과 같습니다:

  • 고속 성능: Starlette와 Pydantic의 기반 위에 설계되어 빠릅니다.
  • 직관적인 코드 작성: 타입 힌트를 사용하여 코드의 가독성을 높입니다.
  • 자동 문서화: 요청 및 응답 스키마에 따라 자동으로 API 문서를 생성합니다.
  • 비동기 지원: 비동기 프로그래밍을 자연스럽게 지원합니다.

2. 요청(Request)과 응답(Response) 개념

웹 애플리케이션에서 클라이언트는 서버에 요청을 보내고, 서버는 클라이언트에게 응답을 보냅니다. 이 요청/응답 구조는 RESTful API의 핵심입니다.

요청은 크게 다음과 같이 분류될 수 있습니다:

  • GET 요청: 서버로부터 자원을 요청합니다.
  • POST 요청: 서버에 새로운 자원을 생성합니다.
  • PUT 요청: 서버에 존재하는 자원을 수정합니다.
  • DELETE 요청: 서버에서 자원을 삭제합니다.

3. FastAPI에서 요청 처리하기

FastAPI에서 클라이언트의 요청을 처리하기 위해서는 엔드포인트를 정의해야 합니다. 엔드포인트는 URL 경로와 HTTP 메서드(GET, POST 등)로 구성됩니다. 요청 처리를 위한 최소한의 FastAPI 코드 예시를 살펴보겠습니다:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

위의 코드에서 우리는 `/items/{item_id}` 경로에 대한 GET 요청을 처리하는 `read_item` 함수를 정의했습니다. {item_id}는 URL 경로의 동적 부분을 나타내며, 이를 통해 클라이언트로부터의 요청을 처리할 수 있습니다.

4. 요청 스키마(Request Schema)

FastAPI에서는 Pydantic을 사용하여 요청의 스키마를 정의할 수 있습니다. 스키마는 주어진 요청에서 어떤 데이터가 필요한지를 정의합니다. 이를 통해 데이터 유효성을 검증하고, 실수를 줄일 수 있습니다. 아래는 POST 요청에서 사용될 스키마를 정의하는 방법입니다:

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None

이 코드는 Item이라는 데이터 모델을 정의합니다. 클라이언트가 요청을 보낼 때, name, description, price, tax와 같은 필드를 포함시키도록 요구합니다. 가격은 반드시 있어야 하며, 그 외의 필드는 선택 사항입니다.

5. 요청에서 스키마 활용하기

정의한 스키마를 FastAPI의 엔드포인트에서 사용할 수 있습니다. 아래의 예에서는 POST 요청을 통해 Item 객체를 받아들이는 엔드포인트를 정의합니다:

@app.post("/items/")
async def create_item(item: Item):
    return {"item_name": item.name, "item_price": item.price}

위의 코드는 클라이언트가 `/items/` 경로에 POST 요청을 보내면, Item 스키마에 맞는 데이터를 요구합니다. 데이터가 스키마를 만족하지 않으면 FastAPI는 자동으로 422 Unprocessable Entity 오류를 반환하게 됩니다.

6. 요청의 정의 및 유효성 검증

FastAPI와 Pydantic은 요청 데이터의 유효성을 자동으로 검증합니다. 예를 들어, 클라이언트가 잘못된 타입의 데이터를 보내면 FastAPI는 요청을 처리하기 전 에러 메시지를 반환합니다. 아래는 그 예입니다:

@app.post("/items/")
async def create_item(item: Item):
    return {"item_name": item.name, "item_price": item.price}

# 잘못된 요청 예시
# POST /items/ 
# { "name": "item_name", "price": "not_a_number" }

위와 같은 잘못된 요청이 들어오면 FastAPI는 잘못된 값을 검출하고, 다음과 같은 에러 메시지를 반환합니다:

{
  "detail": [
    {
      "loc": ["body", "price"],
      "msg": "value is not a valid float",
      "type": "value_error.float"
    }
  ]
}

7. 쿼리 파라미터와 경로 파라미터

FastAPI에서는 경로 파라미터와 쿼리 파라미터를 쉽게 사용할 수 있습니다. 경로 파라미터는 URL 경로의 일부분으로, 특정 리소스를 식별하는 데 사용됩니다. 한편, 쿼리 파라미터는 URL의 ? 뒤에 추가되며 필터링, 정렬 등의 기능을 수행할 수 있습니다.

예를 들어, 아래와 같이 쿼리 파라미터를 사용할 수 있습니다:

@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

이 엔드포인트를 호출하면 ?skip=5&limit=10과 같은 형식으로 요청할 수 있으며, 이에 따라 서버는 skip 및 limit 값을 사용하여 처리합니다.

8. 쿼리 파라미터와 요청 스키마 혼합하기

FastAPI에서는 요청 스키마와 쿼리 파라미터를 혼합하여 사용할 수 있습니다. 아래는 쿼리 파라미터와 스키마를 동시에 사용하는 예입니다:

@app.get("/items/")
async def read_item(item: Item, skip: int = 0):
    return {
        "item_name": item.name,
        "item_price": item.price,
        "skip": skip
    }

이 경우 클라이언트는 Body에 Item 객체를 포함시키고, 쿼리 파라미터로 skip 값을 추가하여 요청할 수 있습니다.

9. 요청의 문서화

FastAPI는 모든 엔드포인트 및 요청 스키마를 기반으로 자동으로 문서를 생성합니다. 이는 Swagger UI를 통해 확인할 수 있으며, 다음 경로로 접근할 수 있습니다:

위의 링크를 통해 API에 대한 자세한 정보를 확인할 수 있으며, 사용자 친화적인 인터페이스에서 각 엔드포인트를 테스트할 수도 있습니다.

10. 결론

FastAPI를 이용한 서버 개발 과정에서 요청의 정의와 스키마의 중요성을 확인하였습니다. 요청 스키마를 활용함으로써, 클라이언트와의 데이터 통신에서 발생할 수 있는 오류를 줄이고, 서버의 신뢰성을 높일 수 있습니다. 또한, 자동으로 생성되는 문서화 기능은 개발 효율성을 극대화해 줍니다.

FastAPI는 현대 웹 어플리케이션에 적합한 선택지입니다. 비동기 프로그래밍과 높은 성능을 더해, 여러분의 프로젝트에서 활용해보세요. 앞으로 더 많은 기능들과 예제를 다룰 예정이니, 계속 지켜봐 주세요!