본문 바로가기

Java/Spring

spring boot yaml 파일에 쓰기! jackson-dataformat-yaml을 이용한 방법(snakeyaml? how to write to yaml file in spring boot)

Spring boot에서 yaml 파일에 쓰기

Spring boot에서 보통 application.yml 파일이나 application.properties 파일에 설정값들을 적고 @ConfigurationProperties을 이용해서 Bean으로 등록해서 사용하는 게 일반적이다.

그래서 그런지 yaml 파일에 쓰는 것은 인터넷에 많이 안 나온다.

아래에서 아주 간단하게 써볼 예정이다.

필자가 yaml 파일에 쓰는 것을 생각하게된 이유는 서버 애플리케이션 운영 중에 설정 값을 변경하고 싶고, 서버 애플리케이션이 꺼졌다가 다시 실행되더라도 설정 값을 유지하고 싶었기 때문이다.

간단하게 데이터베이스에 설정 값을 저장하면 되지만...?

필자는 서버 애플리케이션을 개발하지만 DB를 내 마음대로 달 수가 없는 환경이다...

필드의 CS엔지니어에게 yml 파일 하나로 모든 설정 값을 관리하도록 가이드하기 위해서 한 번 시도해볼 것이다.


예제 프로젝트, jackson-dataformat-yaml

* 구글에 검색해보니 snakeyaml 이라는게 나오던데 더 좋아 보이고 익숙한 jackson의 jackson-dataformat-yaml를 이용했다.

- 사용법

1. dependency 추가

<dependency>
	<groupId>com.fasterxml.jackson.dataformat</groupId>
	<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

2. 프로젝트 경로에 yml파일 생성 ( /config/application-config.yml )

---
name: eqp1
maxCount: 1
threshold: 90
associated:
  name: cha
  number: 1

3. 읽고 쓰기

@RestController
public class TestController {
	private static final String projPath = System.getProperty("user.dir");
	@Autowired
	private EquipmentProperties equipmentProperties;
	
	@GetMapping("/")
	public String test2() throws JsonParseException, JsonMappingException, IOException {
		ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
		String absolutePath = projPath + File.separator +"config" + File.separator + "application-config.yml";
		File file = new File(absolutePath);
		System.out.println(equipmentProperties);
		//EquipmentProperties [name=eqp1, maxCount=1, threshold=90, associated=Associated [name=cha, number=1]]
		equipmentProperties = objectMapper.readValue(file, EquipmentProperties.class);
		System.out.println(equipmentProperties);
		//EquipmentProperties [name=eqp1, maxCount=1, threshold=90, associated=Associated [name=cha, number=1]]
		equipmentProperties.setMaxCount(5);
		objectMapper.writeValue(file, equipmentProperties);
		System.out.println(equipmentProperties);
		//EquipmentProperties [name=eqp1, maxCount=5, threshold=90, associated=Associated [name=cha, number=1]]
		return equipmentProperties.toString();
	}
}

예제니까 그냥 컨트롤러 하나만들어서 거기다가 전부 작성했다.

json 다룰 때처럼 ObjectMapper를 생성하는데 거기에 YAMLFactory를 넣어서 생성해준다.

그러면 objectMapper는 yaml로 읽고 쓸 수 있게 되었다.

아까 만들어놓은 application-config.yml 파일 경로를 이용해서 파일을 생성했다.

그리고 @Autowired로 EquipmentProperties Bean을 가져왔는데 이 Bean은 @ComfigurationProperties로 가져온 것이다. 아래 참조

package com.example.demo.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import com.example.demo.dto.Associated;

@Component
@ConfigurationProperties
public class EquipmentProperties {
	private String name;
	private int maxCount;
	private int threshold;
	private Associated associated;
	
	public EquipmentProperties() {}
	public EquipmentProperties(String name, int maxCount, int threshold, Associated associated) {
		super();
		this.name = name;
		this.maxCount = maxCount;
		this.threshold = threshold;
		this.associated = associated;
	}
	//toString()
	//getter, setter ...
}

/////////////////////////////////다른 파일/////////////////////////////////
public class Associated {
	private String name;
	private int number;
	
    public Associated() {}
	public Associated(String name, int number) {
		super();
		this.name = name;
		this.number = number;
	}
	//toString()
	//getter, setter
}

getter, setter, toString은 주석으로 생략했다.

@Autowired로 가져온 Bean을 출력해보니 잘 출력되는 것을 알 수 있다. (주석 참조)

그다음 objectMapper로 똑같은 파일을 읽어와도 똑같이 잘 출력되는 것을 알 수 있다. (주석 참조)

다음에 설정값을 수정하고 writeValue로 파일에 저장했더니 파일이 변경되었다.

---
name: "eqp1"
maxCount: 5
threshold: 90
associated:
  name: "cha"
  number: 1

class-path에는 다음과 같이 application.yml을 작성했다.

spring:
  profiles:
    active:
    - config
    
server:
  port: 8181

* spring.profiles.active에 config라고 적어서 /config/application-{profile}. yml로 읽어진 것이다.

reference를 참고하면 /config 경로밑에 있는 application-{profile}. yml을 찾아서 읽어주는 기능이 있다.

* 위에 yaml 파일로 쓴 것에 특이사항이 있다. 문자열 데이터 양 옆에 쌍 따옴표가 붙는 것이다. (eqp1 -> "eqp1")

뭐 이정도는 괜찮다고 본다.

또 이상하게 붙는 것이 ---으로 시작하는 것이다.

yml에서 ---은 구분자인데 저절로 생긴다.

이번에 테스트해보고 위의 문제뿐만 아니라 다른 문제점이 있었다.

기존에 작성한 yml파일에 설정 값에 대한 주석(#장비명)이 있었는데 아무래도 object를 yml 형식으로 쓰다 보니 해당 정보는 다 날아갔다. (방법이 없는 것 같다... 어디서는 stream으로 다 읽고 쓰기도 하더라...)

그리고 한 파일로 전체를 관리해야한다는 것이다.

server:
  address: 192.168.0.115

name: eqp1
maxCount: 5
threshold: 90
associated:
  name: cha
  number: 1

만약에 위와 같이 yml파일을 작성했다면 클래스 파일에서는 불필요하게(?) server 클래스를 만들고 address도 만들고, 설정값이 더 많다면 엄청나게 많은 클래스를 만들어야 할 것이다.

뭐 yml파일을 분리해서 관리할 것이다! 라고하면 그렇게 해도 좋으나 필자는 한 파일에서 모든 것을 설정하고 관리하기를 바라므로 다른 방법을 생각해야 할 것 같다...

폐쇄망만 아니면 스프링 클라우드 컨피그를 쓴다든지, 디비를 사용하게 해주면 디비에 쓴다든지 했을 텐데 아쉽다.

끝.