[Spring Boot] yaml 설정파일 불러오기(@ConfigurationProperties)

개요

properties를 사용하면 map처럼 key와 value 형식으로 가능하지만, list 형태로 쓰려면 prefix로 구분해야 하는 불편함이 있다. 계층형 구조를 나타내는 설정은 yaml이 매우 편리해 보인다. Spring Boot에서 yaml을 불러올 때 제약사항이 꽤 많아서 정리해본다.

예제1:Map

먼저 src/main/resource 아래에 yml 파일을 하나 생성했다.


servers.yml

servers:
    local:
        ip: 127.0.0.1
        port: 8080
    real:
        ip: xxx.xxx.xxx.xxx
        port: 80

그 다음, yml 파일을 불러올 클래스를 생성한다.


ServerHolder.java

package com.tistory.jekalmin;

import java.util.HashMap;
import java.util.Map;

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

@Component
@ConfigurationProperties(locations="classpath:servers.yml")
public class ServerHolder {

    public Map<String, Map<String, String>> servers = new HashMap<String, Map<String, String>>();

    public Map<String, Map<String, String>> getServers() {
        return servers;
    }

}

이제 외부에서 데이터가 잘 들어갔는지 확인한다.


TestController.java

package com.tistory.jekalmin;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/test")
public class TestController {

    @Resource
    ServerHolder serverHolder;

    @RequestMapping("/index")
    public void index(){
        System.out.println("servers : " + serverHolder.getServers());
    }
}


결과는 다음과 같다.

servers : {real={ip=xxx.xxx.xxx.xxx, port=80}, local={port=8080, ip=127.0.0.1}}



에제2:List

위와 같은 예제를 리스트 형식으로 바꿔보자.


server.yml

servers:
  - name: local
    ip: 127.0.0.1
    port: 8080
  - name: real
    ip: xxx.xxx.xxx.xxx
    port: 80


ServerHolder.java

package com.tistory.jekalmin;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

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

@Component
@ConfigurationProperties(locations="classpath:servers.yml")
public class ServerHolder {

    public List<Map<String, String>> servers = new ArrayList<Map<String, String>>();

    public List<Map<String, String>> getServers() {
        return servers;
    }

}


TestController.java는 그대로 사용해서 출력했더니 결과는 아래와 같았다.

servers : [{ip=127.0.0.1, port=8080, name=local}, {name=real, ip=xxx.xxx.xxx.xxx, port=80}]

간단한 예제인데도 많은 삽질을 했는데, 그 내용들은 다음과 같았다.

주의사항:
  • yaml 언어는 공백 하나에도 민감하다. 일단 하위 계층으로 내려갈 때 tab이 아닌 스페이스바를 사용하고, 콜론(:)이나 하이픈(-) 이후에 공백 한칸도 필요하다. yaml이 민감한건지 아니면 자바에서 사용한 라이브러리인 snakeyaml이 민감한건지는 잘 모르겠다.
  • 다음은 위에 ServerHolder.java에 해당하는 @ConfigurationProperties를 사용한 클래스이다. 먼저 어노테이션 안에 locations="classpath:servers.yml" 이 되기 전에 locations="servers.yml" 로 테스트를 했었는데, 유닛테스트 할때는 잘 작동하다가 Application.java를 이용해서 접근하려 하면 파일을 찾을 수 없었다.
  • ServerHolder.java 안에 servers가 List이던 Map이던 new를 꼭 시켜줘야 한다.
  • ServerHolder.java 안에 getServer()와 같은 getter가 꼭 있어야 한다.

결론

주의사항에 나열한 내용들과 yml의 민감한 문법 때문에 오늘 하루종일 삽질한 것 같다. 이 글을 읽고 계신 분들은 조금이나마 수월하게 진행 하셨으면 하는 바람이다. 그리고 지금은 Map<Strig, String>으로 받았는데, 클래스로 받을 수도 있다.


참고 : http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-yaml