[둥지] FastAPI 프로젝트 초기 설정: Config와 Main 구성하기
FastAPI 애플리케이션의 뼈대인 main.py(CORS, Health Check) 초기화 과정과 Pydantic Settings를 활용한 타입 안전한 환경 변수(.env) 관리 및 분리 전략을 공유합니다.
이번 포스트에서는 FastAPI 프로젝트의 초기 설정과 환경 변수 관리 전략을 공유합니다. 특히 Pydantic Settings를 활용한 타입 안전한 설정 관리와 환경별 설정 파일 분리 방법을 다룹니다.
1. FastAPI 앱 초기화: main.py
FastAPI 애플리케이션의 시작점인 app/main.py는 프로젝트의 뼈대를 구성합니다.
① FastAPI 인스턴스 생성
FastAPI 앱을 생성할 때 메타데이터를 함께 설정하면, 자동으로 생성되는 API 문서(Swagger UI)에 프로젝트 정보가 표시됩니다.
1
2
3
4
5
6
# FastAPI 앱 인스턴스 생성
app = FastAPI(
title="Doongzi Backend API",
description="부동산 계약 체크리스트 및 권리분석 자동화 서비스 백엔드 API",
version="1.0.0",
)
이 정보는 /docs 경로에서 확인할 수 있는 Swagger UI의 헤더 부분에 표시됩니다.
② CORS 미들웨어 설정
1
2
3
4
5
6
7
8
# CORS 미들웨어 설정
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # TODO: 운영 환경에서는 실제 도메인으로 제한해야 함
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
프론트엔드와 백엔드가 다른 도메인에서 실행될 때 발생하는 CORS(Cross-Origin Resource Sharing) 문제를 해결하기 위해 미들웨어를 추가합니다.
③ 헬스 체크 엔드포인트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Health Check API (서버 및 로드밸런서 상태 확인용)
@app.get("/health", tags=["System"])
async def health_check() -> Dict[str, str]:
"""
서버 및 로드밸런서 상태를 확인합니다.
Input:
없음
Output:
Dict[str, str]: 상태(status)와 확인 메시지(message)
Exception:
없음
Description:
- 도커 컨테이너, AWS 로드밸런서 등에서 서버가 정상적으로 응답하는지 주기적으로 찔러보는 용도의 엔드포인트입니다.
"""
return {"status": "ok", "message": "Doongzi API server is running smoothly."}
서버가 정상적으로 동작하는지 확인하기 위한 간단한 헬스 체크 엔드포인트를 구성합니다. 이는 로드 밸런서나 모니터링 도구에서 활용됩니다.
2. 환경 설정 관리: config.py
프로젝트의 모든 설정을 한 곳에서 관리하는 것이 중요합니다. app/core/config.py에서 Pydantic의 BaseSettings를 활용하여 타입 안전한 설정 관리를 구현합니다.
왜 Pydantic Settings를 사용하나?
Python에서 환경 변수를 읽으면 모든 값이 문자열(str)로 반환됩니다. 이를 int나 bool로 변환하고, 필수 값이 누락되었는지 검증하는 작업은 번거롭고 실수하기 쉽습니다.
Pydantic Settings는 이 모든 과정을 자동화합니다:
- 자동 타입 변환: 환경 변수
PORT=5432를 자동으로int로 변환 - 필수 값 검증: 기본값이 없는 필드는 반드시 환경 변수에 있어야 함
- IDE 자동완성: 설정 객체의 속성에 타입 힌트가 있어 개발 경험 향상
Settings 클래스 구성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Settings(BaseSettings):
"""
애플리케이션 전역 설정을 관리하는 환경변수 클래스입니다.
Input:
없음 (.env 파일 또는 OS 환경변수에서 자동으로 로드)
Output:
없음
Exception:
pydantic.ValidationError: 필수 환경변수가 누락되었거나 타입이 맞지 않을 때 발생
Description:
- 데이터베이스, Redis, AWS S3, 외부 스크래핑(등본 발급) 정보를 관리합니다.
- .env.example 파일에 명시된 환경변수 규격을 엄격하게 따릅니다.
"""
# 애플리케이션 기본 정보
APP_NAME: str = "Doongzi API"
APP_VERSION: str = "1.0.0"
# [MODE] 운영 환경 구분 (local, dev, prod)
ENV_MODE: str = "local"
# [AWS] local 환경에서만 필요하므로 Optional 처리
AWS_ACCESS_KEY_ID: Optional[str] = None
AWS_SECRET_ACCESS_KEY: Optional[str] = None
# [Database]
POSTGRES_USER: str
POSTGRES_PASSWORD: str
POSTGRES_DB: str
POSTGRES_HOST: str
POSTGRES_PORT: int = 5432
# [AWS S3]
S3_BUCKET_NAME: str
# [Redis]
REDIS_HOST: str
REDIS_PORT: int = 6379
# [등본 발급 자동화]
IROS_PHONE_NUMBER: str
IROS_TEMPORARY_PASSWORD: str
PAYMENT_CODE_1: str
PAYMENT_CODE_2: str
PREPAYMENT_PASSWORD: str
# [건축물대장 발급 자동화]
BUILDING_REGISTER_SERVICE_KEY: str
@property
def database_url(self) -> str:
"""
데이터베이스 연결을 위한 비동기(asyncpg) URL 문자열을 생성합니다.
Input:
없음
Output:
str: SQLAlchemy 비동기 접속용 URL 반환
Exception:
없음
Description:
- 환경변수로 주입받은 POSTGRES_* 접속 정보들을 조합하여 DSN을 반환합니다.
"""
return f"postgresql+asyncpg://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
# .env.local 파일에서 설정을 읽어올 수 있도록 Pydantic 설정
model_config = SettingsConfigDict(
env_file=".env.local",
env_file_encoding="utf-8",
case_sensitive=True,
extra="ignore",
)
Settings 클래스 설계 원칙 3가지
💡 1. .env 파일의 값이 무조건 1순위 (우선 적용)
config.py에 DB_USER: str = "postgres"라고 적어두었더라도, .env.local 파일에 DB_USER=doongzi_admin이라고 적혀있다면 코드의 값은 무시되고 .env 파일의 값이 덮어쓰기(Override) 됩니다.
💡 2. 값이 안 적힌 변수는 필수(Required) 조건
코드에 DB_PASSWORD: str처럼 = 기호 뒤에 기본값을 주지 않은 변수들이 있습니다. 이 변수들은 .env 파일에 이 값이 없으면 서버를 아예 켜지 말라는 뜻입니다. 보안상 중요한 비밀번호나 API 키는 실수로라도 기본값이 들어가면 안 되기 때문에 이렇게 설정합니다.
💡 3. 변수명은 .env 키와 완벽히 일치해야 함
Pydantic이 자동으로 값을 찾아 매핑해주기 때문에, config.py의 변수명(예: DB_HOST)과 .env.local 파일 안의 키 이름은 반드시 똑같아야 합니다.
3. Settings 인스턴스 생성과 활용
전역 인스턴스 vs 의존성 주입 함수
Settings를 사용하는 방법은 두 가지입니다.
① 전역 인스턴스 (settings)
1
2
3
# app/core/config.py
# 전역에서 settings 객체를 import 해서 사용할 수 있도록 인스턴스 생성
settings = Settings()
해당 Settings 클래스의 인스턴스(여기서는 settings 객체)를 생성하면 Pydantic이 환경 변수를 읽고 데이터를 변환하고 검증합니다. 그래서 그 settings 객체를 사용할 때는 선언한 타입의 데이터를 갖게 됩니다(예: POSTGRES_PORT는 int가 됩니다).
app.core.config.settings는 앱이 켜질 때 한 번 만들어지는 전역 객체입니다. main.py의 FastAPI(title=settings.APP_NAME)처럼 전역 스코프에서 즉시 값이 필요할 때 사용합니다.
② 의존성 주입 함수 (get_settings())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/main.py
@lru_cache()
def get_settings() -> Settings:
"""
애플리케이션 설정을 반환하는 함수입니다.
Input:
없음
Output:
Settings: 애플리케이션 설정 객체
Exception:
없음
Description:
- FastAPI의 Depends()를 통해 의존성 주입 시 사용하기 위한 설정 반환 함수입니다.
- @lru_cache를 사용하여 매번 파일을 읽지 않고 메모리에 캐싱된 인스턴스를 반환하여 성능을 최적화합니다.
"""
return Settings()
@lru_cache 데코레이터를 사용하고 있으므로, Settings 객체는 최초 호출 시 딱 한 번만 생성됩니다.
get_settings()는 나중에 각 API 엔드포인트(라우터) 내부에서 의존성 주입(Depends)으로 안전하게 설정값을 꺼내 쓰고 싶을 때 사용할 수 있습니다.
FastAPI 공식 문서에서도 적극 권장하는 방식입니다.
4. 환경별 설정 파일 분리 전략
로컬 개발 환경과 운영 환경은 DB 주소, API 키 등이 다릅니다. 하나의 .env 파일로 관리하면 실수로 운영 DB를 로컬에서 건드릴 위험이 있습니다.
동적 .env 파일 선택
1
2
3
4
# OS 환경변수에서 현재 운영 환경을 읽어와서 .env 파일 결정
current_env = os.getenv("ENV_MODE", "local")
env_filename = f".env.{current_env}"
“지금 내가 실행된 환경이 어디인지”를 파악해서 바라보는 .env 파일의 이름을 동적으로 바꾸도록 코드를 수정합니다.
이렇게 하면 로컬에서 돌릴 때는 .env.local을 찾고, 개발 서버에서는 .env.dev를 찾게 됩니다!
환경별 파일 구조
.env.local: 로컬 개발 환경용 설정 (DB: localhost, 테스트용 API 키).env.dev: 개발 서버 환경용 설정 (개발 DB, 스테이징 API 키).env.prod: 운영 서버 환경용 설정 (운영 DB, 실제 API 키).env.example: 깃허브에 올리는 예시 파일 (실제 값은 제거)
이렇게 환경을 분리하면 ENV_MODE 환경 변수 하나만 바꿔주면 자동으로 적절한 설정 파일을 불러오게 됩니다.
마치며
이제 “타입 안전하게 설정을 관리하고, 환경별로 설정 파일을 분리한” FastAPI 프로젝트의 기초를 갖추었습니다.
다음 포스트에서는 이 설정을 활용하여 데이터베이스 연결을 구성하고, 실제 API 엔드포인트를 구현하는 과정을 다루겠습니다.
레퍼런스
https://fastapi.tiangolo.com/ko/advanced/settings/

