[Python] YAML, TOML, INI: 설정 파일 포맷
정의
설정 파일 포맷 비교:
- JSON: 데이터 직렬화. 주석 없음, 좀 엄격. (
jsonstdlib) - YAML: 풍부한 표현, 가독성 좋음. 보안 함정. (
PyYAML외부) - TOML: 명확한 명세, Python 친화. (
tomllib3.11+ stdlib) - INI: 단순. (
configparserstdlib)
pyproject.toml이 TOML, GitHub Actions가 YAML, 레거시 설정이 INI. 새 프로젝트는 TOML 권장.
TOML (3.11+ 기본 내장)
# config.toml
[server]
host = "localhost"
port = 8080
debug = true
[database]
url = "postgres://localhost/mydb"
pool_size = 10
[[users]]
name = "Alice"
roles = ["admin"]
[[users]]
name = "Bob"
roles = ["user", "viewer"]
import tomllib # 3.11+
with open("config.toml", "rb") as f: # bytes 모드 필수
config = tomllib.load(f)
print(config["server"]["host"])
print(config["users"][0]["name"])
3.10 이하는 tomli 패키지 (pip install tomli).
쓰기는 표준 라이브러리에 없음. tomli-w 또는 tomlkit (포맷 보존).
pip install tomli-w
import tomli_w
with open("config.toml", "wb") as f:
tomli_w.dump(config, f)
YAML
# config.yaml
server:
host: localhost
port: 8080
debug: true
database:
url: postgres://localhost/mydb
pool_size: 10
users:
- name: Alice
roles:
- admin
- name: Bob
roles: [user, viewer]
pip install pyyaml
import yaml
with open("config.yaml") as f:
config = yaml.safe_load(f) # 항상 safe_load!
print(config["server"]["host"])
# 쓰기
with open("config.yaml", "w") as f:
yaml.safe_dump(config, f, default_flow_style=False)
YAML 보안 함정
yaml.load(data) # RCE 가능! 절대 사용 X
yaml.safe_load(data) # 안전
기본 yaml.load는 임의 Python 객체 생성 가능 (!!python/object). 신뢰할 수 없는 입력 → 시스템 침해. 항상 safe_load.
3.x PyYAML은 경고 표시. 보안 의식 강한 곳은 yaml.SafeLoader 명시.
멀티 문서
YAML은 한 파일에 여러 문서 (--- 구분).
# multi.yaml
name: doc1
---
name: doc2
import yaml
with open("multi.yaml") as f:
for doc in yaml.safe_load_all(f):
print(doc)
Kubernetes manifest 등에 사용.
Anchor, alias
DRY 원칙.
defaults: &defaults
timeout: 10
retries: 3
dev:
<<: *defaults
host: localhost
prod:
<<: *defaults
host: prod.example.com
config = yaml.safe_load(text)
print(config["dev"]) # {'timeout': 10, 'retries': 3, 'host': 'localhost'}
ruamel.yaml: 포맷 보존
PyYAML은 read/write 시 주석·순서를 잃는다. 사용자 설정을 프로그램이 일부 수정 후 저장 시 ruamel.yaml 사용.
pip install ruamel.yaml
from ruamel.yaml import YAML
yaml = YAML()
yaml.preserve_quotes = True
with open("config.yaml") as f:
config = yaml.load(f)
config["server"]["port"] = 9090
with open("config.yaml", "w") as f:
yaml.dump(config, f)
# 주석, 빈 줄, 따옴표 형식 보존
INI (configparser)
# config.ini
[DEFAULT]
debug = false
[server]
host = localhost
port = 8080
[database]
url = postgres://localhost/mydb
import configparser
config = configparser.ConfigParser()
config.read("config.ini")
print(config["server"]["host"])
print(config["server"].getint("port")) # int 변환
print(config["DEFAULT"].getboolean("debug")) # bool 변환
INI는 평면적. 중첩 구조 표현 약함. TOML 권장.
비교 요약
| 포맷 | 사람 가독성 | 안전성 | 표현력 | Python stdlib | 주석 |
|---|---|---|---|---|---|
| JSON | 보통 | 안전 | 기본 | O | X |
| TOML | 좋음 | 안전 | 풍부 | O (3.11+) | O |
| YAML | 최상 | 함정 있음 | 매우 풍부 | X | O |
| INI | 보통 | 안전 | 평면적 | O | O |
환경 변수 + 설정 파일
실무에선 보통 둘 결합.
import os
import tomllib
with open("config.toml", "rb") as f:
config = tomllib.load(f)
# 환경 변수가 우선
config["database"]["url"] = os.environ.get(
"DATABASE_URL", config["database"]["url"]
)
pydantic-settings
pydantic-settings로 환경변수 + 파일 + 기본값 자동 병합.
pip install pydantic-settings
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")
debug: bool = False
database_url: str
api_key: str
settings = Settings() # 환경변수 자동 로드 + 검증
타입 안전 + 검증 + 다중 소스 (.env, 환경변수, secrets directory).
dynaconf
dynaconf는 더 풍부한 옵션 (Vault 통합, 동적 reload, 환경별 분리).
pip install dynaconf
자주 보는 패턴
환경별 설정
# settings.toml
[default]
debug = false
log_level = "INFO"
[development]
debug = true
log_level = "DEBUG"
[production]
log_level = "WARNING"
# 코드
import os
import tomllib
with open("settings.toml", "rb") as f:
all_settings = tomllib.load(f)
env = os.environ.get("APP_ENV", "default")
config = {**all_settings["default"], **all_settings.get(env, {})}
비밀 정보 분리
# config.toml (커밋)
[server]
host = "localhost"
port = 8080
[database]
# url은 환경변수 또는 secret store에서
import os
config = load_config()
config["database"]["url"] = os.environ["DATABASE_URL"]
코드와 설정과 비밀을 분리. 12-factor app 원칙.
스키마 검증
from pydantic import BaseModel, Field
import tomllib
class ServerConfig(BaseModel):
host: str
port: int = Field(ge=1, le=65535)
class Config(BaseModel):
server: ServerConfig
with open("config.toml", "rb") as f:
raw = tomllib.load(f)
config = Config(**raw) # 검증
print(config.server.host)
잘못된 설정을 시작 시 즉시 발견. pydantic, jsonschema 활용.
함정
YAML 미묘한 타입 변환
version: 1.10 # float! '1.1' 아님
port: 8000 # int
date: 2026-06-20 # date 객체
yes: 1 # YAML 1.1: True (PyYAML은 1.1 사용)
명시적 따옴표: version: "1.10".
TOML 날짜 타입
TOML은 datetime, date, time 타입 내장.
created = 2026-06-20T14:30:00+09:00
tomllib.load 결과는 datetime.datetime 객체로 자동 변환.
INI는 모두 문자열
port = 8080
config["server"]["port"] # '8080' (str)
config["server"].getint("port") # 8080 (int)
💬 댓글