Daily Notebook

[Java] YAML 활용하기

by 배부른코딩로그
💡 스프링부트에서 자주 보던 *.yml 파일을 *.properties 대신 사용해보자!

목표

  • YAML 이 무엇인지 설명할 수 있다.
  • SnakeYAML 라이브러리를 통해 YAML 파일을 다룰 수 있다.
  • YAML 데이터를 다루는 참고 예제를 만들 수 있다.

 

서버에서 순수 자바로 구현된 프로세스들이 다수 존재할 수 있다.

보통 XML 혹은 Properties 파일로 관리하는 편이다. 개인적으로 각각의 장단점은 다음과 같다.

확장자 장점 단점
XML 리스트 관리 편함.
명확한 계층구조.
open, close 태그 등 내용이 불필요하게 길어짐.
프로그래밍 비교적 불편.
Properties 프로그래밍이 쉬운 key, value 구조.
비교적 짧은 내용.
리스트 관리 불편.
계층구조를 위한 긴 key 값 표현.

 

그래서 이번엔 YAML(YML)을 적용해보고 싶었다.

YAML 확장자는 계층구조 표현도 깔끔하고 읽기 쉬운 형태의 문법 형식을 가지고 있다고 판단했기 때문이다.

 

YAML 기본문법

주석 (comment)

주석은 #으로 표시한다. 예시는 다음 문법 설명에서 활용할 것이다.

들여쓰기 (indent)

들여쓰기는 기본적으로 2칸 또는 4칸을 지원한다.

# 2칸 들여쓰기(*추천)
server:
  name: Apache Tomcat
  rule: WAS
  apps:
    - test
    - solution

# 4칸 들여쓰기
server:
    name: Apache Tomcat
    rule: WAS
    apps:
        - test
        - solution

데이터 정의 (Map)

데이터는 keyvalue 형식이다. 주의할 점은 key와 value사이에는 반드시 빈칸(Whitespace)이 필요하다는 것이다.

# ok (key: value)
name: 배부른코더

# error (not key-value, string)
key:value

참/거짓

참/거짓은 [ true / false ] 및 [ yes / no ]를 인식한다.

isRunnable: true # ↔ false
isRequired: no 	 # ↔ yes

줄바꿈 (New line)

따옴표(")를 기준으로 숫자 혹은 문자임을 인식할 수 있다.

여러 줄을 표현해야할 경우는 어떻게 해야할까?

"|" 지시어를 사용함으로써 표현이 가능하다.

description: |
    newline
    description test
    syntax ok!

특이한 점은 "|-" 및 ">" 지시어를 통해서 조금 추가된 기능의 줄바꿈도 지원한다.

"|-" 지시어는 마지막 줄바꿈을 제외한다.

description: |-
    newline
    
    description test
    
    syntax ok!

위의 내용은 "newline\n\ndescription test\n\nsyntax ok!" 과 동일하다.

">" 지시어는 내용 사이에 들어간 개행을 제외한다.

description: >
    newline
    
    description test
    
    syntax ok!

위의 내용은 "newline\ndescription test\nsyntax ok!\n" 과 동일하다.

 

YAML 예제

openJDK를 사용했고, YAML을 load 하는 라이브러리는 SnakeYAML을 의존했다.

유틸성 함수들은 따로 분리했고, 다양한 Class type을 로드할 수 있도록 다음과 같이 구현했다.

YAML to Object

// CustomFileUtils.java
public static InputStream readFile(String parentPath, String filename) throws FileNotFoundException {
    String path = parentPath + File.separator + filename;
    return readFile(path);
}

public static <T> Object loadYamlObject(InputStream inputStream, Class<T> clazz) throws IOException {
    Yaml yaml = new Yaml(new Constructor(clazz));
    return yaml.load(inputStream);
}
public Clients loadClients(String directory, String filename) {
    Clients clients = null;
    try {
        InputStream inputStream = FileUtils.readFile(directory, filename);
        clients = (Clients) FileUtils.loadYamlObject(inputStream, Clients.class);

    } catch (FileNotFoundException fe) {
        logger.error("Could not find yaml file!");
    } catch (Exception e) {
        logger.error("Please validate the contents!", e);
    }

    return clients;
}
// Clients.java
public class Clients {
    private List<Client> clients;
}

// Client.java
public class Client {
    private String name;
    private String description;
    
    @Builder.Default
    private boolean runable = false;
    @Builder.Default
    private Integer polling = 10;
    
    private Source source;
    private Target target;
}

전체 코드는 불필요하기 때문에 위와 같이 소스코드가 구현되어져있다고 

clients:
  - name: CLIENT1
    description: 클라이언트 1
    runable: true
    polling: 5
    source:
      protocol: SFTP
      host: 127.0.0.2
      port: 10022
      user: client1
      password: 
    target:
      protocol: SFTP
      host: 127.0.0.1
      port: 22
      user: localhost
      password: 
    
  - name: CLIENT2
    description: 클라이언트 2
    runable: true
    polling: 10
    source:
      protocol: FTP
      host: 127.0.0.3
      port: 10021
      user: client2
      password: 
    target:
      protocol: SFTP
      host: 127.0.0.1
      port: 22
      user: localhost
      password:

위 코드를 실행시키면, load 함수를 통해 Clients 객체로 맵핑이 되게 된다.

맵핑된 객체를 출력해보면 다음과 같다.

Client(runable=true, polling=5, name=CLIENT1, description=클라이언트 1, source=Source(protocol=SFTP, host=127.0.0.2, port=10022, user=client1, password=null), target=Target(protocol=SFTP, host=127.0.0.1, port=22, user=localhost, password=null))
Client(runable=true, polling=10, name=CLIENT2, description=클라이언트 2, source=Source(protocol=FTP, host=127.0.0.3, port=10021, user=client2, password=null), target=Target(protocol=SFTP, host=127.0.0.1, port=22, user=localhost, password=null))

※ YAML 문법 오류시, 틀린 부분도 친절하게 알려주기 때문에 쉽게 수정할 수 있다.

 

Object to YAML

yaml 파일로 쓰는 방법에 대한 간단한 예제이다.

Client client = Client.builder()
            .name("Temp")
            .description("Temp for Write Test")
            .runable(true)
            .polling(60)
            .source(new Source())
            .target(new Target())
        .build();
PrintWriter writer = new PrintWriter(new File("./clients.yml"));
Yaml yaml = new Yaml();
yaml.dump(client, writer);
name: Temp
description: Temp for Write Test
runable: true
polling: 60
source:
  protocol: 
  host: 
  port: 
  user: 
  password: 
target:
  protocol: 
  host: 
  port: 
  user: 
  password:

위와 같이 clients.yml 파일에 잘 작성되는 것을 확인할 수 있다.

간단한 예제로 의존성이 높은 객체들도 Object to Yaml 도 개인적으로 시도해볼 예정이다.

 

 

결론

어떤 포맷이든 라이브러리를 사용하면 쉽게 사용할 수 있다.

하지만, 사람이 보는 파일은 가독성이 좋은 것을 선호하게 된다.

YAML 포맷의 가독성 만큼은 인정한다 : )

가독성 면에서는 XML과 Properties 보다 월등한 느낌이다.

오히려, 열고 닫는 태그를 신경 쓰지 않다 보니 JSON 보다 더 수정 관리하기 편했다.

아직은 문법이 헷갈리지만, 앞으로 파일로 설정정보를 관리하게 된다면, yaml을 적극 활용할 것 같다.

 

 

출처

 

Last Updated. 2022. 05. 10.

 

반응형

'Java' 카테고리의 다른 글

[Java] Null Safe한 자바 컬렉션 정렬  (0) 2022.05.24
[Java] Enum 소화하기  (0) 2022.05.09
[Java] @Required 직접 만들어보기  (0) 2022.04.23
[Java] Array, List, Set, Map 선언과 초기화  (1) 2021.11.05
[Java] Java Optional  (0) 2021.06.14

블로그의 정보

배부른코딩로그

배부른코딩로그

활동하기