본문으로 건너뛰기
Previous
Next
설정 파일 형식 완벽 가이드 | JSON·YAML·XML·TOML·INI 비교

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

JSON 파싱 내부 메커니즘

JSON 파서가 동작하는 원리:

JSON.parse(text) 내부 동작:

1. 토큰화 (Tokenization):
   
   입력 텍스트를 토큰으로 분리:
   
   {"name":"John","age":30}
   
   → 토큰 스트림:
   [ {, "name", :, "John", ,, "age", :, 30, } ]
   
   토큰 타입:
   - LEFT_BRACE: {
   - RIGHT_BRACE: }
   - LEFT_BRACKET: [
   - RIGHT_BRACKET: ]
   - STRING: "..."
   - NUMBER: 123, 3.14
   - TRUE/FALSE/NULL
   - COLON: :
   - COMMA: ,

2. 재귀 하강 파싱 (Recursive Descent Parsing):
   
   문법 규칙:
   value → object | array | string | number | true | false | null
   object → { members }
   members → pair | pair , members
   pair → string : value
   array → [ elements ]
   elements → value | value , elements
   
   파싱 과정:
   
   parseValue():
     if token == '{':
       return parseObject()
     else if token == '[':
       return parseArray()
     else if token == STRING:
       return parseString()
     else if token == NUMBER:
       return parseNumber()
   
   parseObject():
     result = {}
     expect('{')
     while token != '}':
       key = parseString()
       expect(':')
       value = parseValue()  ← 재귀!
       result[key] = value
       if token == ',': advance()
     expect('}')
     return result

3. 값 변환:
   
   "John" → JavaScript String
   30 → JavaScript Number
   true → JavaScript Boolean
   null → JavaScript null
   
   특수 처리:
   - "123" → String (따옴표 있음)
   - 123 → Number (따옴표 없음)
   - "\n" → 개행 문자 (이스케이프)
   - "\u0041" → "A" (유니코드)

4. 에러 검증:
   
   구문 오류 감지:
   - 따옴표 미매칭: {"name": "John}
   - 후행 쉼표: {"name": "John",}
   - 주석: {"name": "John"} // comment
   - 키 미인용: {name: "John"}
   
   → SyntaxError 즉시 발생

JSON 파싱 성능이 빠른 이유:

JSON 문법의 단순성:

1. LL(1) 파서로 구현 가능:
   - 한 토큰만 미리 보면 (lookahead) 결정 가능
   - 백트래킹 불필요
   - 선형 시간 O(n)

2. 타입이 명확:
   - "..."는 무조건 문자열
   - 123은 무조건 숫자
   - true/false/null은 키워드
   
   YAML은 타입 추론 필요:
   - yes → Boolean true
   - "yes" → String "yes"
   - 123 → Number
   - "123" → String
   → 복잡한 파싱 로직

3. 메모리 할당 최소화:
   - JavaScript 엔진의 네이티브 객체로 직접 변환
   - V8 엔진에서 JSON.parse는 최적화된 C++ 코드
   
   벤치마크 (1MB JSON 파일):
   - JSON.parse: ~10ms
   - YAML.parse: ~100ms (10배 느림)
   - XML: ~50ms

JSON5와 JSONC (확장):

// JSON5 (주석 허용, 후행 쉼표 허용)
{
  // 주석 가능
  name: 'John',  // 키 인용 생략 가능
  age: 30,       // 후행 쉼표 OK
}

// JSONC (JSON with Comments - VS Code 설정)
{
  "editor.fontSize": 14,  // 폰트 크기
  "editor.tabSize": 2     // 탭 크기
}

// 표준 JSON은 불가능:
{
  "name": "John",  // ❌ 주석 불가
  "age": 30,       // ❌ 후행 쉼표 불가
}

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은 문맥 의존적 (Context-Sensitive) 문법:

1. 들여쓰기 레벨 추적:
   
   name: value
     nested: value2
       deep: value3
   
   → 파서가 현재 들여쓰기 레벨을 스택으로 관리
   → 들여쓰기 감소 시 스택 pop
   → 들여쓰기 증가 시 스택 push
   
   들여쓰기 오류:
   items:
     - name: item1
      - name: item2  ← 1칸 부족 → 에러!

2. 타입 추론 (Implicit Typing):
   
   YAML은 따옴표 없이 값의 타입 자동 추론:
   
   age: 30           → Number
   age: "30"         → String
   enabled: true     → Boolean true
   enabled: yes      → Boolean true (YAML 키워드)
   enabled: "yes"    → String "yes"
   version: 1.0      → Number 1.0
   version: "1.0"    → String "1.0"
   date: 2026-04-01  → Date 객체
   null_value: null  → null
   null_value: ~     → null (YAML 축약)
   
   → 파서가 모든 값에 대해 타입 판단 필요
   → JSON은 명시적이라 불필요

3. 복잡한 구조 표현:
   
   블록 스타일 (들여쓰기):
   items:
     - name: item1
       value: 100
     - name: item2
       value: 200
   
   플로우 스타일 (인라인):
   items: [{name: item1, value: 100}, {name: item2, value: 200}]
   
   앵커와 별칭 (재사용):
   defaults: &defaults
     timeout: 30
     retries: 3
   
   prod:
     <<: *defaults      ← defaults 내용 병합
     url: https://prod
   
   → 파서가 앵커를 추적하고 참조 시 삽입

4. 멀티라인 문자열:
   
   리터럴 (개행 유지):
   script: |
     line 1
     line 2
     line 3
   → "line 1\nline 2\nline 3"
   
   폴딩 (개행 → 공백):
   description: >
     This is a long
     description text.
   → "This is a long description text."
   
   → 특수 처리 로직 필요

5. 파싱 단계:
   
   text → Tokens → Events → Representation Graph → Native Objects
   
   Events:
   - STREAM_START
   - DOCUMENT_START
   - MAPPING_START (객체 시작)
   - SCALAR (값)
   - MAPPING_END
   - DOCUMENT_END
   - STREAM_END
   
   → JSON은 Tokens → Objects (단순)

YAML 파싱이 느린 이유:

JSON vs YAML 파싱 복잡도:

JSON:
- LL(1) 파서: 1개 토큰만 미리 봄
- 타입 명시적: "string", 123, true
- 단순 재귀 하강
- 시간 복잡도: O(n)

YAML:
- 문맥 의존: 들여쓰기 레벨 추적
- 타입 추론: yes → Boolean, "yes" → String
- 앵커/별칭 해석
- 멀티라인 처리
- 시간 복잡도: O(n) 이상 (복잡한 상수)

벤치마크 (동일한 데이터):
- JSON.parse (V8): 1x (기준)
- yaml.load (js-yaml): 10-20x
- yaml.safe_load (PyYAML): 50-100x

YAML 보안 이슈:

# ❌ 위험: yaml.load() (Python, 임의 코드 실행)
!!python/object/apply:os.system
args: ['rm -rf /']

# ✅ 안전: yaml.safe_load() (기본 타입만)
name: value
age: 30

# Node.js js-yaml:
yaml.load(text)      // ❌ 위험 (커스텀 태그 허용)
yaml.safeLoad(text)  // ✅ 안전 (기본 타입만)

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

비교표

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

사용 사례별 추천

flowchart TD
    Start[파일 형식 선택] --> Q1{용도는?}
    
    Q1 -->|API 응답| JSON["✅ JSON\n빠른 파싱"]
    Q1 -->|설정 파일| Q2{복잡도는?}
    Q1 -->|문서| Markdown["✅ Markdown\nREADME, 블로그"]
    Q1 -->|빌드 설정| Q3{언어는?}
    
    Q2 -->|간단| INI["✅ INI\n간단한 설정"]
    Q2 -->|중간| TOML["✅ TOML\nRust, Python"]
    Q2 -->|복잡| YAML["✅ YAML\nDocker, K8s"]
    
    Q3 -->|Java| XML["✅ XML\nMaven, Spring"]
    Q3 -->|JavaScript| JSON["✅ JSON\npackage.json"]
    Q3 -->|Rust| TOML["✅ TOML\nCargo.toml"]
    Q3 -->|Python| TOML2["✅ TOML\npyproject.toml"]

프로젝트별 사용 현황

프로젝트형식파일명
Node.jsJSONpackage.json
PythonTOMLpyproject.toml
RustTOMLCargo.toml
GoGogo.mod
DockerYAMLdocker-compose.yml
KubernetesYAMLdeployment.yaml
AnsibleYAMLplaybook.yml
MavenXMLpom.xml
GradleGroovybuild.gradle
NginxCustomnginx.conf
ApacheCustomhttpd.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배 느림

내부 동작과 핵심 메커니즘

이 글의 주제는 「설정 파일 형식 완벽 가이드 | JSON·YAML·XML·TOML·INI 비교」입니다. 앞선 튜토리얼을 구현·런타임 관점에서 다시 압축합니다. 구성 요소 간 책임 분리와 관측 가능한 지점을 기준으로 “입력이 어디서 검증되고, 핵심 연산이 어디서 일어나며, 부작용(I/O·네트워크·디스크)·동시성이 어디서 터지는가”를 한 장면으로 그리면 장애 분석이 빨라집니다.

처리 파이프라인(개념도)

flowchart TD
  A[입력·요청·이벤트] --> B[파싱·검증·디코딩]
  B --> C[핵심 연산·상태 전이]
  C --> D[부작용: I/O·네트워크·동시성]
  D --> E[결과·관측·저장]

경계에서의 지연·실패(시퀀스 관점)

sequenceDiagram
  participant C as 클라이언트/호출자
  participant B as 경계(프로세스·런타임·게이트웨이)
  participant D as 의존성(외부 API·DB·큐)
  C->>B: 요청/이벤트
  B->>D: 조회·쓰기·RPC
  D-->>B: 지연·부분 실패·재시도 가능
  B-->>C: 응답 또는 오류(코드·상관 ID)

알고리즘·프로토콜·리소스 관점 체크포인트

  • 불변 조건(Invariant): 각 단계가 만족해야 하는 조건(버퍼 경계, 프로토콜 상태, 트랜잭션 격리, 파일 디스크립터 상한)을 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
  • 결정성: 동일 입력에 동일 출력이 보장되는 순수 층과, 시간·네트워크·스레드 스케줄에 의해 달라질 수 있는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
  • 경계 비용: 직렬화/역직렬화, 문자 인코딩, syscall 횟수, 락 경합, GC·할당, 캐시 미스처럼 누적 비용을 의심 목록에 넣습니다.
  • 백프레셔: 생산자가 소비자보다 빠를 때(소켓 버퍼, 큐 깊이, 스트림) 어디서 어떤 신호로 속도를 줄일지 정의합니다.

프로덕션 운영 패턴

실서비스에서는 기능과 함께 관측·배포·보안·비용·규제가 동시에 요구됩니다.

영역운영 관점 질문
관측성요청 단위 상관 ID, 에러율/지연 분위수(p95/p99), 의존성 타임아웃·재시도가 대시보드에 보이는가
안전성입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가
신뢰성재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가
성능캐시 계층·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가
배포롤백 룬북, 카나리/블루그린, 마이그레이션 호환성·플래그가 문서화되어 있는가
용량피크 트래픽·디스크·파일 디스크립터·스레드 풀 상한을 주기적으로 검증하는가

스테이징은 데이터 양·네트워크 RTT·동시성을 가능한 한 프로덕션에 가깝게 맞추는 것이 재현율을 높입니다.


확장 예시: 엔드투엔드 미니 시나리오

「설정 파일 형식 완벽 가이드 | JSON·YAML·XML·TOML·INI 비교」을 실제 배포·운영 흐름으로 옮긴 체크리스트형 시나리오입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.

  1. 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드 표를 API 또는 이벤트 경계에 둔다.
  2. 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 한 화면(로그+메트릭+트레이스)에서 추적한다.
  3. 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
  4. 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지(또는 피처 플래그) 확인한다.
  5. 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값이 기대 범위인지 본다.

의사코드 스케치(프레임워크 무관)

handle(request):
  ctx = newCorrelationId()
  validated = validateSchema(request)        // 경계에서 거절
  authorize(validated, ctx)                  // 권한·테넌트
  result = domainCore(validated)             // 순수에 가까운 규칙
  persistOrEmit(result, idempotentKey)       // I/O: 멱등·재시도 정책
  recordMetrics(ctx, latency, outcome)
  return result

문제 해결(Troubleshooting)

증상가능 원인조치
간헐적 실패레이스, 타임아웃, 외부 의존성 불안정, DNS최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검
성능 저하N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거
메모리 증가캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납상한·TTL·힙/FD 스냅샷 비교
빌드·배포만 실패환경 변수, 권한, 플랫폼 차이, lockfileCI 로그와 로컬 diff, 런타임·이미지 버전 핀
설정이 로컬과 다름프로필·시크릿·기본값, 지역 리전단일 소스(예: 스키마 검증된 설정)와 배포 매트릭스 표준화
데이터 불일치비멱등 재시도, 부분 쓰기, 캐시 무효화 누락멱등 키·아웃박스·트랜잭션 경계 재검토

권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.

정리

형식 선택 플로우차트

flowchart TD
    Start[설정 파일 형식 선택] --> Q1{주석 필요?}
    
    Q1 -->|No| Q2{파싱 속도 중요?}
    Q1 -->|Yes| Q3{복잡도는?}
    
    Q2 -->|Yes| JSON["✅ JSON\n- API 응답\n- 빠른 파싱"]
    Q2 -->|No| Q3
    
    Q3 -->|간단| INI["✅ INI\n- 간단한 설정\n- 레거시 호환"]
    Q3 -->|중간| TOML["✅ TOML\n- Python/Rust\n- 명확한 문법"]
    Q3 -->|복잡| YAML["✅ YAML\n- Docker/K8s\n- 가독성 최고"]
    
    Start --> Q4{문서 작성?}
    Q4 -->|Yes| MD["✅ Markdown\n- README\n- 블로그"]
    
    Start --> Q5{Java 프로젝트?}
    Q5 -->|Yes| XML["✅ XML\n- Maven\n- 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\nREST API 응답"]
        XML_API["XML\nSOAP, RSS"]
    end
    
    subgraph Config[설정 파일]
        YAML_Config["YAML\nDocker, K8s, Ansible"]
        TOML_Config["TOML\nRust, Python"]
        INI_Config["INI\n간단한 설정"]
    end
    
    subgraph Build[빌드 설정]
        JSON_Build["JSON\npackage.json, tsconfig.json"]
        XML_Build["XML\npom.xml, build.gradle"]
        TOML_Build["TOML\nCargo.toml, pyproject.toml"]
    end
    
    subgraph Doc[문서]
        MD["Markdown\nREADME, 블로그, 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

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


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. JSON, YAML, XML, TOML, INI, Markdown 등 프로그래밍에서 자주 사용되는 파일 형식의 특징과 차이점. 각 형식의 장단점, 사용 사례, 파싱 방법, 실전 예제로 완벽 이해. Start now. 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.

참고 자료


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

JSON, YAML, XML, TOML, INI, Markdown, 설정파일, config 등으로 검색하시면 이 글이 도움이 됩니다.