본문 바로가기
Lecture/토비의 스프링 부트 - 이해와 원리

토비의 스프링 부트 - 외부 설정을 이용한 자동 구성

by Soono991 2023. 2. 18.

💡이 포스팅은 토비님의 인프런 강의인 토비의 스프링 부트 - 이해와 원리를 수강하고 학습한 내용을 정리한 포스팅입니다.

 

토비님의 강의를 수강하며 정리한 GitHub Repository입니다.

 

GitHub - kiekk/inflearn-toby-spring-boot

Contribute to kiekk/inflearn-toby-spring-boot development by creating an account on GitHub.

github.com

 

이번 챕터에 대해 정리할 내용은 다음과 같습니다.

  • Environment Abstraction
  • PropertySource Abstraction
  • Environment Property 우선순위
  • @Value 애노테이션 값 주입, 기본 값 설정
  • PropertyPlaceHolderConfigurer

 

Environment Abstraction

Spring은 Environment Abstraction을 제공하는데, 이를 이용하여 Profile, Properties 값을 Spring Container에게 주입하고 또 Spring Container에서는 Environment 객체를 통해 Profile, Properties 값을 사용할 수 있습니다.

https://docs.spring.io/spring-framework/docs/5.3.7/reference/html/core.html#beans-environment

 

The Environment interface is an abstraction integrated in the container that models two key aspects of the application environment: profiles and properties.

Environment 인터페이스는 애플리케이션 환경의 두 가지 주요 측면인 profiles과 properties를 모델링하며 컨테이너에 추상적으로 통합되어 있습니다

 

A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or with annotations. The role of the 
Environment
 object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default.

 

프로파일이란 이름이 지정된 논리적인 bean definition 그룹이며, 활성화된 경우에만 컨테이너에 등록됩니다. bean은 XML로 정의하건 애노테이션으로 정의하건 관계없이 프로파일에 할당될 수 있습니다.

프로파일과 관련된 Environment 객체의 역할은 현재 활성화된 프로파일과 기본적으로 활성화되어야 하는 프로파일을 결정하는 것입니다.

 

Properties play an important role in almost all applications and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Map objects, and so on. The role of the Environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.

프로퍼티는 거의 모든 애플리케이션에서 중요한 역할을 하며, properties 파일, JVM 시스템 properties, 시스템 환경 변수, JNDI, 서블릿 콘텍스트 파라미터, ad-hoc Properties 객체, Map 객체 등 다양한 소스를 사용할 수 있습니다. properties와 관련된 Environment 객체의 역할은 properties 소스를 configuring 하고, 속성값을 사용하기 좋은 편리한 서비스 인터페이스를 사용자에게 제공하는 것입니다.

 

 

PropertySource Abstraction

Environment Abstraction에서 설명된 properties들을 각각 PropertySource라는 객체로 추상화를 해두었습니다.

https://docs.spring.io/spring-framework/docs/5.3.7/reference/html/core.html#beans-property-source-abstraction

Spring’s Environment abstraction provides search operations over a configurable hierarchy of property sources. Consider the following listing:

Spring의 Environment 추상화는 설정할 수 있는 계층 구조의 속성값 소스에 대한 검색 기능을 제공합니다.

 

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);

 

In the preceding snippet, we see a high-level way of asking Spring whether the my-property property is defined for the current environment. To answer this question, the Environment object performs a search over a set of PropertySource objects. A PropertySource is a simple abstraction over any source of key-value pairs, and Spring’s StandardEnvironment is configured with two PropertySource objects — one representing the set of JVM system properties (System.getProperties()) and one representing the set of system environment variables (System.getenv()).

위의 코드는 현재 환경에 my-property 속성값이 정의되어 있는지를 Spring에 물어보는 고수준 방법이라 할 수 있습니다. 이 질문에 대답하기 위해 Environment 객체는 PropertySource 객체 집합에 대해 검색 작업을 하게 됩니다. PropertySource는 키와 값이 쌍을 이루는 모든 종류의 소스를 간단히 추상화한 것이며, Spring의 StandardEnvironment는 두 개의 PropertySource 객체로 이루어져 있습니다. 둘 중 하나는 JVM system properties(System.getProperties())를 나타내고, 다른 하나는 system environment variables(System.getenv())입니다.

 

These default property sources are present for StandardEnvironment, for use in standalone applications. StandardServletEnvironment is populated with additional default property sources including servlet config and servlet context parameters. It can optionally enable a JndiPropertySource. See the javadoc for details.

이런 default property 소스들은 standalone 애플리케이션에서 사용할 수 있도록 StandardEnvironment로 제공됩니다.
StandardServletEnvironment는 여기에 servlet config와 servlet context parameters를 포함하고 있는 default property 소스를 추가한 것입니다. 그리고 선택적으로 JndiPropertySource를 활성화하는 것도 가능합니다.

 

https://docs.spring.io/spring-framework/docs/5.3.7/reference/html/core.html#beans-property-source-abstraction

구체적으로 설명하자면, StandardEnvironment를 사용하고 있을 때 env.containsProperty("my-property")를 호출하면 다음 경우에 대해 true를 리턴합니다.

  • my-property가 시스템 property인 경우
  • my-property가 현재 런타임의 환경 변수인 경우

 

The search performed is hierarchical. By default, system properties have precedence over environment variables. So, if the my-property property happens to be set in both places during a call to env.getProperty("my-property"), the system property value “wins” and is returned.
Note that property values are not merged but rather completely overridden by a preceding entry.

For a common StandardServletEnvironment, the full hierarchy is as follows, with the highest-precedence entries at the top:
ServletConfig parameters (if applicable — for example, in case of a DispatcherServlet context)
ServletContext parameters (web.xml context-param entries)
JNDI environment variables (java:comp/env/ entries)
JVM system properties (-D command-line arguments)
JVM system environment (operating system environment variables)

검색 작업은 계층구조에 맞춰 진행됩니다. 기본적으로, 시스템 프로퍼티는 환경 변수보다 더 높은 우선순위를 갖습니다.
따라서 env.getProperty("my-property")를 호출했을 때 my-property가 두 군데에 모두 설정되어 있다면, 더 높은 우선순위를 가진 system property가 리턴됩니다.
property 값이 합쳐지는 일은 발생하지 않으며, 더 우선하는 항목에 의해 오버라이드 된다는 점이 중요합니다.
일반적인 StandardServletEnvironment의 계층 구조 전체를 우선순위 순으로 나열하자면 다음과 같습니다.


1. ServletConfig parameters (해당되는 예: DispatcherServlet context)
2. ServletContext parameters (web.xml context-param entries)
3. JNDI environment variables (java:comp/env/ entries)
4. JVM system properties (-D 커맨드 라인 arguments)
5. JVM system environment (운영체제 environment variables)

 

위에 설명된 PropertySource를 종류별로 구분하면 다음과 같습니다.

 

StandardEnvironment

  • JVM System Properties
  • JVM System Environment Variables

StandardServletEnvironment

  • ServletConfig Parameters
  • ServletContext Parameters
  • JNDI Environment Variables

 

Most importantly, the entire mechanism is configurable. Perhaps you have a custom source of properties that you want to integrate into this search. To do so, implement and instantiate your own PropertySource and add it to the set of PropertySources for the current Environment. The following example shows how to do so:

 

이러한 전체 메커니즘을 config 할 수 있다는 점이 가장 중요합니다. 이런 검색 작업에 특정한 커스텀 소스를 추가하려는 사용자 요구가 있을 수 있기 때문입니다. 그렇게 하려면 커스텀 소스로서 PropertySource를 구현하고 인스턴스화한 다음, 현재 환경의 PropertySource set에 추가하면 됩니다. 다음 예제를 봅시다.

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());

 

In the preceding code, MyPropertySource has been added with highest precedence in the search. If it contains a my-property property, the property is detected and returned, in favor of any my-property property in any other PropertySource. The MutablePropertySources API exposes a number of methods that allow for precise manipulation of the set of property sources.

 

위의 코드에서 MyPropertySource는 검색 작업에 가장 높은 우선순위로 추가되었습니다. 만약 MyPropertySource에 my-property 프로퍼티가 포함되어 있다면 my-property 속성을 찾으려 할 때 해당 값이 다른 PropertySource에 있는 값보다 우선적으로 감지되어 리턴되게 됩니다. MutablePropertySources API는 property sources를 상세하게 조작할 수 있는 다양한 메서드를 제공합니다.

 

 

참고 링크

 

Spring Core Technologies - 1.13. Environment Abstraction

 

johngrib.github.io

 

 

Environment Property  우선순위

PropertySource Abstraction을 설명할 때 검색 작업은 계층구조에 맞춰 진행되며 각각 우선순위가 있다고 했는데 이에 대해서 알아보도록 하겠습니다.

 

먼저 앞서 설명했듯 공식문서에 의하면 아래와 같은 우선순위에 의해 property가 검색된다고 했습니다.

 

1. ServletConfig parameters (해당되는 예: DispatcherServlet context)
2. ServletContext parameters (web.xml context-param entries)
3. JNDI environment variables (java:comp/env/ entries)
4. JVM system properties (-D 커맨드 라인 arguments)
5. JVM system environment (운영체제 environment variables)

 

5가지를 모두 확인해볼 수는 없지만 JVM 부분에 대해 맞는지 확인해 보겠습니다.

 

위와 같이 모두 다 작성했을 경우 System Properties가 적용된 것을 확인할 수 있습니다.

여기서 System Properties를 제거하면 어떻게 될까요?

 

이번엔 System Environment가 적용된 것을 확인할 수 없습니다.

이제 System Property, System Environment 모두 제거하면 비로소 properties 파일이 적용되는 것까지 확인해 볼 수 있습니다.

 

정리하면 property는 아래와 같은 우선순위로 적용되는 것을 확인할 수 있습니다.

System Property > Environment Variable > Application Properties

 

@Value 애노테이션 값 주입, 기본 값 설정

@Value 애노테이션을 사용하면 Environment 객체에 있는 각종 property 값들을 읽어올 수 있는데, 이와 관련해서는 정리해 둔 포스팅이 있으니 참고해 보시면 좋을 것 같습니다.

 

property 값 읽기

개발에 필요한 정보들을 yml, properties 등에 작성하게 되는데, 오늘은 property 파일에 작성한 값들을 읽...

blog.naver.com

 

property 값 읽기 - 2

이전 포스팅에서 @Value, @ConfigurationProperties 어노테이션을 사용해서 property 파일의 값을 읽어...

blog.naver.com

 

 

PropertyPlaceHolderConfigurer

@Value 애노테이션에 작성한 placeHolder와 일치하는 property가 있는지 확인한 후 있을 경우 해당 값을 읽어오는데, 이 부분은 자동으로 이루어지는 것은 아니고 PropertyPlaceHolderConfigurer 객체가 이를 가능하게 하는데 이 객체를 간단하게 살펴보겠습니다.

 

PropertyPlaceHolderConfigurer에 processProperties() 메서드를 통해 작성한 placeHolder를 가지고 property를 찾는데 메서드 설명을 보면 ${}로 작성된 property placeholders를 property의 값으로  바꾼다라고 되어 있습니다.

 

그리고 placeHolder의 prefix, suffix와 valueSeparator를 설정하게 되는데 아래와 같이 valueSeparator에는 default value separator가 설정되어 있으며 default value separator는 :(콜론)인 것을 확인할 수 있습니다.

 

이러한 이유로 @Value 애노테이션에 작성한 placeHolder를 통해 실제 property 값을 읽어올 수 있었던 것이며, @Value 애노테이션에 기본값을 설정할 때 :(콜론)을 사용했어야 하는 이유에 대해서도 알았습니다.

 

 

 

댓글