[Spring boot] 나만의 환경 설정 만들기

지난 포스트에서 자동 환경 설정과 개발, 프로덕션, 테스트 환경을 나누는 방법에 대해 포스팅하였습니다. 이번 포스트는 그거에 이어서, 나만의 환경 설정 파일을 만드는 방법에 대해 알아보겠습니다.

 

@Value

우리는 환경설정에서 Property의 키를 사용하여 특정한 값을 호출할 수 있었습니다. 하지만 이 Key로 우리가 무엇을 할 것인지를 프로그래밍 코드에서 구현해야 하며, 일단 그러기 위해서는 우리는 이 값을 어떻게 가져올 수 있는지를 알아야 합니다.

그럴 때 사용하는 것이 @Value 어노테이션입니다.

# application.yml

server:
  port: 8080
spring:
  profiles:
    active: dev
    
property:
  app:
    hello: hello world

먼저 우리는 서버의 메인 메시지에 대한 Property를 간단하게 만들어본다고 생각하고, 위와 같이 코드를 YAML 코드를 추가해보겠습니다.

여기서 우리가 가져와야 할 값은 'hello world'이고, MainController에서 아래와 같이 코드를 작성하면 됩니다.

 

 

그런 다음, 바로 서버를 실행하고, 설정된 환경 포트 주소로 접속하면....

이렇게 Property의 값을 읽어오는 것을 알 수 있습니다.

 

YAML과 @Value

지난 포스트에서 우리는 Property보다 가급적 YAML을 사용한다고 말했습니다. 그 이유는 YAML이 더 다양한 기능을 지원하기 때문인데, 실제로 Property는 Key-value라는 단순 값의 형태라면, YAML은 리스트 등 한 Key의 복수값을 담을 수 있는 등의 형태도 지원합니다.

또, @Value에서는 기본값 지정 옵션, 이를테면 만약, 환경 설정 파일에 해당하는 값이 Required인데, 만약 값이 없다면? 기본값으로 매꾸는 게 현명하겠죠? 이런 방법도 있으니, 차근차근 다뤄보도록 하죠.

# application.yml

server:
  port: 8080
spring:
  profiles:
    active: dev

property:
  app:
    name: Neon K.I.D Blog Example
propertyList: Java,Kotlin,Python

Property 코드를 위와 같이 작성해보도록 하겠습니다. 이번에는 리스트도 포함되어 있죠?

이번에는 Tests 클래스를 이용해서 테스트해보도록 하겠습니다. 이 코드에 작성한 뒤, 빌드를 하게 되면, 여러분이 지정한 코드대로 테스트가 진행되었다면, True 그렇지 않으면 False를 출력시켜줍니다.

 

 

우리는 이러한 코드를 테스트 코드라고 이야기 합니다. 테스트 코드에 대해서는 다른 포스트에서 자세히 한 번 더 다루도록 하겠습니다.

간단히 설명을 드리자면, assert 함수는 파라미터에 있는 인자의 조건이 True이면 통과, 그렇지 않으면 테스트 실패가 발생하게 되는 간단한 함수입니다. 배열 값을 사용할 때는 ArrayEquals, 그 외의 Collections에서는 assertEquals를 사용하면 됩니다.

그리고, Property Value를 줄 떄는 $라는 특수 문자를 사용하였는데, 중간에 보면 #을 사용한 것이 보일 것입니다. 이는 SpEL이라고 합니다. 

 

What is SpEL?

SpEL은 Spring Expression Language의 약자로 스프링 표현 언어라고 합니다. 런타임 객체 참조에 대해 질의하고 조작하는 기능을 지원하는 언어입니다.

쉽게 말하자면, 우리가 위에서 짰던 YAML 코드처럼 리스트나 String이 주어지게 되면, 해당 객체를 런타임시 참조하게 되는데, 그 때 사용하는 메소드 등을 호출하고, 기본 문자열 템플릿 기능을 지원함으로써, 다양한 컬렉션 알고리즘을 통해 프로그래밍 구현에 다양성을 포함시킨 것이죠.

이는 @Value 어노테이션에서만 사용할 수 있습니다.

 

성공적으로 모든 결과가 정확하게 출력되었다면, exit code 0을 반환합니다. 만약 실패한다면, exit code가 0이 아닌 다른 값으로 표시되며, 빨간 화면이 표시될 것입니다.

여기서 주의할 점은 YAML 파일에서 Array나 List를 이용할 경우, 각 요소들에 공백을 주지마세요. 공백을 주게 될 경우, Java 코드에서는 그대로 변수 값에 반영되어, 테스트 중에 문제가 있을 수 있습니다.

여기까지 간단하게 @Value 어노테이션과 함께 SpEL을 사용해봤습니다. 간단히 요약하자면, YAML 파일에서 설정한 키값을 @Value의 프로퍼티 값으로 주게 되면 해당 값이 필드값에 할당되는 방식으로 운영자가 한 개의 설정 파일을 건드려서 나만의 서버 커스텀 환경 설정을 구현할 수 있는 것이 키포인트입니다. 즉, 가능한 프로그래밍 코드를 건드리지 않고, 서버를 설정할 수 있도록 하는 목적을 지니고 있죠.

 

@ConfigurationProperties

좀 더 고급적인 사용법으로는 ConfigurationProperties 어노테이션을 볼 수 있습니다. 이 어노테이션은 다양한 자료형의 Property 값을 매핑할 수 있도록 해주는 녀석인데, 이 녀석을 사용하려면 몇 가지 제약을 따라야 합니다.

  • ConfigurationProperties는 Root Prefix를 활용하여 원하는 객체에 바인딩 (Root Prefix 필수)
  • List, Map과 같은 다수의 자료를 담는 사용자 정의 Property이기 때문에 클래스를 별도로 생성해야 함.
  • 해당 Property 사용을 위해 DI(의존성 주입)은 필수, 따라서 @Component or @ConstructorBinding 어노테이션을 선언해야 함.
  • ConfigurationProperties를 활용해 클래스의 List or Map 필드로 바인딩 되므로, data class 등을 이용하여야 함.
  • Application 클래스에서 @EnableConfigurationProperties 어노테이션으로 Property 클래스를 선언해야 함.

제약 사항이 조금 많은 편이지만, 대부분은 Spring에서 한 클래스를 사용하기 위해 기본적으로 해야하는 일을 명시한 것이 대부분입니다. 특히, 마지막 제약 사항의 경우, Java에서는 Lombok이라는 라이브러리를 사용해서도 쉽게 구현할 수 있고, Kotlin에서는 언어에서 자체적으로 data class를 지원해주기 때문에 어렵지 않게 구현할 수 있습니다.

4번 제약 사항의 경우는 최근에 개선된 사항인데, 기존 Property는 Mutable한 값이어서, 중간에 프로그래밍에 의해 바뀔 수도 있어, 위험성이 컸는데, 특히 Java 언어에서 사용되는 Lombok 이나 setter를 이용한 바인딩은 가변성 특징을 가지고 있어, 데이터 변조의 위험이 있었죠.

하지만 Kotlin에서는 접근 지정자 등으로 가변성, 불변성을 제어할 수 있기 때문에 이러한 불편함을 줄일 수 있지만, 기존 Spring/Spring boot에서는 가변성만 제공하였었죠. 그걸 개선한 것이 @ConstructorBinding 입니다. 따라서 이 어노테이션을 이용하면, 불변성인 Property를 생성할 수 있습니다.

5번 제약 사항의 경우, 과거에는 @ConfigurationPropertiesScan 이라는 어노테이션을 이용하여 Property를 자동으로 Scan 해주는 기능을 가지고 있었지만 Spring boot 2.2.1 버전부터는 일부 신규 어노테이션과의 문제로 인하여 disabled 되어 지금은 수동으로 Property 클래스를 명시해 줘야 합니다.

그럼 구현을 한 번 해보도록 하죠.

# application.yml

server:
  port: 8080
spring:
  profiles:
    active: dev

language:
  list:
    - name: C++
      type: Object-Oriented
    - name: F#
      type: Functional
    - name: PowerShell
      type: script

기존의 property를 지우고, 프로그래밍 언어와 각 프로그래밍 언어의 타입을 나타내는 맵 리스트를 한 개 만들어봤습니다.

LangProperty 라는 이름으로 여러분들의 정의한 Property의 클래스를 만들어주도록 합니다.

data class를 이용하여 위와 같이 불변성 Property를 바인딩할 클래스를 생성해주시면 됩니다. 마지막으로 어떤 Property인지 Prefix를 명시해줍시다.

그리고 Application 클래스에 Property의 사용을 선언해줍니다.

테스트 코드는 위와 같이 작성해보도록 하고, 애플리케이션을 실행하면...

이렇게 성공하는 모습을 볼 수 있습니다. 그런데, 테스트 코드가 썩 보기 좋진 않죠. 애플리케이션 내에서는 Map을 사용함으로써 데이터를 주무르는 게 편하지만, 출력할 때는 그렇게 좋은 모습은 아닌 것 같네요. 

이럴 때는 POJO 타입이 좋습니다. POJO 타입은 List<Map>보다 더 직관적이고, 더 명확하게 객체를 구성할 수 있어, 우리가 실제로 Spring boot에서 REST API를 구현할 때 많이 사용하게 될 것입니다.

 

POJO

POJO는 Plain Old Java Object의 약자입니다. 음 Old가 붙어 있으면 좀 오래되고 안써야 되는 게 맞지 않을까요? 라는 분들도 계실 수 있을 것입니다. 

하지만 POJO는 특정 환경에 종속되지도 않고, 특히 단일 책임의 원칙이라는 것이 잘 되어 있어, 객체 지향적이고 좋습니다. 단일 책임의 원칙이란 것은 한 클래스에서 각기 책임과 역할이 명확하게 정해져 있는 원칙이죠. 실제로 저는 이 원칙 때문에 OOP를 선호하는 편이기도 합니다. 

DB랑 연동할 때도 많은 장점을 가지고 있는 착한 녀석이니, 조금씩 다뤄보시면서 익혀 나가시면 그 장점이 더욱 잘 보일 것이라고 생각합니다.

 

자 그럼 POJO 형태로 한 번 만들어보도록 하겠습니다.

 

실제로 알고리즘을 짤 때도, Map을 쓰기 싫으면 저는 보통 이렇게 하는 경우가 있는데, data class를 사용하게 되면, 이렇게 심플하게 만들 수 있습니다.

이제 Map 대신, POJO 타입의 리스트로 넣어주면 되겠죠? 

이 다음 과제는 위의 테스트 코드를 여러분들이 짜보는 것입니다. Map 대신 클래스를 이용한 것이니, 쉽게 짜보실 수 있을 것입니다.

comments powered by Disqus

Tistory Comments 0