Python 초보자를 위한 FastAPI(2) 1
파이썬의 기본 타이핑
프론트엔드 개발에 Typescript를 사용하기 시작한 이후로 다시는 돌아가지 않겠다고 스스로에게 약속했다... 물론 여전히 일할 때 사용하던 Javascript가 코드베이스에 남아 있긴 하다.
Python은 Javascript와 마찬가지로 동적으로 타입이 지정되는 언어이다. 공식 Python 문서에 따르면 Typing 시스템은 Python 3.5에 도입되었다. 따라서 Python은 Typescript처럼 정적으로 타입이 지정된 언어처럼 '느껴진다'.
지금까지는 일부 내장 타입만 사용해봤는데, 이미 매우 포괄적으로 느껴진다.
a: int = 1
b: float = 1.0
c: bool = True
d: str = "string"
e: bytes = b"data"
다음은 몇 가지 기본 타입으로, 저자는 float
타입과 bytes
타입이 꽤 괜찮다는 것을 알았다. Typescript에는 number
로만 모든 숫자를 표현할 수 있기 때문에 정수든 부동 소수점 숫자인지 상관하지 않는다. bytes
타입은 데이터 전송에 좋은 추가 기능인 것 같다.
a: list[int] = [1]
b: set[int] = {1, 2}
c: dict[str, int] = {"count": 1}
d: tuple[str, int, float] = ("string", 1, 1.0)
e: tuple[int, ...] = (1, 2, 3, 4) # tuple with variable size
내장 데이터 구조에 적절하게 입력하는 방법도 매우 간단해 보인다.
a: int | str = 1
b: int | str = "string"
c: int | None = None # a union type with None makes it optional
그리고 union
또는 optional
타입은 |
를 사용하여 수행할 수 있다.
위의 기본 타입을 사용하여 첫 번째 API 경로 작업을 시작할 수 있다(hello world는 실제로 중요하지 않다).
Pydantic을 사용한 데이터 검증
앞에서 도커 이미지에 몇 가지 종속성을 설치하였다.
fastapi==0.103.1
pydantic==2.3.0
uvicorn==0.23.2
우리는 이 모든 여정이 FastAPI에 관한 것임을 알고 있다. 그리고 서버를 실행하기 위해 uvicorn
명령을 사용했다. 아직 알려지지 않은 것이 하나 있는데, 바로 Pydantic이다.
Pydantic은 Python용 유효성 검사 라이브러리이다. 저자는 꽤 많은 Javascript/Typescript 유효성 검사 라이브러리를 사용해 왔다. Pydantic은 정말 훌륭하다고 말하지 않을 수 없다. 정말 잘 작동한다. 끝내준다.
데이터의 유효성을 검사하려면 먼저 사용자의 입력을 받는 새로운 엔드포인트가 필요하다. 이 시점에서 작은 음악 플레이어 어플리케이션을 만들 수 있을지도 모른다고 생각하고 있다. 그러려면 새로운 트랙 클래스가 필요하다...
from pydantic import BaseModel
class Track(BaseModel):
title: str
artist: str
album: str | None = None
year: int
label: str | None = None
먼저 Pydantic에서 BaseModel
을 import하고, BaseModel
이 상속하는 Track 클래스를 생성한다. 이 단계에서는 너무 복잡하게 만들지 않으려고 한다. 그래서 현재 Track
에는 몇 가지 간단한 속성만 있다. 보시다시피 album
과 label
속성은 str 또는 None일 수 있으므로 선택 사항으로 만들었다. 그리고 a = None
을 추가하면 기본값이 None
으로 설정된다.
이제 /tracks
엔드포인트를 만들 차례이다.
@app.post("/tracks")
async def create_track(track: Track):
return track
이 /tracks
엔드포인트는 트랙 클래스의 타입인 사용자의 요청 본문 track
을 받는다. 그리고 실제로는 아무것도 하지 않고 사용자의 입력을 응답으로 반환한다.
이제 브라우저에서 http://localhost/docs
로 이동하면 다음과 같은 내용을 볼 수 있다.
schema
섹션에서 track
아코디언을 토글하여 열면...
title
, artist
및 year
는 필수로 표시되어 있는 것을 볼 수 있다. album
과 label
은 우리가 원하는 대로 optional
이다.
이제 녹색 게시물 track
아코디언을 토글하여 열고 "Try it out" 버튼을 누른다. 유효한 예제 요청 본문 스니펫이 있는 것을 볼 수 있다. 직접 실행하면 응답에 의미 없는 예제 요청 본문이 모두 포함된 200
OK가 반환된다.
artist
를 제거하고 title
을 true
로 변경한 다음 "Execute"을 누르면, 이제 다음과 같은 응답이 포함된 422
처리할 수 없는 엔티티가 생긴다.
{
"detail": [
{
"type": "string_type",
"loc": [
"body",
"title"
],
"msg": "Input should be a valid string",
"input": true,
"url": "https://errors.pydantic.dev/2.3/v/string_type"
},
{
"type": "missing",
"loc": [
"body",
"artist"
],
"msg": "Field required",
"input": {
"title": true,
"album": "string",
"year": 0,
"label": "string"
},
"url": "https://errors.pydantic.dev/2.3/v/missing"
}
]
}
프론트엔드 친화적인 오류 메시지는 아닐 수도 있습니다. 하지만 모든 오류(loc
속성 내부)와 오류의 원인(type
과 msg
로 파악할 수 있음)을 지적한다.
현재로서는 오류의 형식은 크게 신경 쓰지 않는다. 따라서 이대로 유지한다.
이제 새 트랙을 생성할 방법이 생겼는데 여기서 멈출 이유가 있을까? 우리가 만든 모든 트랙을 검색하는 get
함수의 간단한 프로토타입을 만들어 보자.
tracks: list[Track] = []
@app.get("/tracks")
async def get_tracks():
return tracks
먼저 타입이 list[Track]
인 트랙 변수를 생성한다. 그리고 /track
경로에서 tracks
리스트만 반환하는 간단한 get
연산을 수행한다.
이것은 잘 작동하지만 사용자 입력을 tracks
에 저장해야 한다.
@app.post("/tracks")
async def create_track(track: Track):
tracks.append(track)
return track
반환문 앞에 tracks.append(track)
를 추가한다. append
메서드는 Javascript의 Array.push()
와 동일하다. 이제 새 트랙을 생성하고 tracks
변수에 데이터를 임시 저장할 수 있다.
이 팡트에서는 여기까지이다. 다음에는 OpenAPI 사양에 대해 좀 더 자세히 알아보도록 하겠다...
모든 소스 코드는 github에서 확인할 수 있다.
1: 이 페이지는 FastAPI by A Python Beginner (2)을 편역한 것임.