프로젝트 구조를 만들어 보겠습니다.
셋업
$ mkdir demo_fastapi_tdd_docker && cd demo_fastapi_tdd_docker
$ mkdir project
$ mkdir app
$ python -m venv venv; source venv/bin/activate; python -m pip install --upgrade pip;
(venv)$ pip install fastapi
(venv)$ pip install uvicorn
main.py, _init_.py 파일을 app 디렉토리에 만들어 줍니다. main 모듈에서 FastAPI의 인스턴스를 생성하고 동기식으로
테스트용 엔드포인트 API를 만들어 보겠습니다.
# project/app/main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/ping")
def pong():
return {"ping": "pong!"}
라우트 설정과 서버 가동을 위한 설정은 되었습니다.
그리고 requirements.txt 파일을 만들어 주겠습니다.
$ pip freeze > requirements.txt
❯ tree -L 3 -I "venv"
.
├── app
│ ├── __init__.py
│ └── main.py
└── requirements.txt
2 directories, 3 files
서버 기동을 하겠습니다. project/app 디렉토리에서 아래 명령어를 실행합니다.
❯ uvicorn app.main:app --reload
INFO: Will watch for changes in these directories: ['/Users/main/Project/testdriven/demo_fastapi_tdd_docker/project']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [7860] using StatReload
INFO: Started server process [7900]
INFO: Waiting for application startup.
INFO: Application startup complete.
FastAPI 애플리케이션을 찾을 수 있는 위치를 Uvicorn에 알려줍니다.
브라우저에서 http://localhost:8000/ping으로 이동합니다.
{"ping": "pong!"

Auto-reload
코드 변경 시, 수동으로 서버를 끄고 다시 켜지 않도록 할 수 있습니다.
(env)$ uvicorn app.main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [84187]
INFO: Started server process [84189]
INFO: Waiting for application startup.
INFO: Application startup complete.
설정
app 패키지에 config.py 모듈을 생성합니다. 각 환경별(개발, 테스트, 운영)로 변수의 값 정의를 다르게 하겠습니다.
# project/app/config.py
import logging
from pydantic_settings import BaseSettings
log = logging.getLogger("uvicorn")
class Settings(BaseSettings):
environment: str = "dev"
testing: bool = bool(0)
def get_settings() -> BaseSettings:
log.info("Loading config settings from the environment...")
return Settings()
pydantic-settings를 설치 합니다.
(env)$ pip install pydantic-settings==2.1.0
main.py 를 업데이트 합니다.
# project/app/main.py
from fastapi import FastAPI, Depends
from app.config import get_settings, Settings
app = FastAPI()
@app.get("/ping")
def pong(settings: Settings = Depends(get_settings)):
return {
"ping": "pong!",
"environment": settings.environment,
"testing": settings.testing
}
이후 다시 ping 엔드포인트로 접근하면 아래와 같은 결과가 나타납니다.
{
"ping": "pong!",
"environment": "dev",
"testing": false
}
이번에는 결과가 다르게 나타나도록 하겠습니다.
리눅스의 export 명령어로 환경변수의 키와 값을 설정합니다.
(env)$ export ENVIRONMENT=prod
(env)$ export TESTING=1
다시 서버를 기동하고 해당 엔드포인트로 접근합니다.
{
"ping": "pong!",
"environment": "prod",
"testing": true
}
해당 엔드 포인트로 접근을 여러 차례 시도하면 로그가 여러 차례 출력 되는 걸 볼 수 있습니다.
Loading config settings from the environment...
get_settings 가 여러 차례 보입니다. 해당 모듈을 여러차례 호출한다는 의미입니다. lru_cache를 사용하면 딱! 한번만 호출 할 수 있습니다.
# project/app/config.py
import logging
from functools import lru_cache
from pydantic_settings import BaseSettings
log = logging.getLogger("uvicorn")
class Settings(BaseSettings):
environment: str = "dev"
testing: bool = 0
@lru_cache()
def get_settings() -> BaseSettings:
log.info("Loading config settings from the environment...")
return Settings()
auto-reload 이후 브러우저 새로고침을 여러 차례 해봅니다. 그럼 아래 로그가 딱! 한번 출력 되는 것을 알 수 있습니다.
Loading config settings from the environment...
Async Handlers
동기 핸들러를 비동기로 전환해보겠습니다.
쓰레드를 사용하거나 Celery 또는 RabbitMQ의 task queue를 사용하는 번거로움 없이도 FastAPI에서는 이를 가능하게 만들 수 있습니다. async 키워드를 추가하면 Blocking I/O 없이 핸들러를 처리 할 수 있습니다.
@app.get("/ping")
async def pong(settings: Settings = Depends(get_settings)):
return {
"ping": "pong!",
"environment": settings.environment,
"testing": settings.testing
}
'프로그래밍 언어 > 파이썬' 카테고리의 다른 글
FastAPI를 이용한 TDD 개발 - 4 (0) | 2024.03.14 |
---|---|
FastAPI를 이용한 TDD 개발 - 3 (0) | 2024.03.12 |
FastAPI를 이용한 TDD 개발 - 2 (0) | 2024.03.11 |
FastAPI를 이용한 TDD 개발 (0) | 2024.03.10 |
re 모듈의 sub 메소드 사용법 (0) | 2021.11.28 |