FastAPI로 간단한 API 개발을 시도해 본다 - (2) 요청 처리하기

FastAPI를 이용한 간단한 API 개발 - HTTP Request 처리하기

이전 포스팅을 통해 FastAPI 기반의 API 개발 환경을 만들고, 간단한 Hello World 를 노출하는 GET API를 구현해 보았다.

이번에는 HTTP API라면 당연히 수행해야 할, 몇가지 형태의 HTTP Request 처리를 해보고자 한다.

Pydantic

FastAPI에서는 모델 클래스의 type safety 및 요청 주입을 위해 Pydantic 의존성을 사용한다.

어떻게 보면 TypeScript를 도입했던 JS 진영처럼 Python도 점점 타입 안전성에 대한 고려와 이를 위한 유틸리티들이 늘어간다고도 볼 수 있을 것 같다.

Pydantic이 제공하는 BaseModel 을 상속받아 Python 클래스를 작성하고, 이 클래스를 사용하면서 타입에 관한 IDE의 힌트를 얻을 수도 있고, 필드에 의도했던 데이터 타입이 들어갔는지도 검증할 수 있다. Java로 치면 POJO + JSR-303(380) 과 같은 느낌이다.

본격적인 개발 진행을 위해 아래 명령어를 수행해 Pydantic을 추가해 준다.

1
pip install pydantic

Endpoint 작성

hello world API 구현에서 봤던 것처럼 FastAPI도 애노테이션 기반 엔드포인트 작성을 지원한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from fastapi import FastAPI

app = FastAPI()

@app.get(path="/")
async def hello():
return "hello world"

@app.get(path="/hello/{name})
async def hello_with_name(name: str):
return "hello with name. your name is " + name

@app.get(path="/hello/query/)
async def hello_with_querystring(name: str):
return "hello with name. your name is " + name

이전에 구현해뒀던 hello world API에 더불어서, 경로 변수를 사용할 수 있는 API를 하나 추가하였다.

경로 변수의 사용은 Spring MVC가 지원하는 argument resolution 기반의 경로 변수 매핑과 아주 흡사하다. path 에 중괄호를 이용해 경로변수의 위치 및 이름을 선언한 후, 함수의 파라메터로 같은 이름을 갖는 변수를 받아오면 경로 변수로 넘어온 값이 매핑된다.

아래 hello_with_querystring 은 FastAPI가 쿼리스트링 기반의 요청을 처리하는 방법을 보여주는 것이다. 함수의 파라메터로 선언된 변수의 이름에 해당하는 쿼리 스트링 파라미터를 FastAPI가 자동으로 매핑해준다. 즉, 이 요청 처리 함수가 처리할 수 있는 HTTP GET 요청의 주소는 아래와 같은 모습을 띄게 된다.

{HOSTNAME}/hello/query?name=봄이네집

POST 요청 매핑 - Pydantic Model 작성

POST 요청 매핑을 위해 우선 아까 추가했던 종속성인 Pydantic 기반의 모델 클래스를 작성해 준다.

Kotlin의 data class 와 유사한 문법으로, 필드명과 타입을 작성해주면 빠르고 편하게 모델 클래스의 작성이 끝난다.

1
2
3
4
5
from pydantic.main import BaseModel

class HelloWorldRequest(BaseModel):
name: str
age: int

POST 요청 매핑 - 요청 처리 함수 작성

앞서서 작성한 모델 클래스를 기반으로, 요청 처리 함수를 추가한다.

그동안 했던 것처럼, 모델 클래스를 함수의 파라메터로 받아주면 요청을 처리할 준비가 끝난다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from fastapi import FastAPI
from pydantic.main import BaseModel

class HelloWorldRequest(BaseModel):
name: str
age: int

app = FastAPI()

@app.get(path="/")
async def hello():
return "hello world"

@app.get(path="/hello/{name})
async def hello_with_name(name: str):
return "hello with name. your name is " + name

@app.get(path="/hello/query/)
async def hello_with_querystring(name: str):
return "hello with name. your name is " + name

@app.post(path="/hello/post)
async def hello_post(request: HelloWorldRequest):
return "hello with post. your name: {}, your age: {}"
.format(request.name, request.age)

마무리

간단하게 FastAPI를 이용해 HTTP 요청을 처리하는 방법을 알아봤다.

이 단계만 되어도 FastAPI의 고유 기능을 이용해 HTTP Request를 처리할 모든 준비는 끝난 것 같다. 물론 전통적인 3-tier web application의 구조를 보면, 우리는 아직 DB 통신을 할 준비를 하지 못했고, 상용 서버에 애플리케이션을 노출할 구현도 하지 않았다.

그러나 request/response mapping이라는 웹 API Framework의 본질로 본다면 이후 단계는 개발자의 선택에 따라 얼마든지 다른 모듈을 채택할 수도 있고, 커스터마이즈하는 것도 가능하다.

예를 들어, 잠시 스포하자면 다음 포스팅에선 SQLAlchemy를 이용해 MySQL DBMS에 질의해 요청을 처리하는 과정을 살펴볼 것인데, FastAPI 공식 문서가 추천하는 바 대로 SQLAlchemy를 사용할 뿐이고 실제 개발 상에서는 여타 다른 Python 진영의 데이터베이스 프레임워크를 사용해도 아무 문제가 없다.

즉, 여기까지만 읽어도 숙달된 Python 개발자라면 이미 FastAPI를 이용해 마이크로서비스 하나 정도는 개발할 수 있는 준비가 되었다고 판단된다.

감사하게도 여기까지 제 포스팅을 흥미있게 읽어 주셨다면, FastAPI를 이용해 간단한 서비스 하나를 직접 구현해보는 것을 강력히 추천한다.