ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • yaml 가이드
    공부/데이터 2026. 4. 5. 15:22

    1. YAML이란?

    YAML"YAML Ain't Markup Language" 의 재귀 약어입니다. 초기에는 "Yet Another Markup Language"로 불렸으나, XML 같은 마크업 언어와 구분하기 위해 현재 이름으로 바뀌었습니다.

    공식 정의: 모든 프로그래밍 언어를 위한 사람 친화적 데이터 직렬화 언어(human-friendly data serialization language)

    역사

    연도 사건
    2001 Clark Evans, Oren Ben-Kiki, Ingy döt Net이 최초 YAML 프레임워크 작성
    2004 YAML 1.0 공식 스펙 발표
    2009 YAML 1.2 발표 — JSON 완전 호환 강화
    2021 YAML 1.2.2 발표 (현재 최신) — 오픈 소스 개발 방식 전환

    핵심 특징

    • 들여쓰기 기반 구조 — 중괄호/꺾쇠 대신 스페이스(탭 금지)로 계층 표현
    • 주석 지원# 이후는 주석
    • 3가지 기본 자료형 — 스칼라(Scalar), 시퀀스(Sequence), 매핑(Mapping)
    • 앵커/앨리어스&로 앵커 정의, *로 참조하여 중복 제거
    • 멀티라인 문자열 — 리터럴(|)과 폴드(>) 두 가지 스타일
    • JSON 상위 호환 — 유효한 JSON은 곧 유효한 YAML

    2. 언제 쓰면 좋은가

    주요 Use Cases

    용도 예시
    설정 파일 서버 설정, 앱 설정, 환경별 config
    CI/CD 파이프라인 GitHub Actions, GitLab CI, CircleCI
    컨테이너 오케스트레이션 Kubernetes 매니페스트, Docker Compose
    IaC (인프라 코드화) Ansible 플레이북, Terraform 변수 파일
    API 명세 OpenAPI/Swagger 스펙
    테스트 데이터/픽스처 시드 데이터, mock 응답, 테스트 케이스

    YAML이 적합하지 않은 경우

    • 고성능 데이터 교환 API → JSON이 파싱 속도 우위
    • 단순 키-값만 필요 → INI가 더 직관적
    • 임의 입력 파싱 시 보안 주의 → Python yaml.load() 대신 yaml.safe_load() 필수

    3. 다른 파일 포맷과의 비교

    항목 YAML JSON TOML INI XML
    주석 ✅ (#) ✅ (#) ✅ (<!-- -->)
    가독성 매우 높음 중간 높음 높음 낮음
    중첩 구조 무제한 무제한 제한적 미지원 무제한
    파싱 속도 느림 빠름 중간 빠름 느림
    앵커/재사용
    날짜/시간 타입 ✅ 기본 지원 ✅ 기본 지원
    대표 용도 설정, K8s API, 웹 Rust/Python 패키지 레거시 앱 설정 엔터프라이즈 교환
    - 동일 데이터를 각 포맷으로 표현 — 비교 예시          
    **YAML**
    
    ```yaml
    # 서버 설정
    database:
      host: localhost
      port: 5432
    features:
      - authentication
      - logging
    debug: false
    ```
    
    **JSON**
    
    ```json
    {
      "database": { "host": "localhost", "port": 5432 },
      "features": ["authentication", "logging"],
      "debug": false
    }
    ```
    
    **TOML**
    
    ```toml
    debug = false
    [database]
    host = "localhost"
    port = 5432
    features = ["authentication", "logging"]
    ```
    
    **INI**
    
    ```
    [database]
    host = localhost
    port = 5432
    ; features 배열 표현 불가
    ```
    
    **XML**
    
    ```xml
    <config>
      <database>
        <host>localhost</host>
        <port>5432</port>
      </database>
      <features>
        <feature>authentication</feature>
        <feature>logging</feature>
      </features>
      <debug>false</debug>
    </config>
    ```

    4. YAML 기본 문법

    4-1. 스칼라 (Scalars)

    # 문자열
    name: Alice
    greeting: "Hello,\nWorld!"   # 큰따옴표: 이스케이프 해석
    literal: 'No \n escape'       # 작은따옴표: 이스케이프 없음
    
    # 숫자
    count: 42
    ratio: 3.14
    hex: 0xFF      # 255
    
    # 불리언 (YAML 1.2 기준)
    active: true
    disabled: false
    
    # null
    nothing: null
    also_null: ~
    
    # 타입 강제 지정 (!!)
    as_string: !!str 3.14    # 숫자를 문자열로
    as_int: !!int "80"       # 문자열을 정수로

    4-2. 매핑 (Mappings) — 딕셔너리

    # 블록 스타일
    person:
      name: Bob
      age: 30
      address:
        city: Seoul
        country: KR
    
    # 플로우 스타일 (한 줄, JSON과 유사)
    point: { x: 10, y: 20 }

    4-3. 시퀀스 (Sequences) — 배열

    # 블록 스타일
    fruits:
      - apple
      - banana
      - cherry
    
    # 객체 배열
    users:
      - name: Alice
        role: admin
      - name: Bob
        role: user
    
    # 플로우 스타일
    colors: [red, green, blue]

    위 내용을 json으로 변환하면 아래와 같습니다.

    # 블록 스타일
    {
      "fruits": [
        "apple",
        "banana",
        "cherry"
      ]
    }
    
    # 객체 배열
    {
      "users": [
        {
          "name": "Alice",
          "role": "admin"
        },
        {
          "name": "Bob",
          "role": "user"
        }
      ]
    }

    4-4. 멀티라인 문자열

    # | (리터럴): 줄바꿈 그대로 보존
    script: |
      #!/bin/bash
      echo "Hello"
      npm install
    # 결과: "#!/bin/bash\necho \"Hello\"\nnpm install\n"
    
    # > (폴드): 줄바꿈을 공백으로 변환
    description: >
      This is a long description
      that spans multiple lines.
    # 결과: "This is a long description that spans multiple lines.\n"
    수정자 의미
    ` />`
    ` -/>-`
    ` +/>+`

    4-5. 앵커 & 앨리어스 — 중복 제거

    # &anchor_name 으로 정의, *anchor_name 으로 참조
    defaults: &defaults
      adapter: postgres
      host: localhost
      pool: 5
    
    development:
      <<: *defaults        # defaults 전체를 병합
      database: myapp_dev
    
    production:
      <<: *defaults
      database: myapp_prod
      host: prod-db.example.com  # 로컬 값이 앵커 값을 덮어씀
    
    # 시퀀스(배열)일 때,
    
    -common: &common
      - run: echo "Hello"
    
    jobs:
      lint:
        runs-on: ubuntu-latest
        steps:
          - *common
          - run: npm run lint

    4-6. 복수 문서 (---)

    ---
    # 첫 번째 문서
    name: Document One
    type: config
    ---
    # 두 번째 문서
    name: Document Two
    type: data

    5. Helm에서의 YAML 기법

    출처: Helm Chart Template Guide — YAML Techniques

    타입 강제 (!!)

    age: !!str 21       # 정수 → 문자열
    port: !!int "80"    # 문자열 → 정수

    멀티라인 문자열 (Helm 템플릿에서)

    coffee: |
      Latte
      Cappuccino
      Espresso
    
    # >- : 단일 줄로 폴드 + 마지막 줄바꿈 제거
    coffee: >-
      Latte
      Cappuccino

    파일 내용 삽입 시 들여쓰기

    myfile: |
    {{ .Files.Get "myfile.txt" | indent 2 }}

    템플릿 표현식 자체를 들여쓰면 첫 줄이 이중 들여쓰기됩니다. indent N 함수에 들여쓰기를 위임하세요.

    JSON as YAML (인라인 스타일)

    YAML은 JSON의 상위집합이므로 혼합 표현이 가능합니다.

    # 블록 스타일
    coffee:
      - Latte
      - Cappuccino
    
    # JSON 인라인 스타일 (동일한 의미)
    coffee: ["Latte", "Cappuccino"]

    Helm에서 앵커 주의사항


    6. Kubernetes (K8s) 매니페스트

    4가지 필수 필드

    apiVersion: apps/v1    # 사용할 API 그룹/버전
    kind: Deployment        # 리소스 종류
    metadata:               # 리소스 식별 정보
      name: my-app
      namespace: production
    spec:                   # 원하는 상태 (desired state)
      ...
    • Deployment 예시
    • apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25.3 ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" env: - name: SECRET_KEY valueFrom: secretKeyRef: name: my-secret key: secret-key readinessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 5 periodSeconds: 10
    • Service 예시 (ClusterIP / NodePort / LoadBalancer)
    • # ClusterIP (기본값, 클러스터 내부만) apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 --- # LoadBalancer (클라우드 외부 노출) apiVersion: v1 kind: Service metadata: name: nginx-lb spec: type: LoadBalancer selector: app: nginx ports: - port: 80 targetPort: 80
    • ConfigMap & Secret
    • apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DATABASE_HOST: "postgres.internal" config.yaml: | server: port: 8080 log_level: info --- apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: username: YWRtaW4= # echo -n 'admin' | base64 password: cGFzc3dvcmQ=

    하나의 파일에 여러 리소스 정의--- 로 구분

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-app
    ...
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: my-app-svc
    ...

    7. GitHub Actions / CI/CD 파이프라인

    GitHub Actions 워크플로우는 .github/workflows/*.yml 파일로 정의합니다.

    • 기본 워크플로우 구조
    • name: CI Pipeline on: push: branches: [main, 'release/**'] pull_request: branches: [main] schedule: - cron: '0 2 * * *' env: NODE_VERSION: '20' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npm run build - uses: actions/upload-artifact@v4 with: name: build-output path: dist/
    • 잡 의존성(needs)과 매트릭스 전략
    • jobs: build: runs-on: ubuntu-latest steps: - run: npm run build test: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [18, 20, 22] os: [ubuntu-latest, windows-latest] exclude: - os: windows-latest node-version: 18 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm test deploy: needs: [build, test] runs-on: ubuntu-latest environment: production if: github.ref == 'refs/heads/main' steps: - name: Deploy env: DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} run: ./scripts/deploy.sh
    • 앵커로 반복 스텝 재사용
    • x-common-steps: &common-setup - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci jobs: lint: runs-on: ubuntu-latest steps: - *common-setup - run: npm run lint test: runs-on: ubuntu-latest steps: - *common-setup - run: npm test

    8. Docker Compose

    • 기본 Compose 파일 구조
    • name: my-application services: web: image: nginx:alpine ports: - "8080:80" depends_on: app: condition: service_healthy networks: - frontend - backend app: build: context: . dockerfile: Dockerfile environment: - DATABASE_URL=postgres://user:pass@db:5432/mydb healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 networks: - backend restart: unless-stopped db: image: postgres:16-alpine volumes: - postgres-data:/var/lib/postgresql/data environment: POSTGRES_DB: mydb POSTGRES_USER: user POSTGRES_PASSWORD: pass networks: - backend volumes: postgres-data: networks: frontend: backend: internal: true
    • 앵커를 활용한 Compose 최적화
    • # x- 접두사는 Compose가 무시하는 확장 필드 x-common-env: &common-env LOG_LEVEL: info TIMEZONE: Asia/Seoul x-resource-limits: &resource-limits deploy: resources: limits: cpus: '0.5' memory: 512M services: service-a: image: my-image:latest environment: <<: *common-env SERVICE_NAME: service-a <<: *resource-limits service-b: image: my-image:latest environment: <<: *common-env SERVICE_NAME: service-b <<: *resource-limits

    9. Ansible 플레이북

    • 플레이북 기본 구조
    • --- - name: Configure web servers hosts: webservers become: true vars: http_port: 80 packages: - nginx - git tasks: - name: Install required packages ansible.builtin.apt: name: "{{ packages }}" state: present update_cache: yes - name: Start and enable nginx ansible.builtin.service: name: nginx state: started enabled: yes notify: Reload nginx handlers: - name: Reload nginx ansible.builtin.service: name: nginx state: reloaded
    • 변수, 조건문, 반복문, 블록
    • tasks: # loop — 반복 - name: Create user accounts ansible.builtin.user: name: "{{ item.name }}" groups: "{{ item.groups }}" loop: "{{ users }}" # when — 조건 - name: Install Apache (Debian 계열만) ansible.builtin.apt: name: apache2 when: ansible_os_family == "Debian" # block — 그룹화 + 에러 처리 - name: Deploy application block: - name: Pull code ansible.builtin.git: repo: https://github.com/org/app.git dest: /var/www/app rescue: - name: Notify failure ansible.builtin.debug: msg: "Deployment failed!" always: - name: Cleanup ansible.builtin.file: path: /tmp/deploy state: absent

    10. OpenAPI / Swagger 스펙

    • 기본 OpenAPI 3.x 구조
    • openapi: 3.0.4 info: title: User Management API version: 1.0.0 servers: - url: https://api.example.com/v1 paths: /users: get: summary: 사용자 목록 조회 parameters: - name: page in: query schema: type: integer default: 1 responses: '200': content: application/json: schema: $ref: '#/components/schemas/UserList' /users/{userId}: get: parameters: - name: userId in: path required: true schema: type: string format: uuid responses: '200': content: application/json: schema: $ref: '#/components/schemas/User' components: schemas: User: type: object properties: id: type: string format: uuid email: type: string format: email UserList: type: object properties: data: type: array items: $ref: '#/components/schemas/User' total: type: integer securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT security: - BearerAuth: []

    11. 프로그래밍 언어별 YAML 라이브러리

    Python — PyYAML

    import yaml
    
    # ✅ 항상 safe_load 사용 (yaml.load()는 임의 코드 실행 위험)
    with open('config.yaml', 'r') as f:
        config = yaml.safe_load(f)
    
    # 직렬화
    data = {'server': {'host': 'localhost', 'port': 8080}}
    yaml_string = yaml.dump(data, default_flow_style=False, allow_unicode=True)
    
    # 멀티 도큐먼트
    docs = list(yaml.safe_load_all("---\nname: doc1\n---\nname: doc2\n"))

    JavaScript/TypeScript — js-yaml

    import yaml from 'js-yaml';
    import fs from 'fs';
    
    const config = yaml.load(fs.readFileSync('config.yaml', 'utf8'));
    
    const yamlString = yaml.dump({ name: 'app', version: '1.0.0' }, { indent: 2 });

    Go — gopkg.in/yaml.v3

    import "gopkg.in/yaml.v3"
    
    type Config struct {
        Server struct {
            Host string `yaml:"host"`
            Port int    `yaml:"port"`
        } `yaml:"server"`
    }
    
    var cfg Config
    yaml.Unmarshal(data, &cfg)
    
    // Strict mode: 알 수 없는 필드 에러 처리
    decoder := yaml.NewDecoder(reader)
    decoder.KnownFields(true)

    12. 모범 사례 및 주의사항

    흔한 함정

    # ⚠️ 1. Norway Problem — YAML 1.1에서 국가 코드가 boolean으로 해석
    country: NO      # false로 해석될 수 있음!
    country: "NO"    # ✅ 따옴표로 감싸기
    
    # ⚠️ 2. 버전 번호가 부동소수점으로 처리
    version: 1.10    # 1.1과 같아질 수 있음
    version: "1.10"  # ✅ 문자열로 강제
    
    # ⚠️ 3. 탭 사용 금지 — 항상 스페이스
    
    # ⚠️ 4. 콜론 포함 문자열
    message: Hello: World     # 파싱 오류
    message: "Hello: World"   # ✅
    
    # ⚠️ 5. null vs 빈 문자열
    value:    # null
    value: "" # 빈 문자열 (null 아님)

    따옴표 가이드라인

    # 일반 문자열 — 따옴표 불필요
    name: John Doe
    
    # 작은따옴표 — 백슬래시 그대로 유지
    path: 'C:\Users\John'
    regex: '^\d{3}-\d{4}$'
    
    # 큰따옴표 — 이스케이프 시퀀스 필요 시
    newline: "Line 1\nLine 2"
    tab: "Col1\tCol2"

    보안

    DRY 원칙 — 앵커/머지 키 활용

    _defaults: &defaults
      timeout: 30
      retries: 3
    
    production:
      <<: *defaults
      host: prod.example.com
    
    staging:
      <<: *defaults
      host: staging.example.com
      timeout: 60   # 오버라이드

    참고 자료

    댓글