Spring Boot - property

NOWNIZ·2023년 1월 26일
0

Blah Blah

목록 보기
5/9

개요

  • Spring boot에서 Property를 사용하는 방법들에 대해서 알아봅시다!

Properties를 사용하는 이유

값들을 외부로 구성하기 때문에 동일한 애플리케이션 코드로 다른 환경에서 동작할 수 있습니다.

yml VS properties

application.yml

spring: #주석
  application:
    name: sample-app
    key: value
    servers:
     - test1.sample.com
     - test2.sample.com
     - test3.sample.com

application.properties

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

Random Value

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]

Random Value

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]}

Placeholders

app:
  name: MyApp
  description: ${app.name} is a Spring Boot application
  • 계층 구조로 표현하여 가독성이 좋음

Placeholders

app.name=MyApp
app.description=${app.name} is a Spring Boot application

Java에서 property 값을 사용하는 방법 (with thymeleaf)

application.yml

spring:
  application:
    name: sample-app

index.html

<span th:text="'spring.application.name : '+${name}"></span><br>

@Value

IndexController.class

@Controller
public class IndexController {

    @Value("${spring.application.name}")
    private String name;

    @GetMapping("/")
    public String index(Model model){
        model.addAttribute("name", name);
        return "index";
    }
}

result

index.html 확인

@ConfigurationProperties

build.gradle

implementation 'org.springframework.boot:spring-boot-configuration-processor'

SampleConfigurationProperties.class

@Configuration
@ConfigurationProperties(prefix = "spring.application")
public class SampleConfigurationProperties {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

IndexController.class

@Controller
public class IndexController {

    @Autowired
    SampleConfigurationProperties sampleConfigurationProperties;

    @GetMapping("/")
    public String index(Model model){
        model.addAttribute("name", sampleConfigurationProperties.getName());
        return "index";
    }
}

result

index.html

@Value

  • 특정 property의 값을 단일로 불러와 사용하기 편리해 보입니다

@ConfigurationProperties

  • 별도의 Class를 생성하여 prefix로 하위 계층의 값들을 불러올 수 있어 여러 property 값을 담는 객체 형태로 다룰 수 있습니다.

Property 대신 외부값을 적용하는 방법

VM Option

Intellij Run/Debug Configuration

index.html

Environment variable

Intellij Run/Debug Configuration

index.html

VM Options

  • properties에 작성된 key 이름과 동일하게 spring.application.name 이란 key 이름으로 value를 입력하면 어플리케이션 변경 없이 해당 key에 value를 주입하여 사용할 수 있습니다.

Environment variables

  • Environment Variable도 VM Option과 같이 기존 properties의 값 대신 주입하여 사용이 가능했습니다.
  • Environment Variable의 경우는 @ConfigurationProperties와 사용하면 Relaxed Binding을 지원하기 때문에 유연하게 적용이 가능합니다.

Relaxed Binding에 대해서 Document에 적힌 내용이 이해가 잘 안가서 실제로 environment variable key를 변경하며 확인을 해보니 아래와 같이 적용이 가능했습니다.

PropertyEnvironment Variables
spring.person.first-namespring.person.first-name
spring.person.firstName
spring.person.first_name
SPRING_PERSON_FIRST_NAME
SPRING_PERSON_FIRSTNAME
spring.person.firstName위 리스트와 같이 적용 가능
  • 주의할 점은 prefix로 사용되는 이름에는 kebab case 형태로 lowercase와 hyphen(-)을 사용해야 한다고 합니다. 예를 들면 위 property 이름 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의 형태로 외부에서 값을 주입하여 사용할 수 없습니다.
    • (kebab case 형태로만 사용한다면 딱히 문제가 되지는 않을 것 같습니다.)

Common

기존에는 System.getProperties()System.getenv()를 통해서 VM Option과 Environment Variable을 가져와 사용했지만 그럴 필요가 없다는 걸 확인했습니다.

그리고 놀라운 건 property에 선언이 되지 않은 값들도 @Value나 @ConfigurationProperties을 통해서 외부 값만을 불러와 사용하도록 구성이 가능했고 특정 값에 대해서 Null 체크 또한 가능해 보입니다.

property에 선언 없이 Environment Variable 로 불러와 사용하기

application.yml

spring:
  application:
    name: sample-app

java

    @Value("${HOSTNAME:HOSTNAME_NOTRESOLVED}")
    private String hostName;

index.html

    <h3 th:text="'HOSTNAME : '+${HOSTNAME}"></h3>

Intellij Run/Debug Configuration

index.html

Null Check 예시

build.gradle

implementation 'org.springframework.boot:spring-boot-starter-validation'

SampleConfigurationProperties.class

@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이기 때문에 실행이 안됩니다. 각 환경별로 필수 값이 없는 경우 배포 단계에서 실패하도록 세팅이 가능해 유용할 것 같습니다.

DevOps팀에서 애플리케이션에 값을 주입하고 있는 방법 (2021-12-23 기준)

Environment Variables

application.yml

spring:
  application:
    name: sample-app
    authoruser: hjw0426
    team-name: devops
    companyName: midasin
    birth_day: "0426"
PropertyEnvironment Variables
spring.application.nameSPRING_APPLICATION_NAME
spring.application.authoruserSPRING_APPLICATION_AUTHORUSER
spring.application.team-nameSPRING_APPLICATION_TEAMNAME
spring.application.companyNameSPRING_APPLICATION_COMAPNYNAME
spring.application.birth_daySPRING_APPLICATION_BIRTHDAY

Property의 key 이름 같은 경우 kebab case 형태(lowercase와 hyphen(-))로만 사용하면 좋을 듯 합니다.

DevOps팀에서 앞으로 진행할 방법

AWS Parameter Store

AWS Parameter Store란?

  • AWS에서 제공하는 Managed 서비스로 값을 일반 텍스트 또는 암호화된 데이터로 저장이 가능합니다.
  • Key(Name) - Value 형태로 값을 저장한다.
  • Key(Name)은 슬래시(/)를 사용하여 계층 구조를 이룰 수 있다.
    • ex) /config/service-name_environment/spring.application.name
  • Parameter Store에 저장한 값을 이용해서 ECS로 동작하는 컨테이너에 Environment Variable을 주입할 수 있습니다.
  • Spring Boot 애플리케이션에 2가지 dependency를 추가해서 Parameter Store로 직접 접근하여 데이터를 사용할 수 있도록 설정이 가능합니다.

DevOps팀에서는 앞으로 Spring Boot 애플리케이션에 2가지 dependency를 추가해서 Parameter Store로 직접 접근하여 데이터를 사용할 수 있도록 설정을 할 겁니다.

Spring Boot 설정

build.gradle

implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
implementation 'org.springframework.cloud:spring-cloud-starter-aws-parameter-store-config'

bootstrap.xml

aws:
  paramstore:
    enabled:true
    prefix: /config
    name: {APPLICATION_NAME}
    defaultContext: {SERVICE_NAME}
    profileSeparator: _
  • prefix
    • parameter store의 Key 값 가장 첫번째 구분용
    • /aaa/bbb/ccc 와 같은 구조에서 /aaa
    • default = /config
  • name
    • parameter store의 Key 값 두번째 구분용
    • /aaa/bbb/ccc 와 같은 구조에서 /bbb
    • 특정 서비스를 조회할 속성의 경로를 구성할 때 사용할 이름
      • ex) ACCA-file, PHS-judge, ATS-db-manager 와 같은 서비스 이름
    • default = spring.application.name
  • defaultContext
    • parameter store의 Key 값 두번째 구분용
    • /aaa/bbb/ccc 와 같은 구조에서 /bbb
    • name하고 다른 점은 모든 서비스에서 공유되는 속성을 정의하는 컨텍스트의 이름
      • ex) JOBDA, ACCA, PHS와 같은 애플리케이션 이름이 사용될 수 있습니다.
    • default = application
  • profileSeparator
    • 컨텍스트 이름에서 추가된 프로필을 구분하는 문자열
    • /aaa/bbb_profile/ccc와 같은 구조에서 _
    • deafult = _

AWS Parameter Store 설정

/{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팀 입장에서는 별도로 환경변수로 주입해주는 과정이 생략되어 간편합니다.

참고

profile
DevOps & Cloud Engineering

0개의 댓글