서론: 데이터 직렬화, 왜 중요한가?
현대 소프트웨어 개발에서 데이터 직렬화(Serialization)는 필수 불가결한 요소입니다. 서버와 클라이언트 간 통신, 설정 파일 관리, CI/CD 파이프라인 구축 등 모든 곳에서 데이터를 저장하고 전송할 수 있는 텍스트 형식이 필요합니다.
이때 가장 널리 사용되는 두 가지 포맷이 바로 JSON(JavaScript Object Notation)과 YAML(YAML Ain’t Markup Language)입니다. 두 포맷 모두 사람이 읽을 수 있고(human-readable), 프로그래밍 언어 간 호환성이 뛰어나지만, 설계 철학과 사용 목적에서 뚜렷한 차이가 있습니다.
이 글에서는 JSON과 YAML의 역사, 문법적 특징, 성능 차이를 비교하고, 각 포맷이 가장 빛을 발하는 실제 사용 사례를 안내합니다. 또한 두 포맷 간 변환 시 주의해야 할 함정도 함께 다룹니다.
JSON이란 무엇인가?
JSON의 탄생
JSON(JavaScript Object Notation)은 2001년 더글라스 크록포드(Douglas Crockford)가 처음 제안한 경량 데이터 교환 포맷입니다. 원래 JavaScript의 객체 표기법에서 파생되었지만, 현재는 거의 모든 프로그래밍 언어에서 지원하는 사실상의 표준(de facto standard)이 되었습니다.
2013년 ECMA-404와 RFC 7159로 공식 표준화되었으며, 2017년 RFC 8259로 최신 사양이 갱신되었습니다.
JSON 문법 특징
JSON은 매우 단순하고 엄격한 문법을 따릅니다:
기본 데이터 타입
- 객체(Object):
{ "key": "value" }– 중괄호로 감싸고, 키는 반드시 큰따옴표 - 배열(Array):
["item1", "item2"]– 대괄호로 감싸고, 순서가 있는 리스트 - 문자열(String):
"text"– 반드시 큰따옴표 사용 (작은따옴표 불가) - 숫자(Number):
42,3.14– 정수와 부동소수점 - 불리언(Boolean):
true,false - null:
null
JSON 예시
{
"name": "홍길동",
"age": 30,
"isActive": true,
"tags": ["developer", "backend", "nodejs"],
"address": {
"city": "서울",
"zipcode": "06234"
},
"salary": null
}
JSON의 장점
- 간결성: 최소한의 문법으로 데이터 표현
- 파싱 속도: 단순한 구조로 인해 파싱 속도가 매우 빠름
- 보안성: 엄격한 문법으로 인한 낮은 인젝션 공격 위험
- 범용성: 모든 언어에서 네이티브 또는 라이브러리로 지원
- 표준화: 명확한 스펙(RFC 8259)으로 구현 간 차이 최소화
- 압축 효율: 단순한 구조로 gzip 압축 시 효율적
JSON의 단점
- 주석 불가: 공식적으로 주석을 지원하지 않음 (일부 파서는 비표준 확장 지원)
- 가독성: 중괄호와 큰따옴표가 많아 복잡한 구조에서 읽기 어려움
- 중복: 키를 매번 반복해야 해서 파일 크기 증가
- 데이터 타입 제한: 날짜, 정규식, 함수 등 고급 타입 미지원
- 후행 쉼표 불가:
{"a": 1,}같은 형식 허용 안 됨 (엄격함)
YAML이란 무엇인가?
YAML의 탄생
YAML(YAML Ain’t Markup Language)은 2001년 클락 에반스(Clark Evans)가 개발한 사람 친화적 데이터 직렬화 포맷입니다. 원래 “Yet Another Markup Language”의 약자였으나, 마크업 언어가 아닌 데이터 직렬화에 집중한다는 의미로 재귀적 약자로 변경되었습니다.
2009년 YAML 1.2 버전이 발표되었고, JSON의 상위 집합(superset)으로 설계되어 모든 JSON 문서는 유효한 YAML로 파싱됩니다.
YAML 문법 특징
YAML은 가독성을 최우선으로 설계되었습니다:
핵심 문법 규칙
- 들여쓰기: 공백(스페이스) 2개 또는 4개로 계층 구조 표현 (탭 사용 금지)
- 키-값 구분:
key: value– 콜론 뒤 공백 필수 - 리스트:
- item– 하이픈과 공백으로 시작 - 따옴표 선택적: 문자열에 특수문자 없으면 따옴표 생략 가능
- 주석:
# 주석 내용– 해시로 시작 - 멀티라인 문자열:
|(개행 유지),>(개행 제거)
YAML 예시
name: 홍길동
age: 30
isActive: true
tags:
- developer
- backend
- nodejs
address:
city: 서울
zipcode: "06234" # 숫자처럼 보이는 문자열은 따옴표 권장
salary: null
# 멀티라인 문자열 예시
description: |
이것은 여러 줄로 된
설명입니다.
개행이 유지됩니다.
YAML의 장점
- 뛰어난 가독성: 들여쓰기만으로 계층 표현, 중괄호/괄호 최소화
- 주석 지원: 설정 파일에서 설명 추가 가능
- 멀티라인 문자열: 긴 텍스트를 자연스럽게 표현
- 참조/앵커:
&anchor와*alias로 중복 제거 - 고급 타입: 날짜, 타임스탬프, 정규식 등 확장 타입 지원
- 설정 파일 최적화: 사람이 직접 편집하기 편함
YAML의 단점
- 파싱 복잡도: 풍부한 기능으로 인해 파서 구현이 복잡하고 느림
- 들여쓰기 민감: 공백 하나 차이로 구조가 완전히 바뀜 (에러 원인)
- 보안 위험: 일부 파서는 임의 코드 실행 가능 (Python PyYAML의
yaml.load()) - 파일 크기: 들여쓰기와 주석으로 인해 JSON보다 파일 크기 증가
- 스펙 복잡성: YAML 1.2 스펙이 방대하여 파서 간 미묘한 차이 존재
- 디버깅 어려움: 구문 오류 위치 찾기 어려움
JSON vs YAML: 핵심 차이점 비교
| 특성 | JSON | YAML |
|---|---|---|
| 가독성 | 중간 (중괄호/따옴표 많음) | 높음 (들여쓰기 기반) |
| 주석 | ❌ 지원 안 함 | ✅ #으로 지원 |
| 파싱 속도 | 빠름 (단순 문법) | 느림 (복잡한 문법) |
| 파일 크기 | 작음 (압축 효율 높음) | 큼 (들여쓰기/주석) |
| 엄격성 | 매우 엄격 (보안 유리) | 유연함 (보안 주의) |
| 멀티라인 | ❌ \n 이스케이프 필요 |
✅ |, > 지원 |
| 데이터 타입 | 기본 6개 (제한적) | 확장 타입 다수 |
| 중복 제거 | ❌ 불가능 | ✅ 앵커/별칭 지원 |
| API 응답 | ✅ 사실상 표준 | ❌ 거의 사용 안 함 |
| 설정 파일 | △ 가능하지만 불편 | ✅ 최적화됨 |
| JSON 호환성 | N/A | ✅ JSON 상위집합 |
| 에러 디버깅 | 쉬움 (명확한 에러) | 어려움 (들여쓰기 추적) |
성능 벤치마크 (참고 데이터)
동일한 데이터를 JSON과 YAML로 저장/로드할 때의 성능 차이:
| 작업 | JSON (Node.js) | YAML (js-yaml) | 배율 |
|---|---|---|---|
| 파싱 속도 (10KB 파일) | 0.5ms | 3.2ms | 6.4배 느림 |
| 직렬화 속도 | 0.3ms | 2.1ms | 7배 느림 |
| 파일 크기 (압축 전) | 10KB | 12.5KB | 25% 큼 |
| 파일 크기 (gzip 후) | 2.1KB | 2.4KB | 14% 큼 |
※ 실제 성능은 파서 구현, 데이터 구조, 환경에 따라 달라질 수 있습니다.
사용 사례별 선택 가이드
JSON을 선택해야 하는 경우
1. REST API 응답/요청
웹 API에서는 JSON이 사실상 표준입니다.
- 이유: 빠른 파싱, 작은 파일 크기, 브라우저 네이티브 지원
- 예시:
fetch('/api/users')→ JSON 응답
// API 응답 (JSON)
{
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
}
2. NoSQL 데이터베이스 (MongoDB, CouchDB)
문서 기반 DB는 JSON 형식으로 데이터를 저장합니다.
- 이유: 네이티브 JSON 파싱, 빠른 쿼리 성능
- 예시: MongoDB 문서 = BSON (Binary JSON)
3. 실시간 데이터 스트림 (WebSocket, SSE)
실시간 통신에서는 파싱 속도가 중요합니다.
- 이유: 빠른 직렬화/역직렬화, 네트워크 대역폭 절약
- 예시: 주식 시세, 채팅 메시지
4. 로그 파일 (JSON Lines)
구조화된 로그를 한 줄씩 저장할 때 (JSONL 포맷)
- 이유: 각 줄이 독립적으로 파싱 가능, 스트리밍 처리 용이
- 예시:
{"level":"error","msg":"failed","time":"2025-02-02T10:00:00Z"}
5. 브라우저 localStorage/sessionStorage
클라이언트 측 데이터 저장
- 이유:
JSON.stringify(),JSON.parse()네이티브 지원
YAML을 선택해야 하는 경우
1. CI/CD 파이프라인 설정 (GitHub Actions, GitLab CI)
복잡한 빌드 워크플로우를 정의할 때
- 이유: 주석으로 단계별 설명 추가, 멀티라인 스크립트 작성 편리
- 예시:
.github/workflows/ci.yml
# GitHub Actions 워크플로우
name: CI
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: |
npm install
npm test # 테스트 실행
2. DevOps 설정 파일 (Docker Compose, Kubernetes)
인프라 구성을 코드로 관리할 때
- 이유: 가독성, 주석, 앵커로 중복 제거
- 예시:
docker-compose.yml,deployment.yaml
# Docker Compose 예시
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80" # 호스트:컨테이너
volumes:
- ./html:/usr/share/nginx/html
3. 애플리케이션 설정 파일
프레임워크/라이브러리 설정 (Rails, Symfony)
- 이유: 개발자가 직접 편집, 환경별 주석 관리
- 예시:
database.yml,config.yaml
4. OpenAPI/Swagger 스펙
API 문서를 코드로 작성할 때
- 이유: 복잡한 API 구조를 읽기 쉽게 표현
- 예시:
openapi.yaml
5. Ansible Playbook
인프라 자동화 스크립트
- 이유: 멀티라인 셸 명령, 변수 참조
혼합 사용 패턴
실무에서는 두 포맷을 상황에 맞게 혼용합니다:
- 백엔드 API: JSON으로 통신
- 설정 파일: YAML로 관리
- 빌드 도구: package.json (JSON), .gitlab-ci.yml (YAML) 병행
JSON ↔ YAML 변환 시 주의사항
1. 데이터 타입 변환 문제
문자열 타입 추론
YAML은 따옴표 없는 값을 자동으로 타입 변환합니다.
# YAML
zipcode: 06234 # 숫자로 파싱될 수 있음
version: 1.0 # 숫자 1.0으로 파싱
# JSON으로 변환 시
{
"zipcode": 6234, // ❌ 앞자리 0 손실!
"version": 1 // ❌ .0 손실!
}
해결책: YAML에서 따옴표로 명시
zipcode: "06234"
version: "1.0"
불리언 값 혼동
YAML은 다양한 불리언 표현을 허용합니다.
# YAML에서 true로 인식되는 값들
enabled: yes
active: on
debug: true
# JSON으로 변환 시
{
"enabled": true,
"active": true,
"debug": true
}
주의: yes, no, on, off는 YAML에서만 불리언으로 인식되므로, 문자열로 사용하려면 따옴표 필요
2. 주석 손실
YAML → JSON 변환 시 모든 주석이 사라집니다.
# YAML (주석 있음)
database:
host: localhost # 로컬 개발 환경
port: 5432 # PostgreSQL 기본 포트
// JSON (주석 없음)
{
"database": {
"host": "localhost",
"port": 5432
}
}
해결책: 중요한 설명은 별도 필드로 보존
database:
host: localhost
port: 5432
_comment: "로컬 개발 환경, PostgreSQL 기본 포트"
3. 멀티라인 문자열 변환
YAML의 멀티라인 문자열은 JSON에서 이스케이프 시퀀스로 변환됩니다.
# YAML (읽기 쉬움)
description: |
이것은
여러 줄로 된
설명입니다.
// JSON (이스케이프)
{
"description": "이것은\n여러 줄로 된\n설명입니다."
}
4. 앵커/별칭 제거
YAML의 앵커(&)와 별칭(*)은 JSON에서 중복으로 확장됩니다.
# YAML (중복 제거)
defaults: &defaults
timeout: 30
retries: 3
production:
<<: *defaults
host: prod.example.com
// JSON (중복 확장)
{
"defaults": {
"timeout": 30,
"retries": 3
},
"production": {
"timeout": 30,
"retries": 3,
"host": "prod.example.com"
}
}
5. 순서 보존 문제
JSON 객체의 키 순서는 보장되지 않지만, YAML은 순서를 유지합니다. 일부 파서는 순서를 무시할 수 있으므로 순서에 의존하는 로직은 피해야 합니다.
변환 검증 체크리스트
- ✅ 숫자처럼 보이는 문자열에 따옴표 추가했는지 확인
- ✅ 불리언 값이 의도대로 변환되었는지 확인
- ✅ 주석이 손실될 수 있음을 인지
- ✅ 멀티라인 문자열이 올바르게 이스케이프되었는지 확인
- ✅ 앵커/별칭이 확장되어 파일 크기가 증가할 수 있음 인지
결론: 상황에 맞는 포맷을 선택하자
JSON과 YAML은 서로를 대체하는 관계가 아니라, 상호 보완적인 관계입니다. 각각의 강점을 이해하고 적재적소에 활용하는 것이 중요합니다.
간단한 선택 기준
| 상황 | 추천 포맷 | 이유 |
|---|---|---|
| REST API 통신 | JSON | 빠른 파싱, 작은 크기, 표준 |
| 설정 파일 | YAML | 주석, 가독성, 멀티라인 |
| CI/CD 파이프라인 | YAML | 복잡한 워크플로우 표현 |
| 로그 파일 | JSON | 구조화된 검색, 스트리밍 |
| 사람이 직접 편집 | YAML | 읽기/쓰기 편함 |
| 기계가 처리 | JSON | 파싱 빠름, 에러 적음 |
현대적인 개발 워크플로우
2025년 현재, 대부분의 프로젝트는 두 포맷을 혼용합니다:
- 프론트엔드 ↔ 백엔드: JSON으로 통신
- 개발 환경 설정: YAML로 관리 (
.github/workflows/,docker-compose.yml) - 패키지 관리: JSON (
package.json,composer.json) - 인프라 코드: YAML (Kubernetes, Terraform)
무엇보다 중요한 것은 팀 컨벤션입니다. 이미 프로젝트에서 사용 중인 포맷이 있다면, 일관성을 위해 그것을 따르는 것이 최선입니다. 필요하다면 자동 변환 도구를 활용하여 두 포맷 간 전환을 간소화할 수 있습니다.
ToolZipper로 JSON/YAML 다루기
JSON과 YAML을 손쉽게 변환하고 포맷팅하세요. 모든 처리는 브라우저에서 즉시 이루어지며, 데이터는 서버로 전송되지 않습니다.