CSV to YAML 변환이란?
CSV to YAML 변환은 쉼표로 구분된 표 형식 데이터를 YAML(YAML Ain't Markup Language) 형식으로 변환합니다. CSV 파일의 각 행은 YAML 시퀀스의 항목이 되고, 각 열 헤더는 YAML 매핑의 키에 대응합니다. 출력은 사람이 읽기 쉬운 들여쓰기 기반 문서로, 설정 도구와 데이터 파이프라인이 별도의 스키마 파일 없이 직접 파싱할 수 있습니다.
YAML은 2001년에 처음 제안되었고 현재 명세(YAML 1.2)는 2009년에 확정되었습니다. Kubernetes 매니페스트, Docker Compose 파일, Ansible 플레이북, GitHub Actions 워크플로우, 그리고 많은 CI/CD 시스템의 기본 설정 형식입니다. 데이터가 스프레드시트나 데이터베이스 내보내기 형태로 존재할 때, CSV를 YAML로 변환하는 것이 이러한 시스템에 필요한 설정 항목, 시드 데이터 픽스처, 테스트 입력을 생성하는 가장 빠른 방법입니다.
올바른 CSV to YAML 변환기는 RFC 4180 파싱 규칙을 처리해야 합니다. 쉼표나 줄바꿈이 포함된 따옴표 필드, 이스케이프된 큰따옴표, 다양한 구분자가 여기에 해당합니다. YAML 측면에서는 불리언처럼 보이는 값(yes, no, true, false), 숫자, null은 YAML 파서가 의도하지 않은 타입으로 강제 변환하지 않도록 따옴표로 감싸야 합니다. 변환기는 유니코드 콘텐츠도 처리하고 유효한 UTF-8 출력을 생성해야 합니다. YAML 1.2는 UTF-8을 기본 인코딩으로 요구하기 때문입니다.
CSV to YAML 변환기를 사용하는 이유
스프레드시트 데이터에서 YAML을 직접 작성하는 것은 번거롭고 오류가 발생하기 쉽습니다. 잘못된 들여쓰기나 따옴표 없는 특수 문자 하나가 설정 배포나 데이터 가져오기를 중단시키는 유효하지 않은 YAML을 만들어냅니다. 이 변환기는 CSV 필드를 파싱하고, 헤더를 키에 매핑하며, 적절히 들여쓰기되고 올바르게 따옴표 처리된 YAML 출력을 생성합니다.
CSV to YAML 활용 사례
CSV to YAML 매핑 참조
CSV 파일의 각 구조적 요소는 YAML에서 직접 대응하는 개념이 있습니다.
| CSV 개념 | YAML 대응 | 세부 사항 |
|---|---|---|
| CSV file | YAML document | The entire file becomes a YAML sequence (array) of mappings |
| Header row | Mapping keys | Each column header becomes a key in every mapping entry |
| Data row | Sequence item (- ...) | Each row becomes one mapping item in the top-level array |
| Cell value | Scalar value | Strings, numbers, and booleans are inferred by YAML parsers |
| Empty cell | null or empty string | Rendered as null, ~, or an empty value depending on the tool |
| Comma delimiter | Indentation + colon | CSV delimiters are replaced by YAML key: value structure |
CSV vs YAML
CSV는 타입 정보나 계층 구조가 없는 단순한 구분자 기반 형식입니다. YAML은 중첩을 위해 들여쓰기를 사용하고, 여러 데이터 타입을 지원하며, 주석을 허용하는 JSON의 상위 집합입니다. 선택은 다운스트림 도구가 무엇을 요구하는지에 따라 달라집니다.
코드 예제
Node.js, Python, Go, CLI 도구에서의 동작 예제입니다. 각각 CSV 헤더 행을 YAML 키로 읽고, 각 데이터 행을 시퀀스 항목에 매핑하며, 타입이 모호한 값에 대한 따옴표 처리를 수행합니다.
import { parse } from 'csv-parse/sync'
import { stringify } from 'yaml'
const csv = `name,age,city
Alice,30,Berlin
Bob,25,"New York"`
const records = parse(csv, { columns: true, skip_empty_lines: true })
console.log(stringify(records))
// → - name: Alice
// → age: "30"
// → city: Berlin
// → - name: Bob
// → age: "25"
// → city: New York
// Vanilla JS (no dependencies)
function csvToYaml(csv) {
const [headerLine, ...dataLines] = csv.trim().split('\n')
const headers = headerLine.split(',')
return dataLines.map(line => {
const values = line.split(',')
return headers.map((h, i) => ` ${h}: ${values[i] || ''}`).join('\n')
}).map(block => `- ${block.trimStart()}`).join('\n')
}import csv, io, yaml
csv_string = """name,age,city
Alice,30,Berlin
Bob,25,New York"""
reader = csv.DictReader(io.StringIO(csv_string))
data = list(reader)
# default_flow_style=False produces block-style YAML
print(yaml.dump(data, default_flow_style=False, sort_keys=False))
# → - age: '30'
# → city: Berlin
# → name: Alice
# → - age: '25'
# → city: New York
# → name: Bob
# Preserve insertion order with sort_keys=False (Python 3.7+)
# To type-cast numbers: data = [{k: int(v) if v.isdigit() else v ...}]package main
import (
"encoding/csv"
"fmt"
"gopkg.in/yaml.v3"
"strings"
)
func main() {
input := "name,age,city
Alice,30,Berlin
Bob,25,New York"
r := csv.NewReader(strings.NewReader(input))
records, _ := r.ReadAll()
headers := records[0]
var data []map[string]string
for _, row := range records[1:] {
entry := make(map[string]string)
for i, h := range headers {
entry[h] = row[i]
}
data = append(data, entry)
}
out, _ := yaml.Marshal(data)
fmt.Println(string(out))
// → - age: "30"
// → city: Berlin
// → name: Alice
// → - age: "25"
// → city: New York
// → name: Bob
}# Using yq (https://github.com/mikefarah/yq) # yq reads CSV with --input-format=csv yq --input-format=csv --output-format=yaml '.' data.csv # Using Miller (mlr) — converts between CSV, JSON, YAML, and more mlr --icsv --oyaml cat data.csv # Python one-liner for quick conversion python3 -c " import csv, sys, yaml data = list(csv.DictReader(sys.stdin)) print(yaml.dump(data, default_flow_style=False, sort_keys=False)) " < data.csv