Dev

Spring @Value를 통해 환경 변수를 주입받을 때 주의할 점

친환경사과 2024. 9. 16. 21:12

🍎 Spring에서 제공하는 @Value 사용 시 주의해야 할 점을 정리합니다.


📝 Kotlin - Spring 환경에서 @Value를 사용할 때 마주한 문제

1. @Value로 주입받을 변수를 선언하는 방법

- @Value를 주입받기 위해선 app.name 이 application.yml 파일에 정의되어 있다는 가정 하에 아래 두 방식을 사용해 정의될 수 있습니다.

// case 01
@Value("\${app.name}")
val appName: String = "A"

// case 02
@Value("\${app.name}")
lateinit var appName: String

// application.yml file
app:
  name: abc

- case 01의 경우, var를 사용할 때 lateinit을 사용한 이유는 Spring이 런타임에 값을 주입하기 때문입니다.

- case 02의 경우, val을 사용한 변수는 불변일 텐데 어떻게 yml file의 "abc"를 할당할 수 있는 것일까요?

    - 할당하는 시점이 다르기 때문입니다. val은 Kotlin에서 immutable 변수로, 일반적으로 선언된 이후 값을 변경할 수 없습니다.

    - 하지만 Spring Context가 초기화시점에서 값을 주입하기 때문에 초기값 "A"는 무시되고 "abc"가 할당됩니다.

 

2. 기본 값이 지정되어 있지 않을 경우 문제

🍏 상황 설명

- 제가 속해 있던 조직에서는 Develop, Staging, Production 환경에 맞춰 각각의 `application.yml` 파일이 `application-dev.yml`, `application-stg.yml`, `application-prd.yml`로 구분되어 관리되었습니다.

- 한 동료 개발자가 기능을 추가하며 `yml` 파일에 새로운 경로를 추가한 경험이 있었습니다. 문제는 그 경로를 개인적으로 사용하던 `application-mine.yml` 파일에만 추가했었다는 점입니다. 해당 개발자는 자신의 환경에서 기능이 정상적으로 동작하는 것을 확인하고 Develop 브랜치에 코드를 반영했습니다. 그러나 반영된 코드를 Pull 한 다른 개발자들이 애플리케이션을 실행하자, 그들은 곧바로 Exception을 마주하게 되었습니다.

 

- Exception이 발생한다는 것은 애플리케이션이 설정파일에 의존하는 것을 의미합니다.다른 말로 이야기하자면 설정 파일이 비즈니스와 결합해 존재한다고 할 수 있습니다.

yml file에 값을 찾을 수 없는 경우 발생하는 Exception

 

❓어떻게 하면 의존성을 끊어낼 수 있을까요?

// case 01
@Value("\${app.name:abc}"
lateinit appName: String

// case 02
@Value("\${app.name:null}"
val appName: String?

- 위와 같이 설정 파일에 변수가 존재하지 않을 때 기본 값을 정하는 방법으로 의존성을 끊어낼 수 있습니다.

- 기본값으로 주입되는 변수에 대해 처리가 늘어나긴 하겠지만 Exception이 발생되는 것을 막을 수 있습니다.

 

3. @Value에 lateinit var 사용 시 String Type은 주입할 수 있지만 Int Type은 주입할 수 없는 이유

// case 01
@Value("\${student.age}")
lateinit var age: Int // Does not work

// case 02
@Value("\${student.age}")
lateinit var age: String

 

 

❓ String은 되는데 Int는 안 되는 이유는 무엇인가요?

- case02와 같은 String Type은 특별한 변환이 필요하지 않습니다. Spring은 yml 파일에서 읽은 값을 그대로 문자열로 주입할 수 있기 때문입니다.

 

- 위와 같이 작성하게 되면 case01은 동작하지 않습니다. 그 이유는 초기화 타이밍Spring의 주입 시기 때문입니다.

 

  - 초기화 타이밍

    - lateinit 변수는 선언 후 반드시 나중에 초기화가 이뤄질 것이라고 Kotlin 컴파일러에게 알리는 것입니다. 그러나 Spring Context를 초기화하는 과정에서 lateinit 변수가 초기화되지 않은 상태일 수 있습니다. 다시 말해, Spring이 값을 주입하려 할 때 변수는 아직 준비가 되지 않은 상태일 수 있습니다.

 

  - Spring의 주입 시기

    - Spring은 @Value를 통해 설정 파일의 값을 주입할 때, 초기화되지 않은 lateinit 변수에 값을 할당하려고 하면, 초기화가 완료되지 않은 상태에서는 올바르게 값을 설정하지 못합니다. 이로 인해 lateinit 변수에는 값이 주입되지 않거나 예외가 발생할 수 있습니다.

 

❗️ 정리

- @Value에서 Int와 같은 기본 타입은 변환 과정이 필요합니다. 설정 파일에서 값을 읽어오면, 이 값은 기본적으로 문자열 형태로 읽히기 때문에, 이를 Int로 변환해야 합니다. Spring은 자동으로 타입 변환을 시도하지만, lateinit으로 선언된 변수는 초기화가 완료되지 않은 상태로 간주되어, 타입 변환 과정에서 문제가 발생합니다.


🍎 글 마무리

- @Value 기능을 (나름)깊게 알아본 시간이었습니다. 모든 동작에는 이유가 존재한다는 것을 다시금 느낍니다.

- 글에서는 나오지 않았지만 @Value는 메서드 파라미터에서 사용될 수도 있지만 지양합니다. 후에 기회가 된다면 그 이유도 2편으로 정리해 볼까 합니다.!