YAML 앵커와 별칭: DRY 설정 패턴
YAML 파일의 여러 섹션에서 같은 설정을 반복하면 DRY (Don't Repeat Yourself) 원칙을 위반하고 유지보수 문제를 만듭니다. YAML 앵커와 별칭은 값을 한 번 정의하고 여러 번 참조할 수 있게 해결합니다.
기본 구문
앵커 (&): 재사용을 위해 값을 표시
별칭 (*): 앵커된 값을 참조
# 앵커 정의
defaults: &default_settings
timeout: 30
retries: 3
log_level: info
# 별칭으로 참조
development:
<<: *default_settings
log_level: debug
production:
<<: *default_settings
retries: 5
<< 병합 키는 참조된 앵커의 모든 키-값 쌍을 포함합니다. 병합 후 정의된 속성은 앵커된 값을 오버라이드합니다.
해석 후 결과:
development:
timeout: 30
retries: 3
log_level: debug # 오버라이드됨
production:
timeout: 30
retries: 5 # 오버라이드됨
log_level: info
앵커 유형
스칼라 앵커
단일 값 재사용:
max_connections: &max_conn 100
database:
pool_size: *max_conn
cache:
pool_size: *max_conn
시퀀스 앵커
전체 목록 재사용:
common_ports: &ports
- 80
- 443
web_server:
ports: *ports
load_balancer:
ports: *ports
매핑 앵커
전체 객체 재사용 (가장 일반적인 패턴):
logging: &log_config
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
api:
logging: *log_config
worker:
logging: *log_config
병합 키 (<<)
병합 키의 특정 동작:
- 참조된 매핑의 모든 키-값 쌍을 복사
- 명시적으로 설정된 키가 병합된 키보다 우선
- 여러 병합은 순서대로 처리 (충돌 시 먼저 오는 것이 우선)
# 다중 병합
base: &base
a: 1
b: 2
extra: &extra
b: 3
c: 4
combined:
<<: [*base, *extra]
# 결과: a=1, b=2 (base가 우선), c=4
실제 예제
Docker Compose
x-common: &common
restart: unless-stopped
networks:
- app-network
logging:
driver: json-file
options:
max-size: "10m"
services:
api:
<<: *common
image: myapp/api:latest
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://db:5432/myapp
worker:
<<: *common
image: myapp/worker:latest
environment:
QUEUE_URL: redis://cache:6379
scheduler:
<<: *common
image: myapp/scheduler:latest
Docker Compose는 앵커를 보유하는 확장 필드에 x- 접두사 관례를 사용합니다. 더 많은 패턴은 Docker Compose를 위한 YAML 가이드를 참조하세요.
GitHub Actions
jobs:
test:
runs-on: ubuntu-latest
env: &common_env
NODE_VERSION: '20'
CI: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- run: npm test
build:
runs-on: ubuntu-latest
needs: test
env:
<<: *common_env
BUILD_TARGET: production
steps:
- uses: actions/checkout@v4
- run: npm run build
Kubernetes
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
resources: &resources
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
- name: sidecar
resources: *resources # 동일한 리소스 제한
제한사항과 함정
1. 파일 간 참조 불가
앵커는 단일 YAML 파일 내에서만 작동합니다. 다른 파일에 정의된 앵커를 참조할 수 없습니다. 파일 간 재사용에는 Helm, Jsonnet, 또는 Kustomize 같은 템플릿 도구를 사용하세요.
2. 시퀀스에서 부분 오버라이드 불가
목록을 병합하고 부분적으로 오버라이드할 수 없습니다 — 전체 목록을 교체합니다:
base_ports: &ports
- 80
- 443
service:
ports: *ports # 정확히 [80, 443]을 얻음
# 이 목록에 포트 8080을 병합으로 추가할 수 없음
해결책: 확장된 목록을 별도로 정의하세요.
3. 순서 중요
앵커는 참조되기 전에 정의되어야 합니다:
# 잘못됨 - 앵커 전에 별칭
service:
settings: *defaults
defaults: &defaults
timeout: 30
# 올바름 - 별칭 전에 앵커
defaults: &defaults
timeout: 30
service:
settings: *defaults
4. 모든 도구가 병합 키를 지원하지 않음
<< 병합 키는 핵심 YAML 사양의 일부가 아닙니다 — 유형별 확장입니다. 대부분의 인기 YAML 파서가 지원하지만 일부 엄격한 파서는 지원하지 않을 수 있습니다.
YAML 검증기로 YAML 앵커 사용을 검증하세요.
FAQ
YAML을 JSON으로 변환하면 앵커가 유지되나요?
아닙니다. YAML이 파싱될 때 앵커와 별칭은 값으로 해석됩니다. 결과 JSON에는 값이 인라인으로 확장됩니다. 이는 JSON으로 변환 후 다시 돌아오면 DRY 구조가 사라진다는 것을 의미합니다. 앵커는 YAML 소스 포맷에만 존재합니다.
앵커를 복잡한 중첩 오버라이드와 함께 사용할 수 있나요?
병합 키 (<<)는 얕은 병합만 수행합니다 — 앵커된 매핑의 최상위 키만 복사합니다. 중첩 객체는 딥 병합되지 않고 완전히 교체됩니다. 딥 병합 동작에는 도구별 솔루션 (Helm의 merge 함수, 커스텀 YAML 프로세서)이 필요합니다.
관련 리소스
- YAML 검증기 — 앵커와 별칭이 포함된 YAML 검증
- YAML 구문 튜토리얼 — YAML 기초
- Docker Compose를 위한 YAML — Docker 전용 패턴