Properties를 사용하는 이유
값들을 외부로 구성하기 때문에 동일한 애플리케이션 코드로 다른 환경에서 동작할 수 있습니다.
spring: #주석
application:
name: sample-app
key: value
servers:
- test1.sample.com
- test2.sample.com
- test3.sample.com
1spring.application.name=sample-app #주석
2spring.application.key=value
3spring.application.servers[0]=test1.sample.com
4spring.application.servers[1]=test2.sample.com
5spring.application.servers[2]=test3.sample.com
my:
secret: ${random.value}
number: ${random.int}
bignumber: ${random.long}
uuid: ${random.uuid}
number:
less:
than:
ten: ${random.int(10)}
in:
range: ${random.int[1024,65536]
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
app:
name: MyApp
description: ${app.name} is a Spring Boot application
app.name=MyApp
app.description=${app.name} is a Spring Boot application
spring:
application:
name: sample-app
<span th:text="'spring.application.name : '+${name}"></span><br>
@Controller
public class IndexController {
@Value("${spring.application.name}")
private String name;
@GetMapping("/")
public String index(Model model){
model.addAttribute("name", name);
return "index";
}
}
index.html 확인
implementation 'org.springframework.boot:spring-boot-configuration-processor'
@Configuration
@ConfigurationProperties(prefix = "spring.application")
public class SampleConfigurationProperties {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Controller
public class IndexController {
@Autowired
SampleConfigurationProperties sampleConfigurationProperties;
@GetMapping("/")
public String index(Model model){
model.addAttribute("name", sampleConfigurationProperties.getName());
return "index";
}
}
index.html
Intellij Run/Debug Configuration
index.html
Intellij Run/Debug Configuration
index.html
Relaxed Binding에 대해서 Document에 적힌 내용이 이해가 잘 안가서 실제로 environment variable key를 변경하며 확인을 해보니 아래와 같이 적용이 가능했습니다.
Property | Environment Variables |
---|---|
spring.person.first-name | spring.person.first-name |
spring.person.firstName | |
spring.person.first_name | |
SPRING_PERSON_FIRST_NAME | |
SPRING_PERSON_FIRSTNAME | |
spring.person.firstName | 위 리스트와 같이 적용 가능 |
spring.person.first-name
에서 person
이 만약 구분되어야 한다면 per_son
이나 perSon
형태가 아닌 per-son
이어야 합니다.@Value
를 어노테이션으로 사용한다면 Relaxed Binding에 제한이 있습니다.@Value("{demo.item-price}")
를 사용할 때는 demo.item-price
이나 demo.itemPrice
, DEMO_ITEMPRICE
의 형태로 외부에서 값을 주입하여 사용할 수 있지만 @Value("{demo.itemPrice}")
형태로 사용한다면 demo.item-price
하고 DEMO_ITEMPRICE
의 형태로 외부에서 값을 주입하여 사용할 수 없습니다.기존에는 System.getProperties()
와 System.getenv()
를 통해서 VM Option과 Environment Variable을 가져와 사용했지만 그럴 필요가 없다는 걸 확인했습니다.
그리고 놀라운 건 property에 선언이 되지 않은 값들도 @Value나 @ConfigurationProperties을 통해서 외부 값만을 불러와 사용하도록 구성이 가능했고 특정 값에 대해서 Null 체크 또한 가능해 보입니다.
spring:
application:
name: sample-app
@Value("${HOSTNAME:HOSTNAME_NOTRESOLVED}")
private String hostName;
<h3 th:text="'HOSTNAME : '+${HOSTNAME}"></h3>
Intellij Run/Debug Configuration
index.html
implementation 'org.springframework.boot:spring-boot-starter-validation'
@Configuration
@ConfigurationProperties(prefix = "spring.application")
@Validated
public class SampleConfigurationProperties {
private String name;
@NotNull
private String nullCheck;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNullCheck() {
return nullCheck;
}
public void setNullCheck(String nullCheck) {
this.nullCheck= nullCheck;
}
}
위 상태에서 빌드를 하고 애플리케이션을 실행하려고 하면!
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'spring.application' to com.midasin.devops.sampleapp.configurationproperties.SampleConfigurationProperties$$EnhancerBySpringCGLIB$$4855fb47 failed:
Property: spring.application.nullCheck
Value: null
Reason: 널이어서는 안됩니다
Action:
Update your application's configuration
Process finished with exit code 1
해당 값이 null이기 때문에 실행이 안됩니다. 각 환경별로 필수 값이 없는 경우 배포 단계에서 실패하도록 세팅이 가능해 유용할 것 같습니다.
spring:
application:
name: sample-app
authoruser: hjw0426
team-name: devops
companyName: midasin
birth_day: "0426"
Property | Environment Variables |
---|---|
spring.application.name | SPRING_APPLICATION_NAME |
spring.application.authoruser | SPRING_APPLICATION_AUTHORUSER |
spring.application.team-name | SPRING_APPLICATION_TEAMNAME |
spring.application.companyName | SPRING_APPLICATION_COMAPNYNAME |
spring.application.birth_day | SPRING_APPLICATION_BIRTHDAY |
Property의 key 이름 같은 경우 kebab case 형태(lowercase와 hyphen(-))로만 사용하면 좋을 듯 합니다.
AWS Parameter Store란?
dependency
를 추가해서 Parameter Store로 직접 접근하여 데이터를 사용할 수 있도록 설정이 가능합니다.DevOps팀에서는 앞으로 Spring Boot 애플리케이션에 2가지 dependency
를 추가해서 Parameter Store로 직접 접근하여 데이터를 사용할 수 있도록 설정을 할 겁니다.
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
implementation 'org.springframework.cloud:spring-cloud-starter-aws-parameter-store-config'
aws:
paramstore:
enabled:true
prefix: /config
name: {APPLICATION_NAME}
defaultContext: {SERVICE_NAME}
profileSeparator: _
/aaa/bbb/ccc
와 같은 구조에서 /aaa
값/config
/aaa/bbb/ccc
와 같은 구조에서 /bbb
값spring.application.name
/aaa/bbb/ccc
와 같은 구조에서 /bbb
값name
하고 다른 점은 모든 서비스에서 공유되는 속성을 정의하는 컨텍스트의 이름application
/aaa/bbb_profile/ccc
와 같은 구조에서 _
값_
/{prefix}/{defaultContext}{profile-separator}{spring.profiles.active}/{property}
/{prefix}/{name}{profile-separator}{spring.profiles.active}/{property}
>> example
/config/jobda_{{region}}/aws.region
/config/jobda-api_{{region}}/spring.datasource.hikari.jdbc-url
스프링 애플리케이션에서 dependency
2개와 bootstrap.yml
만 추가해 준다면 AWS Parameter Store에 있는 값들을 불러와 바로 사용할 수 있어서 DevOps팀 입장에서는 별도로 환경변수로 주입해주는 과정이 생략되어 간편합니다.