설정 파일 형식 완벽 가이드 | JSON·YAML·XML·TOML·INI 비교

설정 파일 형식 완벽 가이드 | JSON·YAML·XML·TOML·INI 비교

이 글의 핵심

JSON, YAML, XML, TOML, INI, Markdown 등 프로그래밍에서 자주 사용되는 파일 형식의 특징과 차이점. 각 형식의 장단점, 사용 사례, 파싱 방법, 실전 예제로 완벽 이해.

들어가며: 설정 파일 형식의 중요성

모든 프로그래밍 프로젝트는 설정 파일을 사용합니다. package.json, docker-compose.yml, nginx.conf, README.md 등 각 형식마다 특징과 용도가 다릅니다. 올바른 형식을 선택하면 코드 가독성과 유지보수성이 크게 향상됩니다.

이 글에서 다룰 형식:

  • JSON (JavaScript Object Notation)
  • YAML (YAML Ain't Markup Language)
  • XML (eXtensible Markup Language)
  • TOML (Tom's Obvious, Minimal Language)
  • INI (Initialization File)
  • Markdown
  • 기타 (Properties, HCL, Jsonnet)

목차

  1. JSON
  2. YAML
  3. XML
  4. TOML
  5. INI
  6. Markdown
  7. 형식 비교 및 선택 가이드
  8. 실전 변환 및 검증

1. JSON (JavaScript Object Notation)

JSON이란?

JSON은 JavaScript 객체 표기법을 기반으로 한 경량 데이터 교환 형식입니다. 사람이 읽기 쉽고, 기계가 파싱하기 쉽습니다.

JSON 기본 문법

{
  "name": "John Doe",
  "age": 30,
  "email": "[email protected]",
  "isActive": true,
  "balance": 1234.56,
  "tags": ["developer", "designer"],
  "address": {
    "street": "123 Main St",
    "city": "Seoul",
    "zipCode": "12345"
  },
  "projects": [
    {
      "id": 1,
      "name": "Project A",
      "status": "active"
    },
    {
      "id": 2,
      "name": "Project B",
      "status": "completed"
    }
  ],
  "metadata": null
}

JSON 데이터 타입

{
  "string": "Hello, World!",
  "number": 42,
  "float": 3.14159,
  "boolean": true,
  "null": null,
  "array": [1, 2, 3, "mixed", true],
  "object": {
    "nested": "value"
  }
}

JSON 장단점

flowchart TB
    JSON[JSON]
    
    subgraph Pros[장점]
        P1[✅ 파싱 속도 빠름]
        P2[✅ JavaScript 네이티브]
        P3[✅ 간결한 문법]
        P4[✅ 널리 지원됨]
    end
    
    subgraph Cons[단점]
        C1[❌ 주석 불가]
        C2[❌ 후행 쉼표 불가]
        C3[❌ 멀티라인 문자열 불편]
        C4[❌ 날짜 타입 없음]
    end
    
    JSON --> Pros
    JSON --> Cons

JSON 사용 예시

// JavaScript
const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3
};

// JSON으로 변환
const json = JSON.stringify(config, null, 2);
console.log(json);

// JSON 파싱
const parsed = JSON.parse(json);
console.log(parsed.apiUrl);
# Python
import json

config = {
    "apiUrl": "https://api.example.com",
    "timeout": 5000,
    "retries": 3
}

# JSON으로 변환
json_str = json.dumps(config, indent=2)
print(json_str)

# JSON 파싱
parsed = json.loads(json_str)
print(parsed['apiUrl'])

# 파일 I/O
with open('config.json', 'w') as f:
    json.dump(config, f, indent=2)

with open('config.json', 'r') as f:
    loaded = json.load(f)

JSON Schema (검증)

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "minItems": 1
    }
  },
  "required": ["name", "email"]
}

2. YAML (YAML Ain't Markup Language)

YAML이란?

YAML은 사람이 읽기 쉬운 데이터 직렬화 형식입니다. 들여쓰기로 계층 구조를 표현하며, 주석을 지원합니다.

YAML 기본 문법

# 주석은 #으로 시작

# 키-값 쌍
name: John Doe
age: 30
email: [email protected]

# 불린
isActive: true
isDeleted: false

# Null
metadata: null
# 또는
metadata: ~

# 문자열 (따옴표 선택적)
city: Seoul
country: "South Korea"
description: 'Single quotes'

# 멀티라인 문자열
bio: |
  This is a multi-line string.
  It preserves line breaks.
  Very useful for long text.

summary: >
  This is a folded string.
  Line breaks are converted to spaces.
  Useful for long paragraphs.

# 배열 (리스트)
tags:
  - developer
  - designer
  - writer

# 또는 인라인
tags: [developer, designer, writer]

# 객체 (딕셔너리)
address:
  street: 123 Main St
  city: Seoul
  zipCode: "12345"

# 또는 인라인
address: {street: 123 Main St, city: Seoul, zipCode: "12345"}

# 배열 + 객체
projects:
  - id: 1
    name: Project A
    status: active
  - id: 2
    name: Project B
    status: completed

# 앵커와 별칭 (재사용)
defaults: &defaults
  timeout: 30
  retries: 3

production:
  <<: *defaults
  apiUrl: https://api.example.com

development:
  <<: *defaults
  apiUrl: http://localhost:3000

YAML 주의사항

# ❌ 탭 사용 불가 (공백만 가능)
name: value
  nested: error  # 탭으로 들여쓰기하면 에러!

# ✅ 공백 사용
name: value
  nested: correct

# ❌ 들여쓰기 불일치
items:
  - name: item1
    value: 100
   - name: item2  # 들여쓰기 오류!

# ✅ 일관된 들여쓰기
items:
  - name: item1
    value: 100
  - name: item2
    value: 200

# 특수 문자 주의
# 콜론(:)이 포함된 문자열은 따옴표 필요
url: "https://example.com"  # 따옴표 권장
time: "12:30"  # 따옴표 필수

# 숫자로 시작하는 문자열
version: "1.0"  # 따옴표 필요 (아니면 숫자로 파싱)

YAML 사용 예시

# Python
import yaml

config = {
    'name': 'MyApp',
    'version': '1.0.0',
    'database': {
        'host': 'localhost',
        'port': 5432,
        'name': 'mydb'
    },
    'features': ['auth', 'api', 'admin']
}

# YAML로 변환
yaml_str = yaml.dump(config, default_flow_style=False)
print(yaml_str)

# YAML 파싱
parsed = yaml.safe_load(yaml_str)
print(parsed['database']['host'])

# 파일 I/O
with open('config.yaml', 'w') as f:
    yaml.dump(config, f, default_flow_style=False)

with open('config.yaml', 'r') as f:
    loaded = yaml.safe_load(f)

Docker Compose 예시

version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./html:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    environment:
      - NGINX_HOST=example.com
      - NGINX_PORT=80
    depends_on:
      - app
    networks:
      - webnet
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
  
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - NODE_ENV=production
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./app:/app
      - /app/node_modules
    networks:
      - webnet
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
  
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - webnet

networks:
  webnet:
    driver: bridge

volumes:
  db-data:

3. XML (eXtensible Markup Language)

XML이란?

XML은 확장 가능한 마크업 언어로, 태그 기반으로 데이터를 구조화합니다. 엄격한 문법과 스키마 검증을 지원합니다.

XML 기본 문법

<?xml version="1.0" encoding="UTF-8"?>
<!-- 주석 -->

<configuration>
  <!-- 단순 요소 -->
  <name>MyApp</name>
  <version>1.0.0</version>
  
  <!-- 속성 -->
  <database type="postgresql" ssl="true">
    <host>localhost</host>
    <port>5432</port>
    <name>mydb</name>
    <credentials>
      <username>user</username>
      <password>pass</password>
    </credentials>
  </database>
  
  <!-- 배열 (반복 요소) -->
  <features>
    <feature name="auth" enabled="true"/>
    <feature name="api" enabled="true"/>
    <feature name="admin" enabled="false"/>
  </features>
  
  <!-- CDATA (특수 문자 포함) -->
  <description><![CDATA[
    This is a <description> with special characters: & < > " '
  ]]></description>
  
  <!-- 네임스페이스 -->
  <config xmlns="http://example.com/config"
          xmlns:db="http://example.com/database">
    <db:connection>localhost</db:connection>
  </config>
</configuration>

XML 파싱

# Python (ElementTree)
import xml.etree.ElementTree as ET

# XML 파싱
tree = ET.parse('config.xml')
root = tree.getroot()

# 요소 접근
name = root.find('name').text
print(f"Name: {name}")

# 속성 접근
db = root.find('database')
db_type = db.get('type')
print(f"Database type: {db_type}")

# 반복 요소
for feature in root.findall('.//feature'):
    name = feature.get('name')
    enabled = feature.get('enabled')
    print(f"Feature {name}: {enabled}")

# XPath 사용
host = root.find('.//database/host').text
print(f"Database host: {host}")
// JavaScript (Browser)
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, "text/xml");

// 요소 접근
const name = xmlDoc.getElementsByTagName("name")[0].textContent;
console.log(name);

// 속성 접근
const db = xmlDoc.getElementsByTagName("database")[0];
const dbType = db.getAttribute("type");
console.log(dbType);

XML Schema (XSD)

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  
  <xs:element name="configuration">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="version" type="xs:string"/>
        <xs:element name="database" type="DatabaseType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  
  <xs:complexType name="DatabaseType">
    <xs:sequence>
      <xs:element name="host" type="xs:string"/>
      <xs:element name="port" type="xs:integer"/>
    </xs:sequence>
    <xs:attribute name="type" type="xs:string" use="required"/>
  </xs:complexType>
  
</xs:schema>

XML 사용 사례

<!-- Maven (pom.xml) -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>myapp</artifactId>
  <version>1.0.0</version>
  
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>3.2.0</version>
    </dependency>
  </dependencies>
</project>

<!-- Spring (applicationContext.xml) -->
<beans xmlns="http://www.springframework.org/schema/beans">
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="org.postgresql.Driver"/>
    <property name="url" value="jdbc:postgresql://localhost:5432/mydb"/>
  </bean>
</beans>

<!-- Android (AndroidManifest.xml) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
  <uses-permission android:name="android.permission.INTERNET"/>
  <application android:label="MyApp">
    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
      </intent-filter>
    </activity>
  </application>
</manifest>

4. TOML (Tom's Obvious, Minimal Language)

TOML이란?

TOML은 읽기 쉽고 명확한 설정 파일 형식입니다. INI의 개선 버전으로, 타입과 중첩을 지원합니다.

TOML 기본 문법

# 주석

# 키-값 쌍
name = "MyApp"
version = "1.0.0"

# 숫자
port = 8080
timeout = 30.5

# 불린
debug = true
production = false

# 날짜/시간
created_at = 2026-04-01T10:00:00Z

# 배열
tags = ["rust", "web", "api"]

# 인라인 테이블
author = { name = "John Doe", email = "[email protected]" }

# 테이블 (섹션)
[database]
host = "localhost"
port = 5432
name = "mydb"

[database.credentials]
username = "user"
password = "pass"

# 배열 테이블
[[servers]]
name = "server1"
ip = "192.168.1.1"
role = "primary"

[[servers]]
name = "server2"
ip = "192.168.1.2"
role = "backup"

# 중첩 구조
[app.cache]
enabled = true
ttl = 3600

[app.cache.redis]
host = "localhost"
port = 6379

TOML 파싱

# Python
import tomli  # Python 3.11+는 tomllib 내장

with open('config.toml', 'rb') as f:
    config = tomli.load(f)

print(config['name'])
print(config['database']['host'])
print(config['servers'][0]['name'])

# TOML 생성
import tomli_w

data = {
    'name': 'MyApp',
    'version': '1.0.0',
    'database': {
        'host': 'localhost',
        'port': 5432
    }
}

with open('output.toml', 'wb') as f:
    tomli_w.dump(data, f)
// Rust
use serde::Deserialize;
use std::fs;

#[derive(Deserialize)]
struct Config {
    name: String,
    version: String,
    database: Database,
}

#[derive(Deserialize)]
struct Database {
    host: String,
    port: u16,
}

fn main() {
    let contents = fs::read_to_string("config.toml").unwrap();
    let config: Config = toml::from_str(&contents).unwrap();
    
    println!("Name: {}", config.name);
    println!("DB Host: {}", config.database.host);
}

TOML 사용 사례

# Cargo.toml (Rust)
[package]
name = "myapp"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[dev-dependencies]
criterion = "0.5"

[profile.release]
opt-level = 3
lto = true

# pyproject.toml (Python)
[project]
name = "myapp"
version = "1.0.0"
description = "My Python application"
authors = [{name = "John Doe", email = "[email protected]"}]
dependencies = [
    "fastapi>=0.104.0",
    "uvicorn>=0.24.0",
]

[project.optional-dependencies]
dev = ["pytest>=7.4.0", "black>=23.0.0"]

[tool.black]
line-length = 88
target-version = ['py311']

5. INI (Initialization File)

INI란?

INI는 간단한 설정 파일 형식으로, Windows에서 널리 사용됩니다. 섹션과 키-값 쌍으로 구성됩니다.

INI 기본 문법

; 주석은 세미콜론으로 시작
# 또는 해시(#)로도 가능

; 전역 설정 (섹션 없음)
app_name = MyApp
version = 1.0.0

; 섹션
[database]
host = localhost
port = 5432
name = mydb
username = user
password = pass

[cache]
enabled = true
ttl = 3600
type = redis

[cache.redis]
host = localhost
port = 6379

; 배열 (비표준, 구현마다 다름)
[features]
feature1 = auth
feature2 = api
feature3 = admin

; 또는
features = auth,api,admin

INI 파싱

# Python
import configparser

config = configparser.ConfigParser()
config.read('config.ini')

# 값 읽기
app_name = config['DEFAULT']['app_name']
db_host = config['database']['host']
db_port = config.getint('database', 'port')
cache_enabled = config.getboolean('cache', 'enabled')

print(f"App: {app_name}")
print(f"DB: {db_host}:{db_port}")
print(f"Cache: {cache_enabled}")

# 값 쓰기
config['database']['host'] = 'db.example.com'

with open('config.ini', 'w') as f:
    config.write(f)

INI 사용 사례

; php.ini
[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
serialize_precision = -1
disable_functions = exec,passthru,shell_exec,system

[Date]
date.timezone = Asia/Seoul

[Session]
session.save_handler = files
session.save_path = "/var/lib/php/sessions"
session.gc_maxlifetime = 1440

; .gitconfig
[user]
    name = John Doe
    email = [email protected]

[core]
    editor = vim
    autocrlf = input

[alias]
    st = status
    co = checkout
    br = branch
    ci = commit

6. Markdown

Markdown이란?

Markdown은 일반 텍스트로 서식 있는 문서를 작성하는 경량 마크업 언어입니다.

Markdown 기본 문법

# 제목 1 (H1)
## 제목 2 (H2)
### 제목 3 (H3)

**굵게** 또는 __굵게__
*기울임* 또는 _기울임_
~~취소선~~
`인라인 코드`

> 인용문
> 여러 줄 가능

- 순서 없는 리스트
- 항목 2
  - 중첩 항목
  - 중첩 항목 2

1. 순서 있는 리스트
2. 항목 2
3. 항목 3

[링크 텍스트](https://example.com)
![이미지 대체 텍스트](image.png)

---

수평선

| 컬럼 1 | 컬럼 2 | 컬럼 3 |
|--------|--------|--------|
| 값 1   | 값 2   | 값 3   |
| 값 4   | 값 5   | 값 6   |

```코드 블록```
여러 줄 코드

```python
# 언어 지정 (신택스 하이라이팅)
def hello():
    print("Hello, World!")
  • 체크박스 (미완료)
  • 체크박스 (완료)

### GitHub Flavored Markdown (GFM)

```markdown
# GitHub 확장 기능

## 작업 목록
- [x] 완료된 작업
- [ ] 미완료 작업
- [ ] 진행 중

## 테이블 정렬
| Left | Center | Right |
|:-----|:------:|------:|
| 왼쪽 | 중앙   | 오른쪽 |

## 이모지
:smile: :rocket: :tada:

## 멘션
@username

## 이슈 참조
#123

## 코드 블록 파일명
```javascript:app.js
console.log("Hello");

접기/펼치기

클릭하여 펼치기

숨겨진 내용

수식 (LaTeX)

$E = mc^2$

$$ \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$


### Markdown 파싱

```python
# Python (markdown 라이브러리)
import markdown

md_text = """
# Hello

This is **bold** and *italic*.

- Item 1
- Item 2
"""

html = markdown.markdown(md_text)
print(html)
# <h1>Hello</h1>
# <p>This is <strong>bold</strong> and <em>italic</em>.</p>
# <ul>
# <li>Item 1</li>
# <li>Item 2</li>
# </ul>

# 확장 기능 사용
html = markdown.markdown(md_text, extensions=['tables', 'fenced_code', 'toc'])
// JavaScript (marked 라이브러리)
import { marked } from 'marked';

const mdText = `
# Hello

This is **bold** text.
`;

const html = marked.parse(mdText);
console.log(html);

7. 형식 비교 및 선택 가이드

동일한 데이터 표현

JSON

{
  "app": {
    "name": "MyApp",
    "version": "1.0.0",
    "features": ["auth", "api"]
  },
  "database": {
    "host": "localhost",
    "port": 5432
  }
}

YAML

app:
  name: MyApp
  version: 1.0.0
  features:
    - auth
    - api

database:
  host: localhost
  port: 5432

XML

<?xml version="1.0"?>
<config>
  <app>
    <name>MyApp</name>
    <version>1.0.0</version>
    <features>
      <feature>auth</feature>
      <feature>api</feature>
    </features>
  </app>
  <database>
    <host>localhost</host>
    <port>5432</port>
  </database>
</config>

TOML

[app]
name = "MyApp"
version = "1.0.0"
features = ["auth", "api"]

[database]
host = "localhost"
port = 5432

INI

[app]
name = MyApp
version = 1.0.0
features = auth,api

[database]
host = localhost
port = 5432

비교표

특성 JSON YAML XML TOML INI Markdown
가독성 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
파싱 속도 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐
주석
타입 안정성 ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐ N/A
중첩 구조 제한적
파일 크기 작음 중간 중간 작음 중간
스키마 검증

사용 사례별 추천

flowchart TD
    Start[파일 형식 선택] --> Q1{용도는?}
    
    Q1 -->|API 응답| JSON[✅ JSON<br/>빠른 파싱]
    Q1 -->|설정 파일| Q2{복잡도는?}
    Q1 -->|문서| Markdown[✅ Markdown<br/>README, 블로그]
    Q1 -->|빌드 설정| Q3{언어는?}
    
    Q2 -->|간단| INI[✅ INI<br/>간단한 설정]
    Q2 -->|중간| TOML[✅ TOML<br/>Rust, Python]
    Q2 -->|복잡| YAML[✅ YAML<br/>Docker, K8s]
    
    Q3 -->|Java| XML[✅ XML<br/>Maven, Spring]
    Q3 -->|JavaScript| JSON[✅ JSON<br/>package.json]
    Q3 -->|Rust| TOML[✅ TOML<br/>Cargo.toml]
    Q3 -->|Python| TOML2[✅ TOML<br/>pyproject.toml]

프로젝트별 사용 현황

프로젝트 형식 파일명
Node.js JSON package.json
Python TOML pyproject.toml
Rust TOML Cargo.toml
Go Go go.mod
Docker YAML docker-compose.yml
Kubernetes YAML deployment.yaml
Ansible YAML playbook.yml
Maven XML pom.xml
Gradle Groovy build.gradle
Nginx Custom nginx.conf
Apache Custom httpd.conf

8. 실전 변환 및 검증

형식 간 변환

#!/usr/bin/env python3
"""
설정 파일 형식 변환 도구
"""
import json
import yaml
import tomli
import tomli_w
import xml.etree.ElementTree as ET
from pathlib import Path

class ConfigConverter:
    @staticmethod
    def json_to_yaml(json_file, yaml_file):
        """JSON → YAML"""
        with open(json_file, 'r') as f:
            data = json.load(f)
        
        with open(yaml_file, 'w') as f:
            yaml.dump(data, f, default_flow_style=False, allow_unicode=True)
        
        print(f"✅ Converted: {json_file} → {yaml_file}")
    
    @staticmethod
    def yaml_to_json(yaml_file, json_file):
        """YAML → JSON"""
        with open(yaml_file, 'r') as f:
            data = yaml.safe_load(f)
        
        with open(json_file, 'w') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        print(f"✅ Converted: {yaml_file} → {json_file}")
    
    @staticmethod
    def json_to_toml(json_file, toml_file):
        """JSON → TOML"""
        with open(json_file, 'r') as f:
            data = json.load(f)
        
        with open(toml_file, 'wb') as f:
            tomli_w.dump(data, f)
        
        print(f"✅ Converted: {json_file} → {toml_file}")
    
    @staticmethod
    def toml_to_json(toml_file, json_file):
        """TOML → JSON"""
        with open(toml_file, 'rb') as f:
            data = tomli.load(f)
        
        with open(json_file, 'w') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        print(f"✅ Converted: {toml_file} → {json_file}")

# 사용
converter = ConfigConverter()
converter.json_to_yaml('config.json', 'config.yaml')
converter.yaml_to_json('config.yaml', 'config.json')
converter.json_to_toml('config.json', 'config.toml')

검증 도구

# JSON 검증
jq . config.json
# 또는
python -m json.tool config.json

# YAML 검증
yamllint config.yaml

# YAML → JSON (yq)
yq eval -o=json config.yaml

# XML 검증
xmllint --noout config.xml

# TOML 검증 (Python)
python -c "import tomli; tomli.load(open('config.toml', 'rb'))"

온라인 도구

# jq (JSON 쿼리)
cat config.json | jq '.database.host'
# "localhost"

# 배열 필터링
cat config.json | jq '.servers[] | select(.role == "primary")'

# 변환
cat config.json | jq '.database'

# yq (YAML 쿼리)
cat config.yaml | yq '.database.host'

# xmllint (XML 쿼리)
xmllint --xpath '//database/host/text()' config.xml

기타 형식

Properties (Java)

# application.properties (Spring Boot)
server.port=8080
server.address=0.0.0.0

spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=user
spring.datasource.password=pass

# 배열
spring.profiles.active=dev,debug

# 멀티라인 (백슬래시)
app.description=This is a very long \
                description that spans \
                multiple lines.

HCL (HashiCorp Configuration Language)

# Terraform
variable "region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  
  tags = {
    Name = "WebServer"
    Environment = "production"
  }
}

output "instance_ip" {
  value = aws_instance.web.public_ip
}

Jsonnet (JSON 템플릿)

// config.jsonnet
local env = std.extVar('env');

local base = {
  name: 'MyApp',
  version: '1.0.0',
};

local envConfig = {
  dev: {
    apiUrl: 'http://localhost:3000',
    debug: true,
  },
  prod: {
    apiUrl: 'https://api.example.com',
    debug: false,
  },
};

base + envConfig[env]
# 실행
jsonnet -V env=dev config.jsonnet
# {
#   "name": "MyApp",
#   "version": "1.0.0",
#   "apiUrl": "http://localhost:3000",
#   "debug": true
# }

ENV 파일

# .env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
REDIS_URL=redis://localhost:6379
API_KEY=secret-key-123
DEBUG=false

# 배열 (비표준)
ALLOWED_HOSTS=localhost,example.com,*.example.com
# Python (python-dotenv)
from dotenv import load_dotenv
import os

load_dotenv()

port = int(os.getenv('PORT', 3000))
database_url = os.getenv('DATABASE_URL')
debug = os.getenv('DEBUG', 'false').lower() == 'true'

print(f"Port: {port}")
print(f"Database: {database_url}")
print(f"Debug: {debug}")

실전 팁

1. 주석 처리

// JSON은 주석 불가 → JSON5 사용
// package.json5
{
  // 프로젝트 정보
  name: "myapp",
  version: "1.0.0",
  
  // 의존성
  dependencies: {
    express: "^4.18.0",  // 후행 쉼표 가능
  },
}

// 또는 _comment 키 사용 (비표준)
{
  "_comment": "이것은 주석입니다",
  "name": "myapp"
}

2. 환경별 설정

# config.yaml
default: &default
  timeout: 30
  retries: 3

development:
  <<: *default
  apiUrl: http://localhost:3000
  debug: true

production:
  <<: *default
  apiUrl: https://api.example.com
  debug: false
# Python으로 환경별 로드
import yaml
import os

env = os.getenv('ENV', 'development')

with open('config.yaml') as f:
    config = yaml.safe_load(f)

app_config = config[env]
print(app_config)

3. 민감한 정보 관리

# config.yaml (민감한 정보 제외)
database:
  host: localhost
  port: 5432
  name: mydb
  # username과 password는 환경 변수로

# .env (버전 관리 제외)
DB_USERNAME=user
DB_PASSWORD=secret

# .gitignore
.env
*.secret.yaml
# Python으로 병합
import yaml
import os

with open('config.yaml') as f:
    config = yaml.safe_load(f)

# 환경 변수로 덮어쓰기
config['database']['username'] = os.getenv('DB_USERNAME')
config['database']['password'] = os.getenv('DB_PASSWORD')

4. 스키마 검증

# JSON Schema 검증
import json
import jsonschema

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0}
    },
    "required": ["name"]
}

data = {"name": "John", "age": 30}

try:
    jsonschema.validate(data, schema)
    print("✅ Valid")
except jsonschema.ValidationError as e:
    print(f"❌ Invalid: {e.message}")

실전 설정 파일 예시

Node.js 프로젝트

// package.json
{
  "name": "myapp",
  "version": "1.0.0",
  "description": "My awesome application",
  "main": "dist/index.js",
  "scripts": {
    "start": "node dist/index.js",
    "dev": "nodemon src/index.ts",
    "build": "tsc",
    "test": "jest",
    "lint": "eslint src/**/*.ts"
  },
  "dependencies": {
    "express": "^4.18.0",
    "dotenv": "^16.0.0"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "nodemon": "^3.0.0",
    "jest": "^29.0.0"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Python 프로젝트

# pyproject.toml
[project]
name = "myapp"
version = "1.0.0"
description = "My Python application"
readme = "README.md"
requires-python = ">=3.11"
license = {text = "MIT"}
authors = [
    {name = "John Doe", email = "[email protected]"}
]
dependencies = [
    "fastapi>=0.104.0",
    "uvicorn[standard]>=0.24.0",
    "sqlalchemy>=2.0.0",
    "pydantic>=2.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "black>=23.0.0",
    "mypy>=1.7.0",
    "ruff>=0.1.0",
]

[project.scripts]
myapp = "myapp.cli:main"

[tool.black]
line-length = 88
target-version = ['py311']
include = '\.pyi?$'

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]

[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true

[build-system]
requires = ["setuptools>=68.0.0", "wheel"]
build-backend = "setuptools.build_meta"

Rust 프로젝트

# Cargo.toml
[package]
name = "myapp"
version = "0.1.0"
edition = "2021"
authors = ["John Doe <[email protected]>"]
description = "My Rust application"
license = "MIT"
repository = "https://github.com/user/myapp"

[dependencies]
tokio = { version = "1.35", features = ["full"] }
axum = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-native-tls"] }

[dev-dependencies]
criterion = "0.5"

[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true

[[bin]]
name = "myapp"
path = "src/main.rs"

[[bench]]
name = "my_benchmark"
harness = false

Kubernetes 설정

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
        version: v1
    spec:
      containers:
      - name: myapp
        image: myapp:1.0.0
        ports:
        - containerPort: 8080
          protocol: TCP
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: url
        - name: REDIS_URL
          value: redis://redis:6379
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
      imagePullSecrets:
      - name: registry-secret

---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer

파싱 성능 비교

벤치마크

import json
import yaml
import tomli
import time

data = {
    'app': {
        'name': 'MyApp',
        'version': '1.0.0',
        'features': ['auth', 'api', 'admin'] * 100
    },
    'database': {
        'host': 'localhost',
        'port': 5432
    }
}

# JSON 직렬화/역직렬화
start = time.time()
for _ in range(1000):
    json_str = json.dumps(data)
    parsed = json.loads(json_str)
json_time = time.time() - start

# YAML 직렬화/역직렬화
start = time.time()
for _ in range(1000):
    yaml_str = yaml.dump(data)
    parsed = yaml.safe_load(yaml_str)
yaml_time = time.time() - start

print(f"JSON: {json_time:.3f}s")
print(f"YAML: {yaml_time:.3f}s")
print(f"YAML은 JSON보다 {yaml_time/json_time:.1f}배 느림")

# 일반적인 결과:
# JSON: 0.123s
# YAML: 1.456s
# YAML은 JSON보다 11.8배 느림

정리

형식 선택 플로우차트

flowchart TD
    Start[설정 파일 형식 선택] --> Q1{주석 필요?}
    
    Q1 -->|No| Q2{파싱 속도 중요?}
    Q1 -->|Yes| Q3{복잡도는?}
    
    Q2 -->|Yes| JSON[✅ JSON<br/>- API 응답<br/>- 빠른 파싱]
    Q2 -->|No| Q3
    
    Q3 -->|간단| INI[✅ INI<br/>- 간단한 설정<br/>- 레거시 호환]
    Q3 -->|중간| TOML[✅ TOML<br/>- Python/Rust<br/>- 명확한 문법]
    Q3 -->|복잡| YAML[✅ YAML<br/>- Docker/K8s<br/>- 가독성 최고]
    
    Start --> Q4{문서 작성?}
    Q4 -->|Yes| MD[✅ Markdown<br/>- README<br/>- 블로그]
    
    Start --> Q5{Java 프로젝트?}
    Q5 -->|Yes| XML[✅ XML<br/>- Maven<br/>- Spring]

핵심 원칙

# 1. API 응답 → JSON
@app.get("/api/users")
def get_users():
    return {"users": [...]}  # JSON

# 2. 설정 파일 → YAML 또는 TOML
# config.yaml
database:
  host: localhost
  port: 5432

# 3. 문서 → Markdown
# README.md
# My Project
This is a description.

# 4. 빌드 설정 → 언어별
# package.json (Node.js)
# Cargo.toml (Rust)
# pom.xml (Java)

# 5. 간단한 설정 → INI
# app.ini
[database]
host = localhost

장단점 요약

형식 최고 장점 최대 단점 추천 용도
JSON 빠른 파싱 주석 없음 API, 데이터 교환
YAML 가독성 느린 파싱 Docker, K8s, CI/CD
XML 스키마 검증 장황함 Java, SOAP, RSS
TOML 명확한 문법 제한적 지원 Rust, Python 설정
INI 단순함 표준 없음 간단한 설정
Markdown 읽기 쉬움 데이터 구조 표현 제한 문서, README

베스트 프랙티스

# ✅ 권장 사항

# 1. 일관된 들여쓰기 (YAML)
app:
  name: MyApp  # 2칸
  version: 1.0.0

# 2. 주석 활용 (YAML, TOML)
# 데이터베이스 설정
[database]
host = "localhost"  # 개발 환경에서는 localhost

# 3. 환경 변수 분리
# config.yaml (버전 관리)
database:
  host: ${DB_HOST}
  port: ${DB_PORT}

# .env (버전 관리 제외)
DB_HOST=localhost
DB_PORT=5432

# 4. 스키마 검증
# JSON Schema, YAML Schema 사용

# 5. 민감한 정보 암호화
# SOPS, Vault 등 사용

변환 치트시트

명령줄 도구

# JSON → YAML
cat config.json | yq -P > config.yaml

# YAML → JSON
cat config.yaml | yq -o=json > config.json

# JSON 포맷팅
cat config.json | jq . > formatted.json

# YAML 검증
yamllint config.yaml

# XML 포맷팅
xmllint --format config.xml

# JSON 병합
jq -s '.[0] * .[1]' base.json override.json > merged.json

# YAML 병합
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' base.yaml override.yaml

프로그래밍 언어별

# Python: 모든 형식 지원
import json        # 내장
import yaml        # pip install pyyaml
import tomli       # pip install tomli
import configparser  # 내장 (INI)
import xml.etree.ElementTree as ET  # 내장

# JavaScript/Node.js
const json = require('./config.json');  // 네이티브
const yaml = require('js-yaml');
const toml = require('toml');
const ini = require('ini');

# Go
import (
    "encoding/json"
    "gopkg.in/yaml.v3"
    "github.com/BurntSushi/toml"
    "gopkg.in/ini.v1"
)

# Rust
use serde_json;  // JSON
use serde_yaml;  // YAML
use toml;        // TOML

실전 시나리오

시나리오 1: 마이크로서비스 설정

# config/base.yaml
app:
  name: MyService
  version: 1.0.0
  
logging:
  level: info
  format: json

# config/dev.yaml
app:
  debug: true

database:
  host: localhost
  port: 5432

# config/prod.yaml
app:
  debug: false

database:
  host: db.example.com
  port: 5432
  ssl: true
  pool_size: 20
# Python으로 병합
import yaml
from pathlib import Path

def load_config(env='dev'):
    """환경별 설정 로드"""
    base = yaml.safe_load(Path('config/base.yaml').read_text())
    env_config = yaml.safe_load(Path(f'config/{env}.yaml').read_text())
    
    # 딥 머지
    def deep_merge(base, override):
        for key, value in override.items():
            if key in base and isinstance(base[key], dict) and isinstance(value, dict):
                deep_merge(base[key], value)
            else:
                base[key] = value
        return base
    
    return deep_merge(base, env_config)

config = load_config('prod')
print(config)

시나리오 2: CI/CD 파이프라인

# .github/workflows/ci.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '18'
  REGISTRY: ghcr.io

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
    
    - name: Upload coverage
      uses: codecov/codecov-action@v3
      with:
        files: ./coverage/lcov.info
  
  build:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Build Docker image
      run: |
        docker build -t ${{ env.REGISTRY }}/myapp:${{ github.sha }} .
    
    - name: Push to registry
      if: github.ref == 'refs/heads/main'
      run: |
        echo ${{ secrets.GITHUB_TOKEN }} | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
        docker push ${{ env.REGISTRY }}/myapp:${{ github.sha }}
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Deploy to production
      run: |
        kubectl set image deployment/myapp myapp=${{ env.REGISTRY }}/myapp:${{ github.sha }}

에디터 설정

VS Code

// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.tabSize": 2,
  "files.associations": {
    "*.yaml": "yaml",
    "*.yml": "yaml",
    "Dockerfile*": "dockerfile"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.tabSize": 2
  },
  "[yaml]": {
    "editor.defaultFormatter": "redhat.vscode-yaml",
    "editor.insertSpaces": true,
    "editor.tabSize": 2,
    "editor.autoIndent": "advanced"
  },
  "[markdown]": {
    "editor.wordWrap": "on",
    "editor.quickSuggestions": false
  },
  "yaml.schemas": {
    "https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml"
  }
}

EditorConfig

# .editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{js,jsx,ts,tsx,json}]
indent_style = space
indent_size = 2

[*.{yaml,yml}]
indent_style = space
indent_size = 2

[*.py]
indent_style = space
indent_size = 4

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab

정리

형식별 추천 사용처

flowchart TB
    subgraph API[API & 데이터 교환]
        JSON_API[JSON<br/>REST API 응답]
        XML_API[XML<br/>SOAP, RSS]
    end
    
    subgraph Config[설정 파일]
        YAML_Config[YAML<br/>Docker, K8s, Ansible]
        TOML_Config[TOML<br/>Rust, Python]
        INI_Config[INI<br/>간단한 설정]
    end
    
    subgraph Build[빌드 설정]
        JSON_Build[JSON<br/>package.json, tsconfig.json]
        XML_Build[XML<br/>pom.xml, build.gradle]
        TOML_Build[TOML<br/>Cargo.toml, pyproject.toml]
    end
    
    subgraph Doc[문서]
        MD[Markdown<br/>README, 블로그, Wiki]
    end

선택 가이드 요약

✅ JSON 사용:
- REST API 응답
- 웹 애플리케이션 데이터
- JavaScript 프로젝트
- 파싱 속도가 중요한 경우

✅ YAML 사용:
- Docker Compose
- Kubernetes 매니페스트
- CI/CD 파이프라인 (GitHub Actions, GitLab CI)
- Ansible Playbook
- 복잡한 설정 파일

✅ XML 사용:
- Maven (pom.xml)
- Spring 설정
- Android 매니페스트
- SOAP 웹 서비스
- RSS/Atom 피드

✅ TOML 사용:
- Rust 프로젝트 (Cargo.toml)
- Python 프로젝트 (pyproject.toml)
- 명확한 설정 파일

✅ INI 사용:
- 간단한 애플리케이션 설정
- 레거시 시스템
- PHP 설정 (php.ini)

✅ Markdown 사용:
- README 파일
- 문서화
- 블로그 포스트
- GitHub Wiki

체크리스트

JSON

  • 주석 필요 없음 확인
  • 파싱 속도가 중요함
  • JavaScript와 호환 필요
  • 스키마 검증 고려

YAML

  • 들여쓰기 일관성 유지 (공백만)
  • 탭 사용 금지
  • 따옴표 규칙 이해
  • yamllint로 검증

XML

  • 스키마 정의 (XSD)
  • 네임스페이스 사용 고려
  • CDATA 섹션 활용
  • xmllint로 검증

TOML

  • 섹션 구조 설계
  • 날짜/시간 형식 활용
  • 배열 테이블 이해

Markdown

  • 일관된 스타일 가이드
  • 링크 검증
  • 코드 블록 언어 지정
  • 목차 자동 생성 고려

참고 자료

한 줄 요약: API는 JSON, 설정 파일은 YAML 또는 TOML, 문서는 Markdown, Java 빌드는 XML을 사용하되, 각 형식의 장단점을 이해하고 프로젝트 요구사항에 맞게 선택하세요.

---
... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3