728x90

FastAPI 개발자 tiangolo는 FastAPI를 어떻게 사용할까 궁금해서 찾아본 프로젝트

https://github.com/tiangolo/full-stack-fastapi-postgresql

이번 게시글에서는 설정파일을 어떻게 구성하고 구조화하였는지 분석하려고 함

 

환경변수는 pydantic_settings를 기반으로 구성되어있으므로 pydantic_settings를 모른다면 pydantic_settings를 이용한 환경설정을 먼저 보고 오는 것을 권장함

소스 개요

config.py 파일 경로는 다음과 같음

```

app

└──  core # 데이터베이스 마이그레이션을 관리하는 Alembic 설정 파일 및 스크립트가 저장된 디렉토리

         └── config.py # 앱 전역 설정파일

```

import secrets
from typing import Any

from pydantic import (
    AnyHttpUrl,
    HttpUrl,
    PostgresDsn,
    ValidationInfo,
    field_validator,
)
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    API_V1_STR: str = "/api/v1"
    SECRET_KEY: str = secrets.token_urlsafe(32)
    # 60 minutes * 24 hours * 8 days = 8 days
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
    SERVER_HOST: AnyHttpUrl
    # BACKEND_CORS_ORIGINS is a JSON-formatted list of origins
    # e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \
    # "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]'
    BACKEND_CORS_ORIGINS: list[AnyHttpUrl] | str = []

    @field_validator("BACKEND_CORS_ORIGINS", mode="before")
    @classmethod
    def assemble_cors_origins(cls, v: str | list[str]) -> list[str] | str:
        if isinstance(v, str) and not v.startswith("["):
            return [i.strip() for i in v.split(",")]
        elif isinstance(v, list | str):
            return v
        raise ValueError(v)

    PROJECT_NAME: str
    SENTRY_DSN: HttpUrl | None = None

    @field_validator("SENTRY_DSN", mode="before")
    @classmethod
    def sentry_dsn_can_be_blank(cls, v: str) -> str | None:
        if not v:
            return None
        return v

    POSTGRES_SERVER: str
    POSTGRES_USER: str
    POSTGRES_PASSWORD: str
    POSTGRES_DB: str
    SQLALCHEMY_DATABASE_URI: PostgresDsn | None = None

    @field_validator("SQLALCHEMY_DATABASE_URI", mode="before")
    def assemble_db_connection(cls, v: str | None, info: ValidationInfo) -> Any:
        if isinstance(v, str):
            return v
        return PostgresDsn.build(
            scheme="postgresql+psycopg",
            username=info.data.get("POSTGRES_USER"),
            password=info.data.get("POSTGRES_PASSWORD"),
            host=info.data.get("POSTGRES_SERVER"),
            path=f"{info.data.get('POSTGRES_DB') or ''}",
        )

    SMTP_TLS: bool = True
    SMTP_PORT: int | None = None
    SMTP_HOST: str | None = None
    SMTP_USER: str | None = None
    SMTP_PASSWORD: str | None = None
    # TODO: update type to EmailStr when sqlmodel supports it
    EMAILS_FROM_EMAIL: str | None = None
    EMAILS_FROM_NAME: str | None = None

    @field_validator("EMAILS_FROM_NAME")
    def get_project_name(cls, v: str | None, info: ValidationInfo) -> str:
        if not v:
            return info.data["PROJECT_NAME"]
        return v

    EMAIL_RESET_TOKEN_EXPIRE_HOURS: int = 48
    EMAIL_TEMPLATES_DIR: str = "/app/app/email-templates/build"
    EMAILS_ENABLED: bool = False

    @field_validator("EMAILS_ENABLED", mode="before")
    def get_emails_enabled(cls, v: bool, info: ValidationInfo) -> bool:
        return bool(
            info.data.get("SMTP_HOST")
            and info.data.get("SMTP_PORT")
            and info.data.get("EMAILS_FROM_EMAIL")
        )

    # TODO: update type to EmailStr when sqlmodel supports it
    EMAIL_TEST_USER: str = "test@example.com"
    # TODO: update type to EmailStr when sqlmodel supports it
    FIRST_SUPERUSER: str
    FIRST_SUPERUSER_PASSWORD: str
    USERS_OPEN_REGISTRATION: bool = False
    model_config = SettingsConfigDict(case_sensitive=True)


settings = Settings()

주요 설정

  • 일반 설정
    • API_V1_STR: API 버전을 지정하는 문자열
    • SECRET_KEY: 인증 토큰 및 세션 등을 위한 시크릿 키
    • ACCESS_TOKEN_EXPIRE_MINUTES: 액세스 토큰 만료 시간(분 단위)
    • PROJECT_NAME: 프로젝트의 이름
    • EMAIL_RESET_TOKEN_EXPIRE_HOURS: 이메일 리셋 토큰의 만료 시간(시간 단위)
    • EMAIL_TEMPLATES_DIR: 이메일 템플릿 디렉토리 경로
    • EMAILS_ENABLED: 이메일 전송 기능 활성화 여부
    • EMAIL_TEST_USER: 이메일 테스트 사용자
    • FIRST_SUPERUSER: 최초 슈퍼 사용자 이름
    • FIRST_SUPERUSER_PASSWORD: 최초 슈퍼 사용자 비밀번호
    • USERS_OPEN_REGISTRATION: 사용자의 개방된 가입 여부
  • CORS 관련 설정
    • BACKEND_CORS_ORIGINS: 허용된 CORS 원본 목록.
  • 데이터베이스 관련 설정
    • POSTGRES_SERVER: PostgreSQL 서버 호스트
    • POSTGRES_USER: PostgreSQL 사용자 이름
    • POSTGRES_PASSWORD: PostgreSQL 비밀번호
    • POSTGRES_DB: PostgreSQL 데이터베이스 이름
    • SQLALCHEMY_DATABASE_URI: SQLAlchemy를 위한 PostgreSQL 데이터베이스 URI
  • 이메일 관련 설정
    • SMTP_TLS: SMTP TLS 활성화 여부
    • SMTP_PORT: SMTP 포트
    • SMTP_HOST: SMTP 호스트
    • SMTP_USER: SMTP 사용자
    • SMTP_PASSWORD: SMTP 비밀번호
    • EMAILS_FROM_EMAIL: 이메일 발신자 이메일 주소
    • EMAILS_FROM_NAME: 이메일 발신자 이름
  • 서버 관련 설정
    • SERVER_HOST: 서버 호스트 URL
  • 로그 및 모니터링 관련 설정
    • SENTRY_DSN: Sentry 프로젝트 DSN

소스 분석

Python에서 환경 설정을 다루는 데 사용되는 Pydantic 라이브러리를 사용하여 구조화함

Pydantic 및 pydantic_settings 라이브러리를 통해 설정 값을 관리하고 유효성을 검사하는 데 사용

Pydantic을 사용하면 설정을 타입 안정성이 있는 Python 객체로 정의할 수 있음

 

설정 파일에 있는 일부 필드들은 다른 필드의 값에 의존하는데, 이를 위하여 @field_validator 데코레이터와 ValidationInfo 객체를 이용

위의 코드를 보면 EMAILS_FROM_NAME 필드가 PROJECT_NAME 필드의 값을 기본값으로 사용하도록 설정되어 있음

이를 위해 ValidationInfo 객체를 사용하여 필요한 정보를 얻고, 필드 값을 조작하여 반환하고 있음

 

타입 힌트(from typing import Any) 및 Pydantic의 타입들(AnyHttpUrl, PostgresDsn, ValidationInfo)을 사용하여 데이터의 형식을 지정하고 유효성을 검사하는 데 사용

참고

Fastapi 설정 docs : https://fastapi.tiangolo.com/ru/advanced/settings/

 

FastAPI 개발자가 직접 개발한 FastAPI backend 프로젝트 구조

FastAPI 개발자가 직접 개발한 FastAPI backend alembic

FastAPI 개발자가 직접 개발한 FastAPI backend DB 초기화 방법

FastAPI 개발자가 직접 개발한 FastAPI backend pyproject.toml

FastAPI 개발자가 직접 개발한 FastAPI backend 설정파일 config.py

FastAPI 개발자가 직접 개발한 FastAPI backend 데이터모델 models.py

 

728x90

+ Recent posts