부트 개념과 활용-09.외부 설정 1부


외부 설정

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config

사용할 수 있는 외부 설정

  • properties
  • YAML
  • 환경 변수
  • 커맨드 라인 아규먼트

프로퍼티 우선 순위

  1. 유저 홈 디렉토리에 있는 spring-boot-dev-tools.properties
  2. 테스트에 있는 @TestPropertySource
  3. @SpringBootTest 애노테이션의 properties 애트리뷰트
  4. 커맨드 라인 아규먼트
  5. SPRING_APPLICATION_JSON (환경 변수 또는 시스템 프로퍼티) 에 들어있는 프로퍼티
  6. ServletConfig 파라미터
  7. ServletContext 파라미터
  8. java:comp/env JNDI 애트리뷰트
  9. System.getProperties() 자바 시스템 프로퍼티
  10. OS 환경 변수
  11. RandomValuePropertySource
  12. JAR 밖에 있는 특정 프로파일용 application properties
  13. JAR 안에 있는 특정 프로파일용 application properties
  14. JAR 밖에 있는 application properties
  15. JAR 안에 있는 application properties
  16. @PropertySource
  17. 기본 프로퍼티 (SpringApplication.setDefaultProperties)

application.properties 우선 순위 (높은게 낮은걸 덮어 씁니다.)

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

참고. 랜덤값 설정하기

  • ${random.*}
  • 플레이스 홀더

    name = keesun
    fullName = ${name} baik

15.어플리케이션 프로퍼티 사용

#15번째 순위
people.name = real-jaeuk 15
@Component
public class SampleRunner implements ApplicationRunner {

    @Value("{people.name}")
    private String name;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("=============== real application ===============");
        System.out.println("people.name : " + name); // real-jaeuk 15
        System.out.println("=============== real application ===============");
    }
}
  1. @SpringBootTest 애노테이션의 properties 애트리뷰트

test 디렉토티에 resuorce 디렉토리 만들고

프로젝트 구조 (file-project structure) 설정

모듈 설정

프로퍼티 - 소스 탭

해당 경로 (src-test-resuorce)를 테스트 리소스 경로로 지정해 준다.

application.properties

#3번째 순위
people.name = real-jaeuk 3
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
class PropertiesApplicationTests {

    @Autowired
    Environment environment;

    @Test
    void contextLoads() {
        System.out.println("=============== test application ===============");
        System.out.println("people.name : " + environment.getProperty("people.name")); // real-jaeuk 3
        System.out.println("=============== test application ===============");
    }
}

테스트를 실행하면

=============== test application ===============
people.name : real-jaeuk 3
=============== test application ===============

15 무시하고 3이 출력된다

빌드를 할때 어떤 일이 벌어지나면..

src 밑의 main 을 전부 빌드하고 classpath에다 넣습니다.
그 후에 test를 전부 컴파일(빌드)하고 classpath에다 넣습니다.
이 때 똑같은 이름의 application.properties 가 바뀝니다. (같은 이름 - 오버라이딩 해 버림)

근데 이런 방식때문에 테스트시 귀찮은 문제가 발생할 수 있다.

어떤 상황이냐면
main 프로퍼티

#15번째 순위
people.name = real-jaeuk 15 
people.age = ${random.int}               
# 범위 주는 법 ${random.int[1024, 65536]}
# 서버 포트는 랜덤쓸 시 0으로 쓸것, 사용하지 못하는 포트도 잡을 수 있다.
server.port=0

메인에는 age가 있는데 (메인을 실행할 때/메인 age를 사용할 때는 문제 없음)

하지만 테스트를 실행할 때에는(age를 사용하지 않더라도) 깨지는 걸 볼 수 있다.

에러 내용

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sampleRunner': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'people.age' in value "${people.age}"
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405)

이게 왜 에러가 나냐면, 아까 설명한 것 처럼..

src 밑의 main 을 전부 빌드하고 classpath에다 넣습니다.
그 후에 test를 전부 컴파일(빌드)하고 classpath에다 넣습니다.
이 때 똑같은 이름의 application.properties 가 바뀝니다. (같은 이름 - 오버라이딩 해 버림)

해 버리니까, age가 없어지기 때문에 에러가 나는 것.

테스트 프로퍼티에도 age를 선언해 주면 에러는 발생하지 않는다.

하지만 이런 방식보다는,
테스트 만의 프로퍼티 정의가 필요하다면, 다른 방식으로 구현이 가능하다.
(테스트만을 위한 프로퍼티 파일 생성 - 2)

값 재사용-플레이스 홀더

#15번째 순위
people.name = real-jaeuk 15 
#위에 미리 정의한 변수들은 재사용 가능 (플레이스 홀더)
people.fullName = ${people.name} choi

이미 정의한 값을 재사용할 수 있게 이렇게도 사용이 가능하다.
real-jaeuk 15 choi 이렇게 출력.

  1. @SpringBootTest 애노테이션의 properties 애트리뷰트
    @RunWith(SpringJUnit4ClassRunner.class)
    @TestPropertySource( properties = {"people.name=test-jaeuk 2", "people.age=99"})
    @SpringBootTest
    class PropertiesApplicationTests {
    
     @Autowired
     Environment environment;
    
     @Test
     void contextLoads() {
         System.out.println("=============== test application ===============");
         System.out.println("people.name : " + environment.getProperty("people.name"));
         System.out.println("people.age : " + environment.getProperty("people.age"));
         System.out.println("=============== test application ===============");
     }
    }
    
    =============== test application ===============
    people.name : test-jaeuk 2
    people.age : 99
    =============== test application ===============
    

    이렇게도 사용이 가능하고,

아까 말했던, 테스트 만의 프로퍼티 파일을 적용하기 위해서는 이름이 똑같은 application.propterites 파일을 지우고, (이름이 같으니까 오버라이딩 함)

test.properties 파일을 적용해주면 된다. (이름은 상관없음. 같지만 않으면..)

@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(locations = "classpath:/test.properties") // test.properties 파일 적용
@SpringBootTest
class PropertiesApplicationTests {

    @Autowired
    Environment environment;

    @Test
    void contextLoads() {
        System.out.println("=============== test application ===============");
        System.out.println("people.name : " + environment.getProperty("people.name"));
        System.out.println("people.fullName : " + environment.getProperty("people.fullName"));
        System.out.println("people.age : " + environment.getProperty("people.age"));
        System.out.println("=============== test application ===============");
    }
}

더 자세한 내용은 공식 레퍼런스 참조
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config




© 2019. by jaeuk