Spring Boot 를 이용해서 어플리케이션을 만들다보면 외부의 값을 사용해야 하는 경우가 발생한다. AWS 의 특정 컴포넌트를 사용하기 위한 secret key 가 될 수도 있고 외부 API 를 사용하기 위한 API key 가 될 수도 있다. 소스코드에 하드코딩 하기 보다는 노출되어서는 안되는 설정값들을 별도의 파일에 분리하고 파일의 값을 불러오는 형태로 사용한다면 보다 안전하게 구현할 수 있다. 설정 파일은 .gitignore 을 이용하여 깃허브에 업로드하지 않도록 한다.

.properties vs .yml

Spring Boot 어플리케이션을 생성하면 기본적으로 resources 디렉토리에 application.properties 파일이 생성되어 있다.

spring.datasource.hikari.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mariadb://localhost:3306/testdb
spring.datasource.hikari.username=root
spring.datasource.hikari.password=root

spring.datasource.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.datasource.jpa.properties.hibernate.format_sql=true

spring.datasource.jpa.show-sql=true
spring.datasource.jpa.generate-ddl=true

.properties 파일은 가독성도 떨어지며 수정하기 어렵다. .yml 파일을 사용하면 데이터를 계층 구조로 작성하여 보다 쉽게 작성할 수 있다.

spring:
  datasource:
    hikari:
      driver-class-name: org.mariadb.jdbc.Driver
      jdbc-url: jdbc:mariadb://localhost:3306/testdb
      username: root
      password: root
    jpa:
      properties:
        hibernate:
          dialect: org.hibernate.dialect.MySQL5InnoDBDialect
          format_sql: true
      show-sql: true
      generate-ddl: true

참고로 .yml 을 이용하기 위해서는 SnakeYAML 라이브러리가 classpath 에 존재해야 한다. spring-boot-starter 의존성은 이 라이브러리를 기본적으로 제공해준다.

@Value

placeholder 방식

${app.name} 과 같이 ${} 내부에 값의 위치를 적어서 @Value 에 값을 주입하는 방식이다.

application.yml

external:
  record-year: 2020
  api:
    name: kakao
    key: 123123

ExternalService.java

@Service
public class ExternalService {

    @Value("${external.record-year}")
    private String recordYear; // 2020
  
    @Value("${external.api.name}")
    private String apiName; // kakao
  
    @Value("${external.api.key}")
    private Integer apiKey; // 123123
}

SpEL 방식

SpEL 은 Spring Expression Language 의 약자이다. Spring 에서 제공하는 특수한 표현 식으로 이러한 표현 식을 이용해 @Value 에 값을 주입할 수 있다.

@SpringBootTest
public class SpELTest {

    @Value("#{1 eq 1}")
    private boolean spelBoolean; // true

    @Value("#{externalService.apiName eq 'kakao'}")
    private String spelNameString; // "true"

    @Test
    void spelTest() {
        assertThat(spelBoolean).isTrue();
        assertThat(spelNameString).isEqualTo("true");
    }
}

#{} 내부에 표현 식을 작성하여 사용한다. placeholder 방식과 다르게 다른 Spring Bean 을 참조해서 값을 주입할 수 있다. (ex. @Value("#{externalService eq 'chan'}"))

@Value 의 문제점

바로 위의 예제에서 spelNameString 의 값이 boolean 이 아닌 String 으로 받아도 문제 없이 작동하였다. 즉, 값을 호출하는 쪽에서 타입을 설정할 수 있으므로 타입 안정성 이 떨어진다.