Lucian Log
Blog
Computer Science
Algorithm
DB
Network
OS
General
AI
Blockchain
Concurrency
ETC
Git
Infrastructure
AWS
Docker
Java-Ecosystem
JPA
Java
Spring
JavaScript-Ecosystem
JavaScript
Next.js
React
TypeScript
Python-Ecosystem
Django
FastAPI
Python
SQLAlchemy
Software Engineering
Architecture
Culture
Test
Home
Contact
Copyright © 2024 |
Yankos
Home
>
Python-Ecosystem
> FastAPI
Now Loading ...
FastAPI
FastAPI와 비동기
FastAPI와 비동기 FastAPI는 비동기 처리에 최적화 (비동기 코드를 사용한다면 성능 이점) 비동기 작업 CPU와 RAM 간의 작업이 1이라고 한다면, CPU에서 IO 작업은 1000, 10000이 걸린다고 생각할 수 있음 IO 작업시 쉬고 있는 CPU가 다른 작업을 할 수 있다면, 성능이 대폭 상승 비동기 처리를 사용하면 단일 스레드에서 여러 작업을 효율적으로 수행 가능 데이터베이스 쿼리나 HTTP 요청 같이 I/O bound 작업의 성능을 크게 향상시킴 이점을 볼 수 있는 상황 비동기 작업은 프로그램에서 지원해주어야 사용 가능 내부적 관점 프레임워크 내부적인 처리에서 IO 작업들을 비동기로 처리할 것이므로 성능 향상 IO 작업은 따로 없더라도 비동기 코드를 작성했다면 내부적으로도 성능 이점을 얻을 수 있음 프로그램적 관점 CPU 작업만하는 코드로 이루어진 프로그램은 성능상 크게 이점을 보기 어려움 작성한 코드가 IO 작업을 필요로 한다면 성능 향상 조건 코드를 비동기적으로 짜야 함(async, await) 사용하는 IO 라이브러리 자체가 비동기를 지원해야 함 프레임워크가 비동기를 지원해야 함 (FastAPI) 웹서비스 시나리오 쿼리가 3초 걸리는 API 경로로 10개 요청이 동시에 온다면, 동기적 처리 상황 시 첫 사람은 3초가 걸리지만, 10번째 사람은 30초 걸림 비동기적으로 처리할 시, 뒷사람들의 대기시간도 기존 최대 30초보다 훨씬 줄어들 것 async/await async def 비동기 함수를 정의 Future 객체 반환 await 비동기 함수 내에서 사용 특정 비동기 연산이 완료될 때까지 함수의 실행을 일시적으로 중단하고 해당 연산의 완료를 기다림 이벤트 루프 프로그램 진입점에서 실행 asyncio.run(main()) 같이 사용해 주어진 코루틴을 실행하고 완료될 때까지 이벤트 루프를 유지 어떤 이벤트를 등록해두고 특정 이벤트가 발생했는지 여부를 지속적으로 체크 (내부 반복문) 실행되던 비동기 함수가 종료되면서 이벤트를 발생시키고, 그 후 await 뒷 부분의 코드가 이어서 실행됨 asyncio.gather 여러 코루틴을 동시에 실행 모든 코루틴이 완료될 때까지 기다린 후, 코루틴 결과를 포함하는 리스트 반환
Python-Ecosystem
· 2024-05-28
Fast API tutorial - Validation
각각의 Parameters는 인자로 받을 데이터에 대해 여러가지 조건을 걸어 validations(유효성 검사)를 수행할 수 있습니다. 만일 incorrect한 데이터가 감지될 경우 validation에 의해 error가 응답됩니다. Parameter의 종류를 선언하는 함수 앞에서 살펴봤듯이 parameter는 path parameter, query parameter, request body parameter 등 여러가지 형태의 종류가 존재합니다. 이외에도 cookie parameter, header parameter등 더 다양한 형태가 존재하는데, 이러한 parameter를 조금 더 명시적으로 선언할 수 있게 도와주는 함수가 각각 존재합니다. from typing import Optional from fastapi import FastAPI, Path, Query app = FastAPI() @app.get("/items/{item_id}") async def read_items(item_id: Path(...), q: Query(None): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results 만일 parameter의 default 값으로 Path(...)를 설정해주면, 해당 parameter는 required한 path parameter가 됩니다. 혹은 Default 값으로 Query(None)를 사용한다면 해당 parameter는 not required한 query parameter가 됩니다. 이러한 함수들은 함수의 첫 번째 parameter로 default 값을 받습니다. Path([default 값]) Query([default 값]) etc… 이렇게 각각의 parameters는 자신의 이름을 딴 함수를 갖고 있습니다. fastapi에서 import해오는 Path, Query 등이 그 예입니다. 사실 각각의 함수들은 해당 이름의 클래스에서 인스턴스를 만들어 return하는 기능을 하므로, default parameter로 설정하는 것은 해당 이름의 객체가 됩니다. Parameter에 대한 validation은 이러한 함수들을 사용해 적용합니다. 이러한 클래스들이 비슷한 느낌을 띄는 이유가 있습니다. 해당 클래스들은 모두 Param 클래스의 subclass들입니다. 그래서 이들은 validation과 metadata의 추가를 모두 똑같은 방식으로 적용할 수 있습니다. Path(), Query(), Body() 함수로 required parameter 만들기 앞에서 Query 함수의 첫번째 parameter로 None을 사용해 optional parameter를 만들었는데, 만일 Query 함수를 사용해 required parameter를 만들고 싶다면 Query의 첫 번째 argument로 ... (Ellipsis)를 사용하면 됩니다. 이는 나중에 사용할 Path, Body 함수와 더불어 같은 맥락의 함수들에 똑같이 적용됩니다. Query(…) Path(…) Body(…) etc… String Validations Additional Information Fast API는 type hinting과 default parameter를 통해 이에 대한 추가 정보를 인식하고 활용합니다. from typing import Optional from fastapi import FastAPI app = FastAPI() @app.get("/items/") async def read_items(q: Optional[str] = None): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results 위 코드는 async def read_items(q: Optional[str] = None): 부분에 query parameter q에 대한 타입을 명시했습니다. q는 str타입이 단서가 되어 query parameter로 인식됩니다. 또한, = None을 통해 not required한 optional parameter로 인지됩니다. Additional validation Parameter에 인자로 받을 데이터에 대한 validation을 걸어줄 수 있습니다. 일례로, query parameter q에 대해 인자로 들어올 str 데이터의 최대 길이가 50이 넘지 않게끔 검사를 수행하는 validation을 만들겠습니다. from typing import Optional from fastapi import FastAPI, Query app = FastAPI() @app.get("/items/") async def read_items(q: Optional[str] = Query(None, max_length=50)): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results 우선 fastapi에서 Query 함수를 import합니다. from fastapi import FastAPI, Query 그리고 Query 함수를 다음과 같은 형태로 default parameter 자리에 사용합니다. Query([default 값], [조건식]) q: Optional[str] = Query(None, max_length=50)는 default 값으로 None을 유지한 상태에서 q의 최대 길이를 50으로 지정합니다. 그리고 실제로 전달된 데이터의 길이가 50을 넘어가면, error를 응답합니다. 또한, Query 함수는 다음과 같이 parameter를 더 추가해 여러 개의 validation을 지정할 수 있으며, 정규표현식을 validation으로 지정할 수도 있습니다. Query(None, min_length=3, max_length=50) Query(None, min_length=3, max_length=50, regex="^fixedquery$") 이러한 validation 정보들은 Interactive Documentation에도 업데이트됩니다. Parameter에 Multiple values 받기 from typing import List, Optional from fastapi import FastAPI, Query app = FastAPI() @app.get("/items/") async def read_items(q: Optional[List[str]] = Query(None)): query_items = {"q": q} return query_items Parameter를 특정 parameter를 만드는 함수를 사용해 선언한다면, multiple values를 받는 parameter로 만들 수 있습니다. 만일 query parameter를 Query 함수를 사용해 만든다면, multiple values를 받는 query parameter를 만드는 식입니다. 이 경우 query parameter는 반드시 Query 함수와 함께 정의되어야 하는데, 그렇지 않으면 Fast API가 해당 parameter를 request body로 간주할 수 있기 때문입니다. (Singular type이 아닌 type으로 parameter를 선언할 때 나타나는 현상입니다!) q: Optional[List[str]] = Query(None) 위와 같이 List 타입으로 q를 선언하면, http://localhost:8000/items/?q=foo&q=bar 요청과 같이 URL에 여러 개의 query 값이 전달되어도 리스트로 한 번에 받아 처리할 수 있습니다. Fast API는 자동으로 multiple query를 인식해 리스트에 담아줍니다. 위 URL 요청에 대한 response은 다음과 같습니다. { "q": [ "foo", "bar" ] } 만일 리스트로 받을 내부 요소들의 타입까지 체크하고 싶진 않다면, 다음과 같이 list로만 타입을 선언하면 됩니다. q: list = Query([]) Parameter에 metadata 넣기 Parameter의 종류를 선언하는 함수에 인자를 설정해주면, 함수를 적용한 parameter에 metadata를 추가할 수 있습니다. 예를 들어, Query 함수의 parameter를 사용하면 다음과 같이 query parameter에 또 다른 metadata들을 추가할 수 있습니다. async def read_items( q: Optional[str] = Query( None, title="Query string", description="Query string for the items to search in the database that have a good match", min_length=3, ) ): 여기선 title과 description parameter를 추가했는데, 이렇게 추가된 query parameter 정보들은 Interactive Document에도 반영됩니다. Parameter에 Alias 설정하기 REST하게 URL을 만들고 싶다면, _보다 -를 사용하는 것이 좋습니다. 언더스코어 _는 밑줄이 그어지면 가독성이 떨어지기 때문입니다. 그러나 parameter 이름을 item-query처럼 사용하는 것은 Python 문법에 어긋납니다. 따라서, 이러한 경우에는 parameter에 alias를 item-query로 설정해줍니다. 아래는 query parameter의 예입니다. q: Optional[str] = Query(None, alias="item-query") Parameter Deprecating하기 Deprecated는 특정 기능이 아직까지 사용되고는 있지만, 중요도가 떨어져 조만간 사라지게 될 상태를 말합니다. 만일 특정 parameter를 언젠가 제거할 계획이지만 이를 계속 사용하는 클라이언트 개발자들을 위해 한 동안 남겨두려는 상황이라면, 해당 parameter를 deprecating하여 Interactive API Documentation에 해당 parameter가 deprecated 상태임을 명확히 알려줄 수 있습니다. (Documentation은 클라이언트 개발자들과의 소통 창구 역할을 합니다!) 예를 들어, 다음과 같이 Query 함수의 parameter로 deprecated=True를 설정해줍니다. q: Optional[str] = Query(None, deprecated=True) Deprecated 상태에서는 parameter의 이용이 여전히 가능하지만, Interactive Documentation에는 해당 parameter의 deprecated 상태가 명확히 반영됩니다. Numeric Validations 앞에선 String과 관련된 validation을 많이 살펴봤지만, Numeric 형태의 데이터를 다룰 때도 물론 validation을 수행하거나 metadata를 추가해줄 수 있습니다. 이 경우는 Numeric value를 자주 사용하는 path parameter를 주로 사용해서 살펴보겠습니다. Path 함수는 다음과 같이 import해 사용합니다. from fastapi import Path Path Parameter에 metadata 넣기 from typing import Optional from fastapi import FastAPI, Path, Query app = FastAPI() @app.get("/items/{item_id}") async def read_items( item_id: int = Path(..., title="The ID of the item to get"), q: Optional[str] = Query(None, alias="item-query"), ): results = {"item_id": item_id} if q: results.update({"q": q}) return results Path 함수에 metadata를 넣을 때도, Query와 똑같은 방식으로 사용합니다. item_id path parameter에 title 정보를 넣고 싶다면 다음과 같이 Path 함수에 parameter로 삽입하여 적용합니다. item_id: int = Path(..., title="The ID of the item to get") 이 때, path parameter는 path의 일부분이기 때문에, 인자가 반드시 존재해야 하는 parameter입니다. 따라서, 첫 번째 파라미터로 ...을 사용해 Fast API에게 required parameter임을 알려줍니다. 사실 ...이외의 None이나 다른 default 값을 사용하더라도 문제 없이 실행되지만 큰 의미는 없으며, 해당 path parameter는 여전히 required parameter로 기능합니다. Number validations Parameter에 대하여 몇몇 숫자에 대한 validation을 추가할 수 있습니다. from fastapi import FastAPI, Path app = FastAPI() @app.get("/items/{item_id}") async def read_items( *, item_id: int = Path(..., title="The ID of the item to get", gt=0, le=1000), q: str, ): results = {"item_id": item_id} if q: results.update({"q": q}) return results 위 코드의 Path 함수에 들어간 gt, le 같은 validation parameter들의 의미는 다음과 같습니다. gt: greater than ge: greater than or equal lt: less than le: less than or equal Reference Fast API 공식 문서 튜토리얼
Python-Ecosystem
· 2021-06-06
Fast API tutorial - Params
Fast API 튜토리얼 - Parameters of Path, Query, Request body Path Parameters Path Parameters의 정의와 형태 Path parameter는 path 내에 들어있는 variable의 value를 전달받은 parameter를 말합니다. @app.get("/items/{item_id}") def read_item(item_id): return {"item_id": item_id} 위의 코드에서, item_id는 path parameter에 해당합니다. HTTP 요청이 들어오면 해당 URL에서 {item_id}에 해당하는 value를 획득하고, 이 value는 read_item함수의 item_id에 인자로 전달됩니다. 위의 코드를 main.py에 추가해 저장한 후, http://127.0.0.1:8000/items/foo에 들어가면 response로 {"item_id":"foo"}이 확인됩니다. Data conversion and validation @app.get("/items/{item_id}") def read_item(item_id: int): return {"item_id": item_id} 또한, path operation function에서 인자로 사용한 path parameter에 타입 힌트를 줄 수 있습니다. (다른 parameter도 마찬가지로 적용됩니다.) 그리고 이렇게 자료형을 annotate한 parameter는 들어온 인자 값을 annotated된 자료형대로 형 변환해서 parameter에 담습니다. 만일 http://127.0.0.1:8000/items/3으로 요청이 들어온 경우, 원래는 path parameter를 str 타입으로 받아 item_id 값이 ‘3’이 되지만 위 코드에서는 타입 힌트를 보고 int로 형 변환된 3이 담깁니다. 즉, Fast API는 타입 힌트를 통해 자동으로 parsing을 통한 data conversion을 제공합니다. 만일 path parameter에 annotated된 타입과 다른 타입의 값이 요청된다면, 해당 HTTP 요청은 에러를 일으킵니다. 이는 Fast API가 데이터 유효성 검사까지 수행함을 보여줍니다. 실제로 http://127.0.0.1:8000/items/foo에 들어가면 응답에 오류가 발생합니다. Annotated된 int 타입으로 형 변환이 이뤄질 수 없는 foo가 값으로 들어왔기 때문입니다. http://127.0.0.1:8000/items/4.2의 경우도 마찬가지입니다. 타입 힌트로 annotated된 변수는 Interactive API documentation에도 적용됩니다. http://127.0.0.1:8000/docs에 들어가면 path parameter item_id가 integer로 선언되어 있음을 확인할 수 있습니다. Fast API에서 이러한 data conversion 및 validation이 가능한 이유는 내부적으로 Pydantic 라이브러리의 도움 덕분입니다. Pydantic이란? 파이썬 타입 힌트를 사용해 데이터 유효성 검사를 해주는 라이브러리입니다. 만일 어노테이션된 타입과 다른 데이터를 만나면 에러를 띄웁니다. Fast API에서는 Pydantic을 활용하여 간편하게 데이터 유효성 검사를 수행합니다. Path Operation 정의 순서의 중요성 어떤 path operation들은 정의하는 순서에 따라 예상치 못한 처리를 일으킬 수 있습니다. 예를 들어, 고정된 path를 가진 path operation과 path parameter를 가진 path operation이 모두 정의된 경우를 살펴봅시다. from fastapi import FastAPI app = FastAPI() @app.get("/users/me") async def read_user_me(): return {"user_id": "the current user"} @app.get("/users/{user_id}") async def read_user(user_id: str): return {"user_id": user_id} /users/me 코드는 /users/{user_id}보다 앞에 쓰여져야 합니다. 만일 순서가 바뀌면, Fast API는 me를 user_id의 value로 오해하여 본래 의도와 다르게 read_user 함수를 호출할 것입니다. Path Parameter의 값으로 Path를 받는 경우 때로는 path parameter의 값으로 home/dogs/wealsh와 같은 path가 올 수 있습니다. 만일 path operation의 path가 기존처럼 /files/{file_path}이라면, file_path는 /files/home/dogs/wealsh 요청이 들어왔을 때 이를 온전히 인식하지 못하고 {"detail":"Not Found"}를 응답합니다. 하지만, Starlette에서 제공하는 Path convertor를 사용하면 path parameter의 인자가 path 형태로 들어와도 이를 온전히 인식하게 됩니다. Fast API는 Starlette을 기반으로 만들어졌기 때문에, 특별한 import 없이 다음과 같이 써주면 path convertor가 동작합니다. /files/{file_path:path} 이를 활용하면 다음과 같이 path operation에 http://127.0.0.1:8000/files/home/dogs/wealsh 형태로 요청을 보내도 온전히 동작합니다. from fastapi import FastAPI app = FastAPI() @app.get("/files/{file_path:path}") async def read_file(file_path: str): return {"file_path": file_path} 위의 요청의 경우 files/home/dogs/wealsh 값이 file_path에 담겨 응답됩니다. 만일 /files/home/dogs/wealsh 형태로 앞에 /를 추가하여 file_path에 담고 싶다면 http://127.0.0.1:8000/files//home/dogs/wealsh 형태로 요청을 보내면 됩니다. Query Parameters Query Parameters의 정의와 형태 Path operation function에 path parameter가 아닌 다른 parameter를 선언했다면, 해당 parameter들은 자동으로 query parameter로 인식됩니다. Query parameter는 request로 들어오는 query의 값이 담기는 parameter입니다. Query는 URL의 ?뒤에 오는 key-value pair를 의미하며 각각의 query는 &로 구분됩니다. 다음은 request에 담긴 query의 예시입니다. http://127.0.0.1:8000/items/?skip=0&limit=10 또한, 다음과 같은 path operation은 이러한 request에 대해 query parameter를 받습니다. from fastapi import FastAPI app = FastAPI() fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}] @app.get("/items/") async def read_item(skip: int = 0, limit: int = 10): return fake_items_db[skip : skip + limit] 이 경우, query parameter는 skip과 limit이고 각각 0과 10을 인자로 받습니다. 원래대로라면 URL로부터 들어온 str타입의 ‘0’과 ‘10’으로 값을 받았겠지만, skip과 limit의 타입을 int로 선언했기 때문에 형 변환하여 값을 받습니다. 즉, query parameter에도 path parameter에서 적용되던 다음과 같은 프로세스들이 그대로 적용됩니다. Editor Support (Auto completion, Error check, etc…) Data conversion Data validation Automatic Documentation Default value & Optional Parameters from fastapi import FastAPI app = FastAPI() fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}] @app.get("/items/") async def read_item(skip: int = 0, limit: int = 10): return fake_items_db[skip : skip + limit] Query parameter는 default parameter를 설정할 수 있습니다. 이 경우 skip과 limit의 default 값은 각각 0과 10입니다. from typing import Optional from fastapi import FastAPI app = FastAPI() @app.get("/items/{item_id}") async def read_item(item_id: str, q: Optional[str] = None): if q: return {"item_id": item_id, "q": q} return {"item_id": item_id} 또한, query parameter에는 typing 모듈을 활용해서 Optional 타입을 선언할 수 있습니다. q: Optional[str] = None은 query parameter q가 str 타입의 value를 인자로 받거나 혹은 인자가 없을 때는 None을 default value로 가진다는 의미입니다. 즉, Fast API는 q를 required하지 않은 parameter로 인식합니다. 이 때, Fast API는 = None부분을 인식해 query parameter q의 required 여부를 구분합니다. 또한, : Optional[str] 부분에서 Fast API는 str 부분만 인식해 data conversion 및 data validation에 사용합니다. 그리고 나머지 Optional 부분은 Fast API가 아닌 Editor의 Auto completion과 Error check를 support하기 위해 사용됩니다. Required parameter란? Parameter가 Required하다는 것은 특정 parameter가 필수적으로 인자를 받아야만 함을 말합니다. 보통 특정 parameter에 default값을 설정해두면 not required, default 값을 설정하지 않으면 required 상태로 인식됩니다. 만일 not required한 parameter를 굳이 특정 값이 있지 않아도 되는 Optional parameter로 만들고 싶다면, default 값으로 None을 설정하면 됩니다. Request Body Request Body의 정의와 형태 Request body는 클라이언트에서 API로 보내는 data를 의미합니다. 반면에, API가 클라이언트에게 보내는 data는 response body라고 합니다. Response body는 API가 항상 보내야 하는 반면, request body는 클라이언트가 필수적으로 보낼 필요는 없습니다. from typing import Optional from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None app = FastAPI() @app.post("/items/") async def create_item(item: Item): return item Request body는 Pydantic model을 통해 선언합니다. pydantic 라이브러리에서 BaseModel을 import하고, BaseModel을 상속하는 클래스를 생성해 Pydantic 모델을 만듭니다. Model의 attribute들은 query parameter와 같은 방식으로 required 여부를 정할 수 있습니다. 위 경우, name, price는 required한 attribute이고 description, tax는 not required하면서 optional한 attribute입니다. 따라서, 위 모델은 다음과 같은 JSON 객체(혹은 Python dict 객체)를 선언한 것과 같습니다. { "name": "Foo", "description": "An optional description", "price": 45.2, "tax": 3.5 } description과 tax는 optional하기 때문에 다음과 같은 JSON 객체도 request body로 유효하게 전달 받을 수 있습니다. { "name": "Foo", "description": "An optional description", "price": 45.2, "tax": 3.5 } 그리고 path operation fucntion의 parameter에 원하는 pydantic model을 타입 선언 해주면, 해당 파라미터는 request body를 전달받는 parameter로 인식됩니다. 위 코드에서는 async def create_item(item: Item):에서 Item pydantic model을 타입으로 선언해 item을 request body parameter로 만들었습니다. 이렇게 선언된 request body parameter는 다음과 같은 특징을 가집니다. Request body로 들어온 data를 JSON 형식으로 읽어들입니다. 필요할 경우 들어온 data를 선언된 타입에 일치하도록 data conversion합니다. 선언된 타입으로 Data validation을 수행합니다. (Incorrect data에는 error를 띄웁니다!) Editor support를 지원합니다. 해당 model에 대한 JSON schema를 생성해, Automatic Documentation에 적용합니다. Request Body로 전달받은 Model 사용법 from typing import Optional from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None app = FastAPI() @app.post("/items/") async def create_item(item: Item): item_dict = item.dict() if item.tax: price_with_tax = item.price + item.tax item_dict.update({"price_with_tax": price_with_tax}) return item_dict Request body를 전달받은 item은 클래스의 attribute를 사용하는 것과 똑같은 방식으로 자유롭게 사용할 수 있습니다. 예를 들어, item.tax처럼 tax 속성에 접근해 value를 사용할 수 있습니다. 또한, pydantic model의 .dict() 메서드를 사용해 item.dict()로 해당 model의 데이터를 python dict 형태로 사용할 수도 있습니다. 위 코드는 tax 속성에 인자가 들어왔다면, price_with_tax = item.price + item.tax로 새로운 value를 만들고 item에서 추출한 item_dict에 item_dict.update({"price_with_tax": price_with_tax})로 새로운 key-value를 추가하여 item_dict를 return합니다. Path + Query + Request Body Parameters Path, query, request body parameter는 모두 동시에 사용할 수 있습니다. Fast API는 각각의 parameters를 자동으로 구분해냅니다 from typing import Optional from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None app = FastAPI() @app.put("/items/{item_id}") async def create_item(item_id: int, item: Item, q: Optional[str] = None): result = {"item_id": item_id, **item.dict()} if q: result.update({"q": q}) return result 위 경우 item_id는 path parameter, item은 request body parameter, q는 query parameter로 자동 인식됩니다. 기본적으로 parameter 자동 인식은 다음과 같은 기준으로 진행됩니다. Path 안에도 선언되어 있는 parameter는 path parameter로 인식합니다. (혹은 Path(...)가 선언되어 있는 parameter) int, float, str, bool 등의 singular type으로 선언된 parameter는 query parameter로 인식합니다. (혹은 Query(...)가 선언되어 있는 parameter) Pydantic model로 type이 선언된 parameter는 request body parameter로 인식합니다. (혹은 Body(...)가 선언되어 있는 parameter) Path, Query, Request body Parameters의 순서 문제 Query parameter를 default 값이 없는 required parameter로 만들고, path parameter는 default 값으로 Path 인스턴스를 넣어 not required한 parameter로 만드는 다음과 같은 상황을 가정해보겠습니다. async def read_items( item_id: int = Path(..., title="The ID of the item to get"), q: str ): 이 때, Python 문법으로 인해 default 값이 있는 parameter는 default 값이 없는 parameter의 앞에 위치하지 못합니다. 따라서, 위 코드는 오류를 일으킵니다. 하지만, 다음과 같이 순서를 정리하면 오류를 피할 수 있습니다. async def read_items( q: str, item_id: int = Path(..., title="The ID of the item to get") ): Fast API는 parameter의 이름, 타입, default parameter 등의 단서를 통해 parameter의 종류를 인식하므로, 순서에 대한 문제는 Python 문법에서만 고려하면 됩니다. 만일 다음과 같은 약간의 트릭을 사용한다면, default 값 여부에 상관 없이 자유로운 parameter 배열이 가능합니다. async def read_items( *, item_id: int = Path(..., title="The ID of the item to get"), q: str ): *를 함수의 첫 번째 parameter로 사용하면 위와 같이 default 값이 없는 parameter가 뒷 순서로 와도 상관 없습니다. *는 Python 함수의 special parameter 중 하나로, * 뒤에 위치한 parameter들은 모두 키워드 인자만 받도록 강제합니다. Special parameter에 대해 더 자세히 알고 싶다면, Python 공식 튜토리얼 문서의 Special parameters 부분을 읽어 보시길 바랍니다. Reference Fast API 공식 문서 튜토리얼
Python-Ecosystem
· 2021-06-04
Fast API tutorial - Installation
Fast API 공식 문서의 튜토리얼을 살펴보고 정리합니다. 본 글은 윈도우 환경을 기준으로 작성되었습니다. Fast API 설치하기 앞에서는 간단히 Fast API와 uvicorn만 설치하여 진행했지만, 이번엔 튜토리얼을 편하게 진행하기 위해 Fast API와 이에 따른 의존 관계가 있는 모듈들을 한꺼번에 설치하겠습니다. 가상환경을 사용한다면 활성화시켜주시고, [all] 옵션을 사용해 Fast API와 관련 모듈들을 한번에 설치합니다. > pip install fastapi[all] 이 때 uvicorn 서버도 함께 설치되기 때문에, 따로 uvicorn을 설치할 필요없이 프로젝트 디렉토리에 main.py 파일만 만들고 바로 서버를 구동할 수 있습니다. 가장 simplest한 형태의 Fast API 코드를 main.py에 작성해 실행해봅시다. main.py 파일을 프로젝트 폴더에 생성하고 다음 코드를 입력해 저장합니다. # main.py from fastapi import FastAPI app = FastAPI() @app.get("/") def root(): return {"message": "Hello World"} 코드 분석 from fastapi import FastAPI: fastapi 모듈에서 파이썬 클래스 FastAPI를 import합니다. app = FastAPI(): app 변수에 FastAPI 인스턴스를 만들어 담습니다. 해당 변수에 이름에 따라 바로 이어 나올 uvicorn 명령어를 다르게 사용합니다. uvicorn main:[변수이름] --reload처럼 말이죠! @app.get("/"): path operation decorator를 만듭니다. path란 //를 제외하고 url에서 첫 번째로 만나는 /로부터 시작되는 url의 뒷 부분을 말하며, operation은 HTTP method를 말합니다. 이 코드의 경우, decorator가 장식하는 함수가 ‘GET operation을 사용해 / path로 가라는 요청’을 처리하는 역할을 한다고 FastAPI에게 알려줍니다. def root():: path operation function을 정의합니다. 이 코드의 경우, FastAPI는 GET operation으로 URL /에 대한 요청이 들어오면 이 함수를 호출합니다. return {"message": "Hello World"}: content를 리턴합니다. 리턴할 수 있는 객체는 dict, list, int, str, Pydantic model 등 다양합니다. 그리고 Uvicorn 서버를 구동합니다. > uvicorn main:app --reload uvicorn main:app --reload의 의미 uvicorn: uvicorn 서버를 실행합니다 main: main.py 파일(모듈)을 의미합니다. app: main.py 내에서 생성한 FastAPI 클래스의 객체를 의미합니다. --reload: 코드를 수정한 후 자동으로 서버를 재시작해주는 옵션입니다. 현재 개발 중일 때 사용합니다. 이제 브라우저로 로컬머신에서 작동 중인 앱을 확인해봅시다. http://127.0.0.1:8000 주소에 들어가면, JSON 형태의 응답으로 다음과 같이 새로이 마주하는 Fast API 세상과 인사를 나눌 수 있습니다! Uvicorn이란? uvloop와 httptools를 사용하는 초고속 ASGI(Asynchronous Server Gateway Interface) web server입니다. 최근까지 파이썬은 asyncio 프레임 워크를 위한 저수준 서버 / 애플리케이션 인터페이스가 없었는데, uvicorn의 등장으로 Fast API같은 프레임워크의 비동기 처리 성능이 크게 향상됐습니다. Starlette이란? Uvicorn 위에서 실행되는 비동기적으로 실행할 수 있는 web application server입니다. FastAPI는 Starlette 위에서 동작하고, Starlette 클래스를 상속받았기 때문에, Starlette의 모든 기능을 사용할 수 있습니다. Reference Fast API 공식 문서 튜토리얼 Uvicorn이란? 비동기 Micro API server로 좋은 FastAPI
Python-Ecosystem
· 2021-05-24
FastAPI - Simple Start
간단한 fast api 설치 및 앱 실행 원하는 가상 환경과 디렉토리에서 fast api 설치 pip install fastapi uvicorn 설치 pip install uvicorn[standard] main.py 파일 생성하기 아래의 코드를 임시로 사용해서 진행하겠습니다. from typing import Optional from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}") def read_item(item_id: int, q: Optional[str] = None): return {"item_id": item_id, "q": q} 서버 실행 uvicorn main:app --reload 서버를 실행하면 다음과 같은 api가 생성되어 사용할 수 있습니다. http://127.0.0.1:8000 에서 api 접근합니다. /items/{item_id} 로 접근 가능합니다. (GET method 적용) 쿼리를 날려 JSON 형식으로 응답받을 수 있습니다. 쿼리 요청: http://127.0.0.1:8000/items/5?q=somequery 응답: {"item_id": 5, "q": "somequery"} 다음 주소에서 Swagger UI에서 제공하는 Interactive API docs를 확인할 수 있습니다. http://127.0.0.1:8000/docs 혹은 다음 주소에서 ReDoc에서 제공하는 또 다른 Interactive API docs를 확인할 수 있습니다.
Python-Ecosystem
· 2021-05-17
Fast API - Intro
FastAPI 마이크로 프레임워크 마이크로 프레임워크: 필수 기능만 제공하는 경량화된 프레임워크 꼭 필요한 기능만 사용해 빠르고 가볍게 개발 가능 풀스택 프레임워크(장고)가 항공 모함이라고 한다면, 마이크로 프레임워크는 돛단배 추가적으로 필요한 기능은 라이브러리를 붙여 해결 빠른 속도 비동기 프로그래밍으로 I/O 작업(DB 쿼리, 네트워크 통신)이 많은 애플리케이션에 높은 성능 즉, 한 요청이 끝나야 다음 요청을 처리(동기 처리)하는 Django, Flask와 달리, 한 요청이 끝나기 전에 다른 요청을 함께 처리 가능 (비동기 처리) Node.js, Go와 대등할 정도의 높은 성능 (Starlette을 상속 받아 강화시킴) 생산성 간결한 코드로 쉽게 API 개발 빠른 러닝 커브 자동 문서화 Swagger UI, ReDoc 유효성 검사 Pydantic과의 integration으로 강력한 데이터 유효성 검사 (type hint로 지원) 강력한 IDE Auto Completion FastAPI with Open API Specification Open API Specification API schema를 구상할 때 따르길 권장하는 표준 Linux 재단에서 운영하는 공동 프로젝트 모든 HTTP API 스타일을 담진 않지만, 표준을 지켰을 때 RESTful API를 지향할 수 있도록 도움 송수신하는 데이터 타입: JSON schema FastAPI & Open API Specification FastAPI는 Open API Specification을 따름 Open API schema를 통해 Swagger UI와 ReDoc 문서 자동 생성 Starlette FastAPI가 상속하고 있는 경량화된 ASGI 프레임워크 파이썬 프레임워크 중 가장 빠르며 고성능 async 서비스를 만들기 적합 FastAPI는 속도가 빠른 Starlette에 몇가지 기능을 추가한 것 타입 힌트를 통한 데이터 유효성 검사 직렬화 문서 자동화 의존성 주입 및 보안 유틸리티 등 Uvicorn uvloop과 httptools를 사용하는 초고속 ASGI 서버 FastAPI와 Starlette은 Uvicorn 서버 위에서 동작 FastAPI와 Starlette의 속도가 빠른 이유 Pydantic 파이썬 타입 힌트를 사용하여 데이터 유효성 검사를 수행하는 라이브러리 핵심 validation logic이 Rust로 작성되어서 매우 빠름 확장성이 높고 IDE 지원이 잘됨 FastAPI에서는 데이터 유효성 검사와 더불어 직렬화 및 API 문서 정의에 사용
Python-Ecosystem
· 2021-05-16
<
>
Touch background to close