야멜;(yaml/yml)

noname2048·2021년 3월 10일
0

초록

도커도커-컴포즈circleci 같은 ci 툴을 공부하면서 다루는 yaml에 대해 헷갈리는 것들이 많이 한번쯤 정리해 보자는 취지에서 글을 쓰게 되었습니다.

🥕 YAML example

# https://docs.ansible.com/ansible/2.3/YAMLSyntax.html
# Employee records
-  martin:
    name: Martin D'vloper
    job: Developer
    skills:
      - python
      - perl
      - pascal
-  tabitha:
    name: Tabitha Bitumen
    job: Developer
    skills:
      - lisp
      - fortran
      - erlang
docker:
- item    : Basketball
  quantity: 4

위 예제를 보고 자료의 구성이나 자료구조가 바로 이해가신다면 글을 보지 않으셔도 무방합니다.
📃 공식 도큐먼트를 먼저 참고하실 분은 🔹yaml.org/spec/1.2 를 참고해 주세요

본론

https://yaml.org/spec/1.2/spec.html 1절에 나와있는 yaml에 대한 소개

Chapter 1. Introduction

“YAML Ain’t Markup Language” (abbreviated YAML) is a data serialization language designed to be human-friendly and work well with modern programming languages for common everyday tasks. This specification is both an introduction to the YAML language and the concepts supporting it, and also a complete specification of the information needed to develop applications for processing YAML.

json 이후로 인간친화적인 문서형태를 고민하던 후발주자인 yaml 은 YAML Ain’t Markup Language” (abbreviated YAML) 라는 뜻을 가지고, 데이터 직렬화를 지원하기 위해 만들었다고 한다.

실제로 json을 사용하면서 주석trailing comma를 지원하지 않는 다는 것에 불편함을 느끼고 있었는데, json 이 최고라는 나의 관점을 바꾸는 계기가 되어준 언어 되시겠다.

주로 도커도커 컴포즈를 하면서 정말 깔끔한 환경설정을 가능하게 하는 신기한 언어이면서 동시에 개념이 정립되지 않았기에 여러가지 의문점도 가지게 해주었다.

yaml을 사용하는 곳이 실제로 좀 많은 것 같은데, 요약한 블로그를 돌아다녀보면 다들 나랑 달리 능력자라 헷갈리는 부분이 없었나보다 했다. 특히 이건 되고, 왜 둘의 차이가 뭐지 하며 고민을 품게 하는 데에는 dash("-") 라는 부호의 사용이 명확하지 않은데 있었다.

# circle ci 예제
jobs:
  simple_build: # job names
    docker: # docker image
      - image: noname2048/askbanana:simple
        auth:
          username: noname2048 
          password: $DOCKERHUB_PASSWORD
# image 들여쓰기를 다르게 해보았다. (그래도 같은 내용이다)
jobs:
  simple_build: # job names
    docker: # docker image
    - image: noname2048/askbanana:simple
      auth:
        username: noname2048 
        password: $DOCKERHUB_PASSWORD
# json 으로 바꾸면
{
  "jobs": {
    "simple_build": {
      "docker": [
        {
          "image": "noname2048/askbanana:simple",
          "auth": {
            "username": "noname2048",
            "password": "$DOCKERHUB_PASSWORD"
          }
        }
      ]
    }
  }
}

주요컨셉

주요한 특징은

  • 들여쓰기를 문법의 일부로 사용한다. 좀 더 자세히 표현하면 파이썬처럼 들여쓰기가 있고 없고에 관심이 있다. ( 2배, 3배 혹은 4배로 들여쓰기를 하고 다음표현으로 넘어갈 수 있다. )
  • json을 호환하는 상위 문서라 yaml parser는 json을 읽을 수도 있고, yaml 내부에 json표현을 사용할 수 도 있다. 특히 json의 불편한 점을 해결하려고 노력했다는 점이 마음에 들었다.

마이너한 특징 (세부특징) 은

  • scalar에 대한 지원이 풍부하다 (newline, escape에 대한 고려)
  • tag 문법을 지원한다. (주석 외에도 표기가 가능하다)
  • 변수와 같은 기능이 있다.

간단한 예제

yaml.org 1.2 버전(2009) 에서 다음 full length example (figure 2.8) 을 가져와 보았다.

---
Time: 2001-11-23 15:01:42 -5
User: ed
Warning:
  This is an error message
  for the log file
---
Time: 2001-11-23 15:02:31 -5
User: ed
Warning:
  A slightly different error
  message.
---
Date: 2001-11-23 15:03:17 -5
User: ed
Fatal:
  Unknown variable "bar"
Stack:
  - file: TopClass.py
    line: 23
    code: |
      x = MoreObject("345\n")
  - file: MoreClass.py
    line: 58
    code: |-
      foo = bar

주로 리스트와 해시(딕셔너리)를 섞어놓은 느낌이 온다면 아마 조금만 공부하면 실제 사용하는데 무리가 없을 것이라 생각한다.

그러면 공식문서와 같이 주요 기호와 구조를 배워보자.

주요기호

2.1. Collections

YAML’s block collections use indentation for scope and begin each entry on its own line. Block sequences indicate each entry with a dash and space ( “- ”). Mappings use a colon and space (“: ”) to mark each key: value pair. Comments begin with an octothorpe (also called a “hash”, “sharp”, “pound”, or “number sign” - “#”).

  • block collection: 들여쓰기scope를 나눈다.
  • block sequence: dash("-") 로 나타낸다.
  • Mapping: colon and space (": ") 로 표현한다.
  • 주석은 hash, sharp, pound, number sign 등 이라 불리는 ("#") * 으로 표현한다.

아래는 예제를 좀 더 읽어보고, yaml에 등장하는 기호를 정리해 보았다.

기호내용
---내용의 시작을 나타낸다 (생략가능)
...내용의 끝을 나타낸다 (생략가능)
#주석을 작성할때 쓴다
-block sequence 표현자
|줄바꿈이 보존되는 (preserved, In literals) scalar 표현
>줄바꿈이 space로 변경되는 (In the folded scalars) scalar 표현
!tag
!!tag에 대한 shorthand 표현 (약속어)
?complex key 표현, set (unordered) 표현
'The single-quoted style is useful when escaping is not needed.
"The double-quoted style provides escape sequences.
&gloabl variable 선언
기타\b \x0 \u 등 스칼라 표현과 관련해서 쓰는 표현들

주요컨셉

공식문서를 통해 우선 3가지의 개념을 확립하면, 실제에 사용하는데 큰 무리는 없는 듯하다...

주요 용어는 공식에서 사용하던걸 가져오고, 괄호안에는 파이썬과 유사한 개념을 적어 보았다.

  1. scalar형 (variable)
  2. Sequence형 (list)
  3. map형 (dict)

이제 예제를 풀어보자.

스칼라가 단독으로 쓰인 예제는 아직 못 읽어보았다. 즉, 자료구조의 개념으로는 시퀀스와 매핑만 존재하고, scalar 은 데이터를 이루는 말이라고 생각해도 될 듯 하다.

예제를 보면서 json2yaml를 이용해 실제 json으로 어떻게 파싱되는지 보는것이 큰 공부가 되었다. 다른 사이트 중에는 다르게 동작하는게 있어 오히려 헷갈려서, 파싱기가 제대로 동작하는 것 같지 않다는 생각에 다른 것과 비교해보는 것이 도움이 되었다.

아래의 번호는 공식문서에 나오는 예제에 대해 임의로 번호를 매긴것입니다. 실제예제는 링크를 통해 확인해주세요 😀

1.1 시퀀스 (스칼라) Sequence of Scalars

# yaml
- Mark McGwire
- Sammy Sosa
- Ken Griffey

# json
[
  "Mark McGwire",
  "Sammy Sosa",
  "Ken Griffey"
]

1.2 매핑 (스칼라-스칼라) Mapping Scalars to Scalars

# yaml
hr:  65    # Home runs
avg: 0.278 # Batting average
rbi: 147   # Runs Batted In

# json
{
  "hr": 65,
  "avg": 0.278,
  "rbi": 147
}

1.3 매핑 (스칼라 - 시퀀스) Mapping Scalars to Sequences

# yaml
american:
  - Boston Red Sox
  - Detroit Tigers
  - New York Yankees
  
# json
{
  "american": [
    "Boston Red Sox",
    "Detroit Tigers",
    "New York Yankees"
  ]
}

1.4 시퀀스 (매핑) Sequence of Mappings

# yaml
-
  name: Mark McGwire
  hr:   65
  avg:  0.278
-
  name: Sammy Sosa
  hr:   63
  avg:  0.288
  
# json
[
  {
    "name": "Mark McGwire",
    "hr": 65,
    "avg": 0.278
  },
  {
    "name": "Sammy Sosa",
    "hr": 63,
    "avg": 0.288
  }
]

1.5 시퀀스 (시퀀스의) Sequence of Sequences

# yaml
- 
  - name
  - hr
  - avg
- [Mark McGwire, 65, 0.278]

# json
[
  [
    "name",
    "hr",
    "avg"
  ],
  [
    "Mark McGwire",
    65,
    0.278
  ]
]

1.6 매핑 (매핑) Mapping of Mappings

# yaml
Mark McGwire: {hr: 65, avg: 0.278}
Sammy Sosa:
  hr: 63,
  avg: 0.288

# json
{
  "Mark McGwire": {
    "hr": 65,
    "avg": 0.278
  },
  "Sammy Sosa": {
    "hr": 63,
    "avg": 0.288
  }
}

1.7 중복된 내용에 대한 작성 (&, *)

Repeated nodes (objects) are first identified by an anchor (marked with the ampersand - “&”), and are then aliased (referenced with an asterisk - “*”) thereafter.

# yaml
hr:
  - Mark McGwire
  - &SS Sammy Sosa
rbi:
  - *SS
  - Ken Griffey

# json
{
  "hr": [
    "Mark McGwire",
    "Sammy Sosa"
  ],
  "rbi": [
    "Sammy Sosa",
    "Ken Griffey"
  ]
}

1.9 복잡한 key 에 대해 (?)

question mark and space (“? ”) indicate a complex mapping key.

# yaml
? - Detroit Tigers
  - Chicago cubs
:
  - 2001-07-23

? [ New York Yankees,
    Atlanta Braves ]
: [ 2001-07-02, 2001-08-12,
    2001-08-14 ]

# json
{
  "[\"Detroit Tigers\", \"Chicago cubs\"]": [
    "2001-07-23"
  ],
  "[\"New York Yankees\", \"Atlanta Braves\"]": [
    "2001-07-02",
    "2001-08-12",
    "2001-08-14"
  ]
}

⭐1.10 중첩 매핑⭐

이것 때문에 공부를 시작하고 글을 쓰게 된 계기였다.
item 밑의 quantity 가 seq 형인지 map 형인지, 어디에 포함되는지 많이 헷갈렸었다. dash("-") 가 쓰인 곳은 map을 인자로 사용하는 seq 형으로, 다음 대시가 나오기 전까지의 인자를 map으로 묶어 seq 형에 넣는다.

# json
# Products purchased
- item    : Super Hoop
  quantity: 1
- item    : Basketball
  quantity: 4
- item    : Big Shoes
  quantity: 1
  
# yaml
[
  {
    "item": "Super Hoop",
    "quantity": 1
  },
  {
    "item": "Basketball",
    "quantity": 4
  },
  {
    "item": "Big Shoes",
    "quantity": 1
  }
]

연습문제 (이해했다고 생각된다면 1번과 2번의 차이를 말해보자...)

# 01
Fatal:
  Unknown variable "bar"
Stack:
  - file: TopClass.py
    line: 23 # dash 추가됨!!!!
    code: |
      x = MoreObject("345\n")
  - file: MoreClass.py
    line: 58
    code: |-
      foo = bar
# 02
Fatal:
  Unknown variable "bar"
Stack:
  - file: TopClass.py
  - line: 23 # dash 추가됨!!!!
    code: |
      x = MoreObject("345\n")
  - file: MoreClass.py
    line: 58
    code: |-
      foo = bar
# 03
jobs:
- simple_build:
  filters:
    branches:
      only:
      - master
# 04
jobs:
- simple_build:
  filters:
  branches:
      only:
      - master

실전으로

마지막 실제 사용 예제를 올린다. 허접하지만, circleci 에서 (ci 툴) 환경설정을 하면서 직접 사용했던 파일이다. 이때 docker 하위 node에 (이제 와서 node라고 표현을 써서 그렇지만 😅, xml과 같은 트리구조의 형태이기 때문에 yaml.org에서도 node라고 표현. 비슷한 개념의 DOM을 생각해보자.) 존재하는 dash("-") 에 대해서 어떻게 생각해야할지 고민이 많았는데, 해결이 되었다.

version: 2.1

jobs:
  simple_build: # job names
    docker: # docker image
    - image: noname2048/askbanana:simple
      auth:
        username: noname2048 
        password: $DOCKERHUB_PASSWORD
    environment:
      DEBUG: "False"
      DB: "local"
    working_directory: /askbanana/django_project # ~/project # default
    steps:
    - run:
        name: send to codecov
        command: |
          coverage run ./manage.py test
          codecov -t $CODECOV_TOKEN

결과적으로 대쉬가 존재하는 이유는 job에서 docker를 이용하여 ci 프로세스를 진행할때, 여러개의 도커를 사용할 수 있기 때문이였다. 여기서는 한개의 도커를 사용하고, 0번째 시퀀스 아래에는 해당 도커를 정의하는 image와 그 이미지를 가져올 수 있는 auth 가 정의되어있다.

enviroment가 바깥에 나와있는 이유는 아마 모든 docker image에 공통적용이 되서 그런가 보다 하고 넘어가기로 했다.(ㅎㅎ;)

마찬가지로 steps 안에는 여러개의 run이 존재할 수 있다.

참고자료

profile
설명을 쉽게 잘하는 개발자를 꿈꾸는 웹 개발 주니어

0개의 댓글