...
YAML 이란?
우리가 타 시스템 간에 데이터를 주고 받을 필요가 있을때 데이터의 연동과 호환성을 위해 포맷에 대한 규칙이 필요하게 된다.
한번 쯤 들어보았고, 또한 이때까지 자주 사용해왔던 CSV, XML, JSON, Properties 등이 바로 그것이다.
기존에는 웹에서는 XML과 JSON으로 그리고 자바 프로젝트에서는 properties 파일을 이용하여 어떤 값들을 정의하고 저장하고 사용해왔다.
XML은 사용하기 매우 까다롭고 가독성도 좋지 않기 때문에 요즘 들어 JSON 포매팅 방식으로 많이 이용되는 편이지만, JSON 역시 주석을 달수 없는 등 약간의 제한이 있고 쓸데없는 중괄호와 대괄호의 남발로 코드 길이가 강제적으로 길어지게 된다는 단점이 있다.
이러한 사용하기가 복잡하다는 점 때문에 2001년에 Clark Evans에 의해 최초 제안된 새로운 포매팅 방식이 YAML / YML 이다.
yaml은 xml과 json 포맷과 같이 타 시스템 간에 데이터를 주고받을 때 약속된 포맷(규칙)이 정의되어있는 또 하나의 파일 형식이라고 보면 된다.
다만 좀더 인간 친화적으로 작성해 가독성을 높이는 쪽으로 무게를 두었다.
그래서 고급 컴퓨터 언어에 친화적이다.
json과 달리 주석도 쓸수 있으며, 위의 사진과 같이 간결한 문법으로 같은 데이터량이라도 코드길이를 많이 줄일수 있다.
yaml은 주로 Doker Compose, Kubernetes, Flutter, Spring boot 프로젝트에서 설정파일을 정의할때 자주 애용된다.
[ Yaml 이름 어원 ]
Yaml의 뜻은 'Yaml Ain't Markup Language' 라는 뜻으로 'YAML은 마크업 언어가 아니다'라는 재귀적 말장난 같은 이름을 가지고 있다.
이게 무슨 소리인가 싶겠지만, 사실 YAML은 Yet Another Markup Language로 또 다른 마크업 언어라는 이름을 가지고 있다가 마치 NoSQL이 Not Only SQL로 뜻이 변화했듯이, 핵심은 마크업이 아니라 데이터가 중심이라는 보여주기 위해 Yaml Ain't Markup Language가 되어 버렸다고 보면 된다.
[ .yaml 과 .yml 차이점 ]
결론부터 말하자면 yaml과 yml파일 확장자는 모두 해석 및 구문이 동일하다. 즉, 어떤식으로 파일확장자를 짓든 똑같은 것이다.
이렇게 분리된 이유는, 옛날 Windows에서 Extensions는 파일확장자가 3자로 제한되는 특성이 있었기 때문이다.
그래서 예전에는 .html 대신 .htm 으로도 쓰였는데 이와 같은 원리이다.
하지만 요즘은 확장자에 3글자를 넣어야 하는 OS 시스템 수준의 시행이 없으니, 그냥 단순하게 .yaml을 사용하자.
yaml 문법 생김새를 보면 무언가 파이썬 스럽다는 것을 느낄 수 있다.
json은 중괄호를 이용해 데이터간의 구분을 표현하지만, 실제로 yaml은 띄어쓰기로 데이터 구분을 하기 때문이다.
문득, json과 yaml 간의 활용도 차이점에 대해 궁금할 수 도 있다.
문법 생김새만 다를뿐 데이터를 표현하고자 하는 의도는 둘이 같으니, 마치 프로그래밍 언어를 시작할때 자바로 할지 파이썬으로 할지 선택을 고민하는 선택장애가 오는 것과 비슷하다.
이에 대해 간단히 정리하자면, 대부분의 웹에서 데이터 통신을 위해 JSON을 많이 사용하고, reference를 정의할때 복잡한 object 구조를 표현 하기 위해 YAML이 적합하다고 보면 된다.
YAML 모델이 JSON 보다 좀 더 복잡하기 때문에 파싱하고 생성하는데 YAML이 더 느리다.
또한 문법 생김새로 인해, 파이썬 커뮤니티에서는 파이썬 문법과 비슷한 indent로 구분하는 YAML을 더 선호하고, 자바스크립트 진영에서는 별도의 파서가 필요없고 Javascript Object와 구조가 유사한 JSON을 선호하기도 한다.
YAML 문법 정리
yaml 포맷에는 고급 프로그래밍에 어울리다고 말한만큼 은근히 많은 문법들이 있다.
yaml 문법은 기존 프로그래밍 언어와 달리 많이 생소 하기 때문에 문법을 설명할때 우리에게 익숙한 json 포맷과 차이를 비교하여 설명할 예정이다.
다음 사이트에서는 YAML 예제를 입력하면 자동으로 JSON으로 변환되어, 해당 문법이 어떤 것을 의미하되는지 쉽게 확인할 수 있다.
이외에도 입력한 YAML에 문법상 오류가 있다면 오류 메시지가 출력되기도 하여, 문맥 확인/학습에 도움을 줄 수 있다.
데이터 정의
Key: Value 표기
json 포맷과 같이 기본적으로 key와 value는 콜론(:)을 기준으로 구분한다.
단, 주의 할점은 value를 쓸때 반드시 콜론(:) 뒤에 띄어쓰기를 한번 해줘야 한다는 점이다.
즉, 콜론을 쓰고 그 뒤에 공백문자까지 표기해 줘야 key와 value를 정의한 것이다.
만일 콜론(:) 뒤에 공백이 없으면 하나의 스트링으로 인식되어, 문법 오류가 나타나게 된다.
콤마 표기 안함
또한 yaml 데이터를 보면 알 듯이, json과 달리 여러개의 데이터가 있어도 콤마를 쓰지않고 바로 개행하는 것을 볼 수 있다.
name: 장돌준
age: 44
major: 컴퓨터공학
들여쓰기(indent)
중괄호로 계층 구조를 표현하는 JSON과 달리, YAML은 파이썬과 같이 들여쓰기로 계층 구조를 표현하기 때문에 들여쓰기 유무는 매우 중요하다.
들여쓰기는 기본적으로 2칸 혹은 4칸을 지원한다.
만일 owner 속성안에 name, age, major 키를 가진 객체를 포함시키도록 표현하려면, 들여쓰기를 통해 포함 관계를 표시해주어야 한다.
owner:
name: 장돌준
age: 44
major: 컴퓨터공학
{
"owner": {
"name": "장돌준",
"age": 44,
"major": "컴퓨터공학"
}
}
"따옴표" 표현
JSON을 작성할 때는 기본적으로 따옴표를 써줘야 하지만, YAML을 쓸 때는 큰 따옴표, 작은 따옴표 혹은 안 써도 자동으로 숫자와 문자열로 인식 된다.
그런데 만일 쌍점 : 가 들어간 문자열의 경우 무조건 따옴표로 둘러쌓야 제대로 인식된다.
name: Mark
address1: San Francisco
address2: "San Hoje"
address3: 'Los Angeles'
ratio1: "1:2"
ratio2: '1:2'
ratio3: 1:2 # 제대로 인식 안됨
{
"name": "Mark",
"address1": "San Francisco",
"address2": "San Hoje",
"address3": "Los Angeles",
"ratio1": "1:2",
"ratio2": "1:2",
"ratio3": 3720
}
작은 따옴표와 큰 따옴표 차이점
이처럼 YAML에서는 문자 데이터를 표현할때 따옴표를 안 써도 되지만, 이스케이프 문자를 구분해야 할때는 이 둘을 구분해서 사용하여야 한다.
예를들어 큰 따옴표는 escape sequence 처리를 해주고, 작은 따옴표는 주어진 그대로 문자열 처리한다.
그래서 다음 코드에서와 같이 \n 문자를 큰 따옴표로 묶으면 개행문자로 인식되고, 작은 따옴표로 묶으면 문자 그대로 \n로 처리해 주게 된다.
string1: "\t tab \n NL"
string2: '\t tab \n NL'
{
"string1": "\t tab \n NL",
"string2": "\\t tab \\n NL"
}
배열 & 리스트
단순 배열
YAML에서의 배열을 표현할 때는 하이픈(-)으로 시작하는 하위 엘리먼트로 표현할 수 있다.
이때도 반드시 하이픈(-) 뒤에 띄어쓰기를 써주어야 리스트 인식이 된다.
ingredients:
- 닭
- 튀김가루
- 자바소스
{
"ingredients": [
"닭",
"튀김가루",
"자바소스"
]
}
또한 다음과 같이 하이픈(-) 대신, 일반적인 배열 표현법 같이 대괄호로 리스트를 한줄로 표현도 가능하다.
ingredients: [닭, 튀김가루, 자바소스]
객체 배열
이번엔 단순한 값 리스트의 나열이 아니라, 보다 복잡한 구조체의 리스트를 표현하고자 한다면 하이픈(-) 과 key: value 구조를 이용하여 객체를 포함한 배열을 표현 가능하다.
이때 하나의 객체 중괄호 쌍인것을 나타내기 위해, 하나의 객체 원소면 하이픈(-)을 쓰지않고 그대로 나열한다.
students:
- name: Mark
major: Math
age: 20
- name: Julie
major: Arts
age: 23
- name: Tommy
major: Music
age: 25
{
"students": [
{
"name": "Mark",
"major": "Math",
"age": 20
},
{
"name": "Julie",
"major": "Arts",
"age": 23
},
{
"name": "Tommy",
"major": "Music",
"age": 25
}
]
}
일반 배열 하이픈(-)과 무슨차이가 있냐면, 하이픈(-) 뒤에 key: value 구조가 온다면 이것은 객체를 포함한 배열, 그냥 데이터가 바로 온다면 단순 원소 배열이라고 인식하면 된다.
ingredients: # 단일 원소 배열
- 닭
- 튀김가루
- 자바소스
ingredients2: # 객체 원소를 포함한 배열
- animal: 닭
- spice: 튀김가루
- source: 자바소스
{
"ingredients": [
"닭",
"튀김가루",
"자바소스"
],
"ingredients2": [
{
"animal": "닭"
},
{
"spice": "튀김가루"
},
{
"source": "자바소스"
}
]
}
이 역시 심플하게 한 줄로 표현할수도 있는데 다만, 가독성이 오히려 떨어지고 작성하기 더 불편해 질 수 있다.
students: [{name: Math, major: Math, age: 20}, {name: Julie, major: Arts, age: 23}, {name: Tommy, major: Music, age: 25}]
Boolean 표현
일반적인 프로그래밍 언어와 json과 달리 yaml의 불리언 표현은 총 4가지이다.
데이터 부분에 yes, no, true, false 를 지정하면 boolean 값으로 인식된다. 추가적으로 대문자 소문자를 가리지 않는다.
만약 yes나 no를 문자열 값으로서 전달하고자 한다면 따옴표로 묶어주어야 한다.
booleans:
- yes
- Yes
- YES
- true
- True
- FALSE
- "YES"
- "NO"
{
"booleans": [
true,
true,
true,
true,
true,
false,
"YES",
"NO"
]
}
주석 표현
json에서는 주석을 표기할수 없지만, yaml에선 파이썬과 같이 샵(#) 으로 주석을 표기할 수 있다.
# students
name: John
major: Math
텍스트 Multi Line
만일 개행이 있는 문자열을 json으로 표현한다면, 무자열 내에 \n 문자를 사이사이 넣어주어야 한다.
YAML에서는 일반적인 프로그래밍과 다르게 다른 방법으로 지원한다.
개행된 문자열을 표현하는데 다음과 같이 크게 두가지 방법이 존재한다.
>기호를 쓰는 방법(folded block scalar)|기호를 쓰는 방법(literal block scalar)
folded block scalar
> 기호는 개행(엔터) 문자를 스페이스로 치환해주고, 빈 줄 하나가 단독으로 있는 경우만 줄바꿈으로 바꿔준다.
원래는 맨 마지막은 자동으로 개행 처리를 해주지만, > 기호 옆에 - 를 붙여주면 맨끝의 줄바꿈 문자를 포함시키지 않는다.
# 중간에 엔터문자가 있다
# 끝에도 엔터문자가 있다
paragraph1: >
abc
def
ghi
aab
ccc
paragraph2: >- # 맨끝의 줄바꿈 문자를 포함시키지 않는다.
abc
def
ghi
aab
ccc
{
"paragraph1": "abc def ghi\naab ccc\n",
"paragraph2": "abc def ghi\naab ccc"
}
literal block scalar
| 기호는 개행(엔터) 문자 그대로 줄을 바꿔준다.
| 기호 옆에 - 를 붙여주면 맨끝의 줄바꿈 문자를 포함시키지 않는다.
# 그대로 줄바꿈 + 맨끝 개행문자 포함
paragraph3: |
abc
def
ghi
aab
ccc
# 그대로 줄바꿈 + 맨끝 개행문자 제외
paragraph4: |-
abc
def
ghi
aab
ccc
{
"paragraph3": "abc\ndef\nghi\n\naab\nccc\n",
"paragraph4": "abc\ndef\nghi\n\naab\nccc"
}
변수(alias) 기능
중복된 데이터가 여러번 반복되어 나열된다면 json 포맷에서는 하는수 없이 일일히 기재하여야 한다.
만일 중복되어 나열되는 데이터가 1000개 10000개 라면 꽤나 큰 용량 낭비가 된다.
이처럼 재사용하는 값 또는 구조체 등이 있을 경우, 보통 고급 프로그래밍 언어에서는 변수나 함수로 선언해 할당하는 식으로 구조를 짠다.
yaml에서도 이러한 변수와 비슷한 태그 기능을 지원해주는데, & 기호로 변수를 선언하고, * 기호로 변수를 참조(사용) 할 수 있다.
이를 이용해 반복되는 데이터 부분을 태그 하고 참조하는 식으로 구성해 놓으면, 보다 가독성 높게 데이터를 구성 할 수 있다.
default: &default_school # default_school 라는 변수를 선언하고, 그 내용은 group 과 description 데이터를 지니고 있다
group: '서울대학교'
description: |
서울에 위치하는 대한민국 대학교!
student:
- name: '홍길동'
<<: *default_school # default_school 변수 내용물을 대입한다
- name: '임꺽정'
<<: *default_school # default_school 변수 내용물을 대입한다
{
"default": {
"group": "서울대학교",
"description": "서울에 위치하는 대한민국 대학교!\n"
},
"student": [
{
"name": "홍길동",
"group": "서울대학교",
"description": "서울에 위치하는 대한민국 대학교!\n"
},
{
"name": "임꺽정",
"group": "서울대학교",
"description": "서울에 위치하는 대한민국 대학교!\n"
}
]
}
artist:
artist-alias1: &MK # 변수 선언
name: Mika
albums:
- Life in Cartoon Motion
- The Boy Who Knew Too Much
artist-alias2: &MJ # 변수 선언
name: Michael Jackson
albums:
- Off the Wall
- Thriller
- Bad
students:
- name: Mark
major: Math
age: 20
favorites: [*MK, *MJ] # 변수 할당
- name: Julie
major: Arts
age: 23
favorites: [*MK] # 변수 할당
{
"artist": {
"artist-alias1": {
"name": "Mika",
"albums": [
"Life in Cartoon Motion",
"The Boy Who Knew Too Much"
]
},
"artist-alias2": {
"name": "Michael Jackson",
"albums": [
"Off the Wall",
"Thriller",
"Bad"
]
}
},
"students": [
{
"name": "Mark",
"major": "Math",
"age": 20,
"favorites": [
{
"name": "Mika",
"albums": [
"Life in Cartoon Motion",
"The Boy Who Knew Too Much"
]
},
{
"name": "Michael Jackson",
"albums": [
"Off the Wall",
"Thriller",
"Bad"
]
}
]
},
{
"name": "Julie",
"major": "Arts",
"age": 23,
"favorites": [
{
"name": "Mika",
"albums": [
"Life in Cartoon Motion",
"The Boy Who Knew Too Much"
]
}
]
}
]
}
Multiple Documents
마지막으로 yaml 에는 두 파일의 데이터를 하나의 파일로 포함시켜 표현해주는 기능을 제공해준다.
이는 한 파일에서 모든 configuration을 관리할 수 있다는 것이다.
예를들어 다음과 같은 스프링 프로젝트 properties 파일 두개가 있다고 가정하자.
spring.profiles=dev
spring.datasource.url=jdbc:mysql://
management.endpoints.web.base-path=/
spring.datasource.hikari.minimum-idle=1
spring.datasource.hikari.maximum-pool-size=3
spring.profiles=prod
spring.datasource.url=jdbc:mysql://
management.endpoints.web.base-path=/
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=10
스프링 프로젝트 환경이 dev냐 product 환경이냐에 따라 적용되는 옵션 값들이 다른 설정 프로퍼티 파일들인데, yaml에서는 하이픈 3개를 이용하여 --- 이 두 옵션 파일을 하나의 파일에 포함시켜 한꺼번에 정의해줄 수 있다.
다만, 이러한 방법은 오히려 특정 환경의 설정만 바꾸고 싶은데 똑같은 key들이 환경별로 있어서 헷갈린다는 사람도 있을 것이고, 하나의 파일에서 설정을 다 바꿀 수 있어서 여기 저기 다른 파일을 탐색하지 않아서 좋다고 하는 사람도 있을 수 있을 것이다.
따라서 자기 취향 혹은 팀의 가이드라인을 따라 사용하면 된다.
properties 데이터들을 yaml 데이터로 변환해주면 다음과 같이 된다.
spring:
profiles: dev
datasource:
url: jdbc:mysql://
hikari:
minimum-idle: 1
maximum-pool-size: 3
management:
endpoints:
web:
base-path: /
---
spring:
profiles: prod
datasource:
url: jdbc:mysql://
hikari:
minimum-idle: 5
maximum-pool-size: 10
management:
endpoints:
web:
base-path: /
스프링부트 2.4부터는 properties 파일에서도#---기호로 로 설정 분리가 가능하기 때문에 반드시 yaml 확장자를 사용할 필요는 없다.
document 시작 / 끝
보다 면밀히 문법을 보자면, --- 와 ... 구분자로 document의 시작과 끝을 표현할 수 있다.
--- # Document content begin
spring:
profiles: local
datasource:
url: jdbc:mysql://local
... # Document content end
--- # Document content begin
spring:
profiles: dev
datasource:
url: jdbc:mysql://dev
... # Document content end
다만 ... 은 생략해도 문제없기 때문에 보통 다음과 같이 --- 만 이용하여 multiple document를 표현한다.
spring:
profiles: local
datasource:
url: jdbc:mysql://local
---
spring:
profiles: dev
datasource:
url: jdbc:mysql://dev
# 참고자료
https://needjarvis.tistory.com/590
https://perfectacle.github.io/2018/08/19/yaml/
https://luran.me/397
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.