본문 바로가기

Spring/Spring

Spring boot 설정 파일 yaml 사용법 (설정 파일을 읽어서 bean으로 필요할 때 사용하는 방법)

반응형

Spring boot의 설정 파일 YAML을 사용하는 방법

대부분의 애플리케이션에서 설정과 관련된 변수들은 보통 파일에다가 쓰고 읽어오는 방식으로 프로그래밍한다.

외부에 설정파일을 넣을 수도 있고 내부적으로 프로젝트에 넣을 수도 있다.

해당 파일들은 포맷(.properties, .ini 등)도 다양하다.

스프링 부트에서도 설정에 대한 내용을 다양한 파일에 적고 읽어와 사용하는데 그 중에서 가장 적합하고 스프링 부트에서 권장하는 형식인 yaml에 대해서 간단히 설명하고 사용해본다.

왜 YAML 이어야 하는가?

-> 사람이 보기 편하다!

1
2
3
4
5
6
7
environments:
    dev:
        url: http://dev.example.com
        name: Developer Setup
    prod:
        url: http://another.example.com
        name: My Cool App


[YAML]

1
2
3
4
environments.dev.url=http://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=http://another.example.com
environments.prod.name=My Cool App


[Properties]

똑같은 설정이지만 yaml을 사용할 경우 위와 같이 들여쓰기(띄어쓰기)로 구분하여 사람이 보기가 편하다. 곧 관리가 편리하다.

또한 아래와 같이 리스트로 표현하고자 할 때는 "-" (대쉬) 하나로 쓸 수 있다.

1
2
3
4
my:
servers:
    - dev.example.com
    - another.example.com


뿐만 아니라 profile을 지정해서 환경에 따라 설정값을 다르게 가져갈 수 있는 장점이 있고, 하나의 파일에서 마치 여러개의 설정파일을 쓰는 듯하게 구분하여 사용할 수 있다. (아래처럼 "---"를 써서 구분)

1
2
3
4
5
6
7
8
9
10
11
12
server:
    address: 192.168.1.100
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: production & eu-central
server:
    address: 192.168.1.120


여러모로 장점이 많아서 잘 쓰면 아주 유용하게 사용할 수 있다.

굳이 단점을 말하자면 문법(?)이 아주 약간 엄격한 것이 단점이다. 예를들면 하위 계층을 표현할 때 tab들여쓰기가 아닌 공백문자를 써야한다든가, :(콜론) 다음에 공백 한칸이 있어야한다든가하는 문제가 있다.

이런 문제는 몇 번 삽질해보면 금방 깨닫게 되고 사용법을 아래에서 간단히 익히면 해결될 문제다.


진짜로 yaml 사용(테스트) 해보기 

example 프로젝트를 만들어 보았다.

[프로젝트 구조]

프로젝트 구조는 위와 같이 구성하였다. 기존에 있던 application.properties는 지우고 classpath인 src/main/resources에 application.yml 파일을 생성했다.

참고로 .yml = .yaml 이다. 또한 스프링부트에서는 자동으로 classpath안에 있는 설정파일을 찾게 되어있다.

위 그림은 spring-boot-starter-parent의 pom.xml의 일부로, 보다시피 application이 접두사(prefix?)로 붙은 설정파일을 classpath이하의 어떤 디렉토리에 있든 .yml, .yaml, .properties 순서로 찾아서 읽는다.

1
2
3
4
env:
  servers:
    - dev.example.com
    - prod.example.com


[application.yml]

보다시피 env 밑에 servers가 있는데 그 안에 List형태로 두 개의 String값이 들어가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.demo.config;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
@Component
@ConfigurationProperties(prefix="env")
public class TestConfig {
    
    //getter, setter
    private List<String> servers = new ArrayList<String>();
 
    public List<String> getServers() {
        return this.servers;
    }
 
    public void setServers(List<String> servers) {
        this.servers = servers;
    }
}
 


[TestConfig.java]

스프링 부트가 설정파일에서 읽어온 값을 관리할 객체를 나타낸 것이다.

@ConfigurationProperties로 설정 파일에서 읽어온 값을 해당 클래스에 바인딩할 것이고 prefix 부분을 두어서 필요한 부분만 가져올 수도 있다.

따라서 prefix인 "env" 아래에 있는 servers라는 List만 가져올 것이므로 클래스에 있는 변수의 자료형도 List<String>으로 설정해서 가져왔다.

또한 @Component로 설정해서 bean객체로 등록할 수 있게 했다.

bean객체로 등록하게되면 알다시피 스프링 부트 어디에든(?) bean객체를 주입받아 설정 값들을 사용할 수 있게 된다.

(* 참고로 스프링부트에서는 @SpringBootApplication가 쓰인 메인클래스가 있는 최상위 패키지 이하의 패키지에서 등록된 모든 Component를 스캔한다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.demo;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.example.demo.config.TestConfig;
 
@RestController
public class TestController {
    
    @Autowired
    TestConfig testConfig;
    
    @RequestMapping("/")
    public String test() {
        return testConfig.getServers().get(0+ " / " + testConfig.getServers().get(1); 
    }
}


[TestController.java]

테스트를 위해 RestController를 만들었다.

또한 아까 등록한 bean객체를 주입받기 위해 @Autowired를 사용했고 루트경로(localhost:8080)로 들어왔을 때 해당 값을 리턴해서 테스트 해보았다.

[결과] -> 잘 나왔다.


변형된 사용법

무난하게 사용하는 방법은 위에서 설명한 것과 같고 이제는 조금 변형된 yaml파일의 경우에 설정 값을 가져오는 방법을 알아보려고한다.

1. application.yml이 아닌 다른 파일을 사용할 경우

특별한 경우가 아니면 application.yml 혹은 applicationXXX.yml 이런식으로 사용하면 자동으로 찾아진다.

그러나 abc.yml 같이 이름을 다르게 지정하고 싶고 경로도 다르게 하고 싶으면 아래와 같이 쓰면된다.

1
@ConfigurationProperties(locations="classpath:abc.yml", prefix="env")


스프링부트 1.4버전 이후부터는 'locations' property가 deprecated되었다. 심지어 @PropertySource 어노테이션으로 가져오려고 해도 yml을 지원하지 않는 문제가 있다... (@PropertySource는 스프링에서도 권장하지 않는 방법임.)

대신 application.yml에 spring.config.location과 spring.config.name을 이용해서 경로와 파일이름이 다른 yml파일을 적용시킬 수 있다.

2. 여러 자료구조를 사용할 경우

바인딩이 자연스럽게 된다.

1
2
3
4
5
env:
  servers: helloworld
  obj:
    number: 10
    sec: 10s


[applicatoin.yml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.demo.config;
 
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
@Component
@ConfigurationProperties(prefix="env")
public class TestConfig {
    
    private String servers;
    private Sample obj;
    
    public String getServers() {
        return servers;
    }
    public void setServers(String servers) {
        this.servers = servers;
    }
    public Sample getObj() {
        return obj;
    }
    public void setObj(Sample obj) {
        this.obj = obj;
    }
    
}
cs

[TestConfig.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.demo.config;
 
import java.time.Duration;
 
public class Sample {
    private int number;
    private Duration sec;
    
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public Duration getSec() {
        return sec;
    }
    public void setSec(Duration sec) {
        this.sec = sec;
    }
    
}
 


[Sample.java]

[결과]

역시 잘 들어가 있다.


3. profile로 환경에 따른 설정파일 세팅하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
spring:
  profiles:
    active: local # 기본 환경 선택
 
# local 환경
---
spring:
  profiles: local
  datasource:
    data: classpath:data-h2.sql # 시작할때 실행시킬 script
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-drop
  h2:
    console:
      enabled: true
 
# 운영 환경
---
spring:
  profiles: set1
server:
  port: 8081


<출처 : http://jojoldu.tistory.com/269>

위와 같이 spring.profiles.active: local로 하면 profiles가 :local인 설정이 적용되고 set1으로하면 set1인 설정이 적용된다.

default 환경을 설정해둘 수 있고 active되는 것마다 바꾸면 되니 개발하고 배포할 때 아주 편리하게 할 수 있다.


4. 여러 파일을 하나의 yml파일로 관리하기

이것은 3번처럼 그냥 ---으로 나누면 다른 파일에서 불러온 것처럼 쓸 수 있다. (고로 생략..)


* 참고사항

- 꼭 String이 아니어도 binding이 유연하게 적용된다.

- yaml을 작성할 때는 kebab case(중간에 -를 쓰는것)를 사용하는 것을 권장한다. ex) jeong-pro.server.ip: ...

반응형
  • 익명 2018.06.24 02:06

    비밀댓글입니다

  • Favicon of https://defacto-standard.tistory.com BlogIcon defacto standard 2018.06.26 09:31 신고

    yaml은 발음이 어떻게 되나요?

  • ~_~ 2018.11.15 15:21

    yml 처음 봤는데 단번에 해결되었어요 ㅎㅎ
    검색어 application.yml 로 첫번째 링크네요 ㅎ

  • yml 2019.01.02 17:25

    덕분에 yml의 정체에 대해서 알게되었네요.
    master(git) 소스를 받아서 application.yml을 보니까
    spring:
    profile:
    active: local
    로 되어 있더라구요..

    서버에서는 저 active value값이 다르게 되어 있겠죠? product 라던가..
    운영서버 배포할 때 대형사고 나겠네요.
    이부분 제대로 확인안하면..

    제가 이해한게 맞나요? ^^;

    classpath는 굳이 연관 지을 필요는 없겠죠?
    단순히 가져다 사용하는 방법(구조 확인)에 대한 궁금증이라고 했을때요.

    • Favicon of https://jeong-pro.tistory.com BlogIcon JEONG_AMATEUR 2019.01.03 16:06 신고

      설정을 나눠서 어떤 것을 어떤 설정을 가지게 할지 정하는 것이죠.
      active: product로 바꾸고 운영서버에 올리는건 일반적인 경우 같습니다.
      테스트중인데 product로 지정하면 문제가 생길 수 있죠.
      DB가 실제 서비스되고 있는 곳에 영향을 줄 수 있는등...
      잘 이해하신 것 같습니다^^

      spring2.1.0 부터는 또 구성이 바뀌었습니다.

      spring:
      profiles:
      active:
      - config
      - external
      이렇게 적용하면
      application-config.yml에 설정된 파일을 액티브하고,
      application-external.yml에 설정된 파일을 액티브합니다.
      해당 버전이라면 확인하고 사용부탁드립니다.

  • yyaml 2020.01.21 14:36

    yml 호출 시점이 어떻게 되는지 알 수 있을까요?
    그리고 중간에 yml 값이 변경 되었을때 재 호출을 하고 싶은데 어떻게 하면 되는지 알고계신다면 알려줄수 있을까요? ㅠㅠ

    • Favicon of https://jeong-pro.tistory.com BlogIcon JEONG_AMATEUR 2020.01.21 18:35 신고

      yml을 로드하는 시점은 굉장히 일찍? applicationContext를 생성하기도 전으로 알고 있습니다. (정확히 이벤트 로그를 다 찾아본 건 아닙니다. 정확하지 않으니 주의바랍니다.)

      yml은 configuration 성격이 강하기 때문에 실행 초기에만 읽는게 보통이라 별도로 구현하셔야할 겁니다.

      만약 클라우드 환경을 구성하실 수 있으시면 spring cloud 프로젝트의 config server를 검색해보시길 바랍니다.
      간단히 설명드리면, yml파일을 git repository에서 관리하고, 값이 변경될 때마다 config server로 변경을 인지하도록 hook을 걸어 해결하는 방법입니다.

      만약 위의 방법이 불가능하다면, WatchService를 검색해보셔도 될 것 같습니다.
      간단히 설명드리면, 디렉토리에 변경을 감지하는 서비스를 만드는 것인데요.
      yml파일이 있는 디렉토리를 지켜보다가 파일 내용이 변경되면 이벤트를 줍니다.
      이때마다 jackson에서 제공하는 YmlMapper를 사용해서 .yml파일을 다시 읽어오는 식의 방법입니다.

  • 유저 2020.02.25 02:19

    locations="classpath:abc.yml",

    이 옵션 존재하는거 맞나요? 어노테이션을 까봐도 안보이는데요?

    • Favicon of https://jeong-pro.tistory.com BlogIcon JEONG_AMATEUR 2020.02.25 14:08 신고

      잘못된 지식을 전달하게 되어 죄송합니다.
      한편으로 지적 감사합니다. 덕분에 수정했습니다.
      저도 찾아보니까@ConfigurationProperties의 속성값 locations가 deprecated 되었음을 확인했습니다.
      spring.config 옵션을 이용하거나 SpringApplicationBuilder 클래스로 SpringApplication을 생성할 때, properties위치를 주는 방식으로 사용해야 다른 파일을 이용할 수 있습니다.

  • 하잉 2020.12.17 11:07

    정말 자세하고 쉬운 설명으로 yml 파일과 그 설정에 대해 잘 깨닫고 갑니다 감사합니다~!