FastAPI(Asyncpg + Docker)에서 “invalid literal for int() with base 10: 'None'” 오류 수정

발행: (2026년 3월 18일 PM 12:59 GMT+9)
4 분 소요
원문: Dev.to

Source: Dev.to

번역할 텍스트가 제공되지 않았습니다. 번역이 필요한 전체 내용을 알려주시면 한국어로 번역해 드리겠습니다.

문제 개요

Docker 환경에서 Aiven PostgreSQL을 사용해 FastAPI 애플리케이션을 배포할 때 다음과 같은 트레이스백이 발생할 수 있습니다:

ValueError: invalid literal for int() with base 10: 'None'

SQLAlchemy의 URL 파서는 문자열 -None을 데이터베이스 URL의 포트 부분으로 해석합니다. 이 URL은 다음과 같이 표시됩니다:

postgresql+asyncpg://user:pass@host:None/dbname

원인

SQLAlchemy의 make_url() 함수는 포트 구성 요소를 정수로 변환하려고 시도합니다. URL을 만들 때 사용된 변수가 None으로 평가되면, f‑string 보간이 이를 문자열 "None"으로 바꾸어 오류가 발생합니다.

전형적인 코드 패턴:

import os

DB_USER = os.getenv("DB_USER")
DB_PASSWORD = os.getenv("DB_PASSWORD")
DB_HOST = os.getenv("DB_HOST")
DB_PORT = os.getenv("DB_PORT")
DB_NAME = os.getenv("DB_NAME")

ASYNC_DB_URL = f"postgresql+asyncpg://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
engine = create_async_engine(ASYNC_DB_URL)

DB_PORT(또는 다른 구성 요소)가 컨테이너 환경에 없으면 None이 되어 :None이 포함된 잘못된 URL이 생성됩니다.

전체 DATABASE_URL과 구성 요소 기반 구성을 혼합하는 대체 로직도 이 문제를 일으킬 수 있습니다:

ASYNC_DB_URL = os.getenv("DATABASE_URL") or f"postgresql+asyncpg://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

DATABASE_URL이 없을 경우, 구성된 URL에 None 포트가 그대로 포함됩니다.

Solution

Use a Single DATABASE_URL

Prefer a single environment variable that contains the complete connection string. Validate its presence early and avoid building the URL from separate components.

import os
from sqlalchemy.ext.asyncio import create_async_engine

DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
    raise ValueError("DATABASE_URL environment variable is not set")

engine = create_async_engine(DATABASE_URL)

Adjust the Scheme for asyncpg

Aiven (and many cloud providers) supply URLs with the postgres:// scheme. For SQLAlchemy async support, replace it with postgresql+asyncpg://.

raw_url = os.getenv("DATABASE_URL")
if raw_url and raw_url.startswith("postgres://"):
    raw_url = raw_url.replace("postgres://", "postgresql+asyncpg://", 1)

engine = create_async_engine(raw_url)

Full Example (database.py)

import os
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base

# Retrieve and validate the full URL
DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
    raise ValueError("DATABASE_URL environment variable is required")

# Ensure the asyncpg driver scheme
if DATABASE_URL.startswith("postgres://"):
    DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql+asyncpg://", 1)

# Create the async engine
engine = create_async_engine(DATABASE_URL, echo=True)

# Session factory
AsyncSessionLocal = sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

# Base class for models
Base = declarative_base()

Docker 구성

DATABASE_URL.env 파일을 통해 또는 docker‑compose.yml에 직접 지정하여 컨테이너에 전달합니다.

services:
  app:
    build: .
    env_file:
      - .env

.env 예시:

DATABASE_URL=postgresql+asyncpg://avnadmin:password@host:port/defaultdb?sslmode=require

모범 사례

  • 단일 진실 소스: 전체 연결 문자열을 하나의 변수에 저장합니다.
  • 조기 검증: 필수 변수가 누락된 경우 명확한 오류를 발생시킵니다.
  • 방어적 프로그래밍: 조각난 구성 요소로 URL을 구성하는 것을 피합니다.
  • 일관된 스키마: SQLAlchemy async를 사용할 때 postgresql+asyncpg://를 사용합니다.
  • 최소 환경 변수: 환경 간 값 불일치 또는 누락 위험을 줄입니다.

구성을 단일 검증된 DATABASE_URL로 통합하면 None 포트 오류를 제거하고 Docker, Kubernetes 또는 기타 플랫폼 전반에 걸친 배포를 간소화할 수 있습니다.

0 조회
Back to Blog

관련 글

더 보기 »

배열에서 최대 및 최소 요소 찾기

배열에서 내장 min 또는 max 함수를 사용하지 않고 최소와 최대 요소를 찾는 방법은, min 과 max 을 배열의 첫 번째 요소로 초기화하는 것입니다.