설정 파일 형식 완벽 가이드 | 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 (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)

---
수평선
| 컬럼 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
- 일관된 스타일 가이드
- 링크 검증
- 코드 블록 언어 지정
- 목차 자동 생성 고려
참고 자료
- JSON Specification
- YAML Specification
- XML Specification
- TOML Specification
- CommonMark (Markdown)
- JSON Schema
- YAML Lint
한 줄 요약: API는 JSON, 설정 파일은 YAML 또는 TOML, 문서는 Markdown, Java 빌드는 XML을 사용하되, 각 형식의 장단점을 이해하고 프로젝트 요구사항에 맞게 선택하세요.