스프링부트 외부설정

Shaun·2023년 3월 22일
2

SpringBoot

목록 보기
19/20
post-thumbnail

외부설정

  • 하나의 애플리케이션을 여러 다른 환경에서 사용할때 사용 (Ex: 개발서버,운영서버)

과거

  • 예전 방식은 그 애플리케이션에 해당하는 외부 설정값을 넣어준뒤 빌드해서 사용한다

외부 설정 방법

  • OS환경 변수
  • 자바 시스템 속성 : JVM안에서 사용
  • 자바 커맨드 라인 인수 : 커맨드 라인에서 전달하는 외부설정, 실행시 main(args)에서 사용
  • 외부파일: 외부파일 직접읽어서 사용

-> 각각의 외부설정 방식은 따로 존재하지만 여기서는 설명하지 않겠다.

외부설정 - 스프링 통합

  • 각각의 외부 설정 방식이 다르다. 한가지 방법을 사용하다가 다른방법으로 변경시에 해당하는 코드들을 다 바꿔야한다. 이를 스프링에서는 어떻게 통합했는지 살펴보자

  • 스프링은 이 문제를 Environment와 PropertySource 로 해결한다

PropertySource

  • 스프링은 로딩 시점에 필요한 PropertySource를 생성하고 Environmnet에서 사용할 수 있도록 연결해둔다.

Environment

  • Environment 를 통해서 특정 외부 설정에 종속되지 않고, 일관성 있게 key=value 형식의 외부 설정에 접근할 수 있다

  • environment.getProperty(key) 를 통해서 값을 조회할 수 있다.

  • 같은 값이 있을 경우를 대비해 스프링에서는 우선순위를 정해 놨다.

  • 우리가 자주 사용하는 application.properties/ yml 도 PropertySource에 추가된다. 따라서 Environment 를 통해 접근 가능

우선순위

1.커맨드 라인 옵션인수

--url=proddb --username=prod_user --password=prod_pw

2.자바 시스템 속성

-Durl=devdb -Dusername=dev_user -Dpassword=dev_pw
  • 설정 방법을 다르게 해서 값을 Environment를 통해 읽으면 무슨 값이 나올까? -> 1번
  • 유연한 것이 우선권을 가진다.
  • 범위가 넓은 것 보다 좁은 것이 우선권을 가진다.

-> JVM안에서 모두 접근할수 있는 반면 커맨드 라인 옵션 인수는 main(args) 통해 들어오기 떄문에 접근 범위가 더 좁다.

설정데이터v1 - 외부파일(properties,yml)

 url=dev.db.com
  username=dev_user
  password=dev_pw
  • 지금까지 알아본 방법들은 외부설정 값이 늘어 날수록 관리가 어렵다

  • 그래서 외부 설정값들을 하나의 파일에 넣어 관리하는 방법을 사용한다.

  • properties는 캐밥 표기법 사용

  • 로딩 시점에 각각에 해당하는 환경에 따라 다르게 읽는다(개발,운영)

스프링과 외부데이터 설정

  • applicaion.properties 를 만들어주면 스프링에서는 이 파일을 읽어서 사용할 수 있는 PropertySource의 구현체를 제공함

  • 당연히 Environment 를 통해서도 조회 가능

  • 하지만 이렇게 외부파일은 파일자체 관리 문제가 생김, 예를들어 변경이력, 변경이력이 코드에 어떤 영향을 주는지 등등

설정데이터v2 - 내부파일(properties,yml)

  • yml,properties를 내부에 두는 방식

  • 이 방식은 설정파일의 변경사항도 함께 배포 가능

  • 개발,운영 설정파일을 같이 빌드하고 외부설정값으로 읽을 설정파일을 정함

  • 이 두 설정 파일을 구분하기 위해 스프링은 프로필 이라는 개념 지원

spring.profiles.active=dev -> application-dev.properties
spring.profiles.active=prod -> application-prod.properties

설정데이터 v3 - 내부파일 합체

spring.config.activate.on-profile=dev
  url=dev.db.com
  username=dev_user
  password=dev_pw
#---
  spring.config.activate.on-profile=prod
  url=prod.db.com
  username=prod_user
  password=prod_pw
  • 스프링은 application.properties 파일 안에서 논리적으로 영역을 구분하는 방법 제공

    properties : #--- 또는 !--
    yml : ---

  • 별거없다, 그냥 개발,운영 외부설정 파일을 함께 관리 하는 방법

  • 프로필에 값을 주지 않으면 기본은 Default라는 값이 들어옴

우선순위

url=local.db.com
  username=local_user
  password=local_pw
  #---
  spring.config.activate.on-profile=dev
  url=dev.db.com
  username=dev_user
  password=dev_pw
  #---
  spring.config.activate.on-profile=prod
  url=prod.db.com
  username=prod_user
  password=prod_pw
  • 코드는 위에서 아래로 실행된다 그러므로 외부 옵션을 주지 않을 경우 profile은 Default로 설정되며 맨위의 외부 설정값 들이 나온다.
  • 하지만 외부 옵션을 dev로주는 경우는 dev의 외부 설정값 들이 나옴
  • 외부옵션 > 코드흐름

속성 부분 적용

 url=local.db.com
  username=local_user
  password=local_pw
  #---
  spring.config.activate.on-profile=dev
  url=dev.db.com
  • Dev 프로필을 사용하면 해당하는 데이터 값만 데이터 프로필로 변경된다.

스프링이 지원하는 다양한 외부 설정 조회 방법

  • Environment
  • @Value
  • @ConfigurationProperties

Environment

@Slf4j
    @Configuration
    public class MyDataSourceEnvConfig {
        private final Environment env;
        public MyDataSourceEnvConfig(Environment env) {
            this.env = env;
}
        @Bean
        public MyDataSource myDataSource() {
            String url = env.getProperty("my.datasource.url");
            String username = env.getProperty("my.datasource.username");
            String password = env.getProperty("my.datasource.password");
               Integer.class);
          Duration timeout = env.getProperty("my.datasource.etc.timeout",
  Duration.class);
          List<String> options = env.getProperty("my.datasource.etc.options",
  List.class);
          return new MyDataSource(url, username, password, maxConnection,
  timeout, options);
  • MyDataSource를 스프링 빈으로 등록

  • Environment.getProperty(key,type) -> 타입정보를 주면 해당 타입으로 변환시켜줌

  • application.properties 생성 -> Environment 를 통해 외부설정 값 조회 -> MyDataSource 만듬

-> 이 방법은 직접 Env를 사용해야 한다는 단점이 있다. 이를 해결하기 위해 스프링은 @Value라는 애노테이션 제공!

외부설정 조회 @Value

@Configuration
  public class MyDataSourceValueConfig {
      @Value("${my.datasource.url}")
      private String url;
      @Value("${my.datasource.username}")
      private String username;
      @Value("${my.datasource.password}")
      private String password;
      
      @Bean
      public MyDataSource myDataSource1() {
                 return new MyDataSource(url, username, password);
}
  • @Value 애노테이션을 사용해 외부설정값을 쉽게 불러올 수 있다.
@Bean
      public MyDataSource myDataSource2(
              @Value("${my.datasource.url}") String url,
              @Value("${my.datasource.username}") String username,
              @Value("${my.datasource.password}") String password,
              @Value("${my.datasource.etc.max-connection}") int maxConnection,
              @Value("${my.datasource.etc.timeout}") Duration timeout,
              @Value("${my.datasource.etc.options}") List<String> options) {
          return new MyDataSource(url, username, password, maxConnection,
  timeout, options);
}
  • 이런식으로 파라미터를 통해서도 받을 수 있다.
  • 값이 없는경우 기본값을 사용할 수 있다.

    ex) @Value("${my.datasource.etc.max-connection:1}")

-> @Value 방식도 편하지만 ,하나하나 외부 설정 값들을 불러와야 한다는 단점이 있다. 정보의 묶음, 즉 객체로 변환해서 사용해보자

외부설정 조회 @ConfigurationProperties

  • 스프링은 외부 설정을 묶음, 즉 객체로 변환하는 기능을 제공한다.

  • 객체를 사용하면 타입을 사용할 수 있어서 잘못된 타입이 들어오는 문제도 방지할 수 있다.

  • 외부설정을 자바코드로 관리하는법

예시

@Data
  @ConfigurationProperties("my.datasource")
  public class MyDataSourcePropertiesV1 {
      private String url;
      private String username;
      private String password;
      private Etc etc = new Etc();
      @Data
      public static class Etc {
          private int maxConnection;
          private Duration timeout;
          private List<String> options = new ArrayList<>();
          }
  • @ConfigurationProperties가 있으면 외부 설정을 주입 받는 객체라는 뜻이다.

  • 여기서 외부 설정 key의 묶음 시작점인 my.datasource를 적어준다.

@EnableConfigurationProperties(MyDataSourcePropertiesV1.class)
  public class MyDataSourceConfigV1 {
      private final MyDataSourcePropertiesV1 properties;
      public MyDataSourceConfigV1(MyDataSourcePropertiesV1 properties) {
          this.properties = properties;
}
      @Bean
      public MyDataSource dataSource() {
          return new MyDataSource(
                  properties.getUrl(),
 } }
properties.getUsername(),
properties.getPassword(),
properties.getEtc().getMaxConnection(),
properties.getEtc().getTimeout(),
properties.getEtc().getOptions());
  • @EnableConfigurationProperties(외부설정객체.class) 로 스프링에게 사용할 @ConfigurationProperties를 지정해준다. 빈으로 자동 등록 되서 주입받아 사용 가능하다.

  • 이 방법은 하나하나 직접 등록 할때고 , 특정 범위로 자동 등록할 때는 @ConfigurationPropertiesScan 을 사용하면 된다.

@ConfiugrationProperties 생성자, 범위해결

  • 외부설정은 초기화때 한번 설정되고 변경되서는 안된다. 하지만 위에 예제를 보면 setter가 존재 함으로 변경될 가능성이 있다. 자바빈 프로퍼티 방식이 아니라 생성자를 통해서 객체를 만들자

  • application.properties 에 필요한 외부 설정을 추가하고, @ConfigurationProperties 의 생성자 주입을 통해서 값을 읽어들였다. Setter 가 없으므로 개발자가 중간에 실수로 값을 변경하는 문제가 발생하지 않는다.

  • 타입검증은 가능하지만 범위에 대한 해결책은 아직 없다. 이문제에 대해서는 @Validation을 사용해 해결해주자. 이 글에서는 @Validation에 대해 자세히 다루지 않겠다.

YML

  • application.properties , application.yml 을 같이 사용하면 application.properties가 우선권을 가진다.
my:
    datasource:
      url: local.db.com
      username: local_user
      password: local_pw
      etc:
        maxConnection: 2
        timeout: 60s
        options: LOCAL, CACHE
--- 
spring:
    config:
      activate:
        on-profile: dev
  my:
    datasource:
      url: dev.db.com
      username: dev_user
      password: dev_pw
      etc:
        maxConnection: 10
        timeout: 60s
        options: DEV, CACHE
--- 
spring:
    config:
      activate:
        on-profile: prod
  my:
    datasource:
      url: prod.db.com
      username: prod_user
      password: prod_pw
  • yml도 ---를 사용해 여러개의 설정값 설정 가능

  • spring.config.active.on-profile을 통해 프로필 설정 가능
    -> 외부설정값에 따라 설정데이터들이 달라짐

@Profile

 @Slf4j
  @Configuration
  public class PayConfig {
      @Bean
      @Profile("default")
      public LocalPayClient localPayClient() {
		  log.info("LocalPayClient 빈 등록");
          return new LocalPayClient();
      }
      @Bean
      @Profile("prod")
      public ProdPayClient prodPayClient() {
		  log.info("ProdPayClient 빈 등록");
          return new ProdPayClient();
      }
 @Component
  @RequiredArgsConstructor
  public class OrderRunner implements ApplicationRunner {
      private final OrderService orderService;
      @Override
      public void run(ApplicationArguments args) throws Exception {
          orderService.order(1000);
      }
}
  • 하나의 인터페이의 구현체가 각각 다른 상황

  • 환경마다 설정값이 다른것은 해결했다. @Profile외부 설정값에 따른 빈등록을 할때 사용

  • 외부 설정값을 주지 않으면 default, prod로 주면 두번째 코드가 빈 등록이 된다.

  • 외부설정 값에 따라 빈등록이 달라짐

  • ApplicationRunner 인터페이스를 사용하면 스프링은 빈 초기화가 모두 끝나고 애플리케이션 로딩이완료되는 시점에 run(args) 메서드를 호출

profile
호주쉐프에서 개발자까지..

0개의 댓글