[스프링 인 액션] 5장 구성 속성 사용하기

    5장 구성 속성 사용하기

    💻 실습 : https://github.com/cusbert/spring-in-action-5th

    🎯 이 장에서 배우는 내용

    • 자동-구성되는 빈 조정하기
    • 구성 속성을 애플리케이션 컴포넌트에 적용하기
    • 스프링 프로파일 사용하기

    5.1 자동-구성 세부 조정하기

    스프링 부트는 구성 속성(Configuration Property)을 사용하는 방법을 제공한다.
    스프링 애플리케이션 컨텍스트에서 구성 속성은 빈의 속성이다. 그리고 JVM 시스템 속성, 명령행 인자, 환경 변수 등 여러 가지 원천 속성 중에서 설정할 수 있다.

    스프링에는 다음 두 가지 형태의 구성이 있다.

    1. 빈 연결 (Bean wiring)
      컨텍스트에서 빈으로 생성되는 애플리케이션 컴포넌트 및 상호 간에 주입되는 방법을 선언하는 구성
    2. 속성 주인(Property injection)
      컨텍스트에서 빈의 속성값을 설정하는 구성

    이 두 가지 구성은 스프링 xml 구성과 자바 기반 구성 모두에서 종종 같은 곳에 선언된다.

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDataSourceBuilder()
        .setType(H2)
        .addScript("taco_schema.sql")
        .addScripts("user_data.sql", "ingredient_data.sql")
        .build();
    }

    5.1.1. 스프링 환경 추상화 이해하기

    스프링 환경 추상화(environment abstraction)는 구성 가능한 모든 속성을 한 곳에서 관리하는 개념이다. 즉 속성의 근원을 추상화하여 각 속성을 필요로 하는 빈이 스프링 자체에서 해당 속성을 사용할 수 있게 해준다.

    스프링 환경에서는 다음과 같은 속성의 근원으로부터 원천 속성을 가져온다.

    • JVM 시스템 속성
    • 운영체제의 환경 변수
    • 명령행 인자 command line argument
    • 애플리케이션의 속성 구성 파일

    그 후 스프링 환경에서는 이 속성들을 한군데로 모은 후 각 속성이 주입되는 스프링 빈을 사용할 수 있게 해준다.

    스프링 환경 추상화

    스프링 부트에 의해 자동으로 구성되는 빈들은 스프링 환경으로부터 가져온 속성들을 사용해서 구성될 수 있다.

    • server port 9090 지정하는 여러 가지 방법
    1. application.properties
        server.port=9090
    1. application.yml
        server:
            port: 9090

    3.애플리케이션을 시작할 때 명령행 인자로 server.port 속성 지정

    java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090
    1. 운영체제 환경 변수에 설정
    export SERVER_PORT=9090

    5.1.2 데이터 소스 구성하기

    • 로컬 호스트의 Mysql 데이터 베이스 application.yml 에 구성
    spring:
      datasource:
        driver-class-name: org.mysql.jdbc.Driver
        url: jdbc:mysql://localhost/tacodb
        username: sa
        password: sa
    • JNDI(Java Naming and Directory Interface) 속성 설정
    spring:
        datasource:
            jndi-name: java:/comp/env/jdbc/tacoCloudDS

    5.1.3 내장 서버 구성하기

    • JDK의 keytool 명령행 유틸리티를 사용해서 keystore 생성하기
    keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA
    server:
        port: 8443
        ssl:
            key-store: file:///path/to/mykeys.jks
            key-store-password: letmein
            key-password: letmein

    5.1.4 로깅 구성하기

    로깅 구성을 제어할 때는 classpath 루트의 (src/main/resources) 에 logback.xml 파일을 생성 할 수 있다.

    • logback 예제
    <configuration>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
                </pattern>
            </encoder>
        </appender>
        <logger name="root" level="INFO"/>
            <root level="INFO">
        <appender-ref ref="STDOUT" />
        </root>
    </configuration>

    스프링 부트를 사용하면 logback.xml 를 생성하지 않고 로깅 수준과 로그를 기록할 파일을 수정할 수 있다.

    logging:
        path: /var/logs/
        file: TacoCloud.log
        level:
            root: WARN
            org.springframework.security: DEBUG
    • /var/logs/ 에 쓰기 퍼미션을 가지고 있다면 로그 항목들이 /var/logs/TacoCloud.log 에 저장된다.
    • 스프링 2.0부터는 날짜별로 로그 파일이 남으며 지정된 일 수가 지난 로그 파일은 삭제된다.

    5.1.5 다른 속성의 값 가져오기

    다른 구성 속성으로부터 값을 가져올 수도 있다.

    greeting:
        welcome: You are using ${spring.application.name}.

    5.2 우리의 구성 속성 생성하기

    구성 속성의 올바른 주입을 지원하기 위해 스프링 부트는 @ConfigurationProperties 어노테이션을 제공한다. 어떤 스프링 빈이건 이 어노테이션이 지정되면, 해당 빈의 속성들이 스프링 환경의 속성으로부터 주입될 수 있다.

    5.2.1 구성 속성 홀더 정의하기

    @ConfigurationProperties가 컨트롤러나 특정 빈에만 사용될 수 있는 것이 아니다. @ConfigurationProperties는 구성데이터의 홀더로 사용되는 빈에 지정되는 경우가 많다.

    컨트롤러 이외의 다른 애플리케이션 클래스 외부에 구성 관련 정보를 따로 유지 할 수 있다.
    또한 여러 빈에 공통적인 구성 속성을 공유 할 수 있다.

    @Component // OrderProps 빈을 OrderController 에 주입해서 사용
    @ConfigurationProperties(prefix = "taco.orders")
    @Data
    @Validated
    public class OrderProps {
    
        @Min(value=5, message="must be between 5 and 25")
        @Max(value=25, message="must be between 5 and 25")
        private int pageSize = 20;
    }
    • @ConfigurationProperties(prefix = "taco.orders") : 접두어로 taco.orders를 갖는 @ConfigurationProperties 지정
    • @Component 가 지정되어 스프링 컴포넌트 스캔에서 OrderProps을 자동으로 찾은 후 빈으로 생성해준다.

    OrderProps와 같은 구성 속성 홀더 클래스에 특별한 것은 없다. 구성 속성 홀더는 스프링 환경으로부터 주입되는 속성들을 갖는 빈이므로 해당 속성들이 필요한 다른 빈에 주입될 수 있다.
    따라서 OrderController에서는 기존의 pageSize 속성을 제거하고 OrderProps 빈을 주입해서 사용하면 된다.

    @Slf4j
    @Controller
    @RequestMapping("/orders")
    @SessionAttributes("order")
    public class OrderController {
    
        private OrderRepository orderRepository;
        private OrderProps orderProps;
    
        public OrderController(OrderRepository orderRepository,
                               OrderProps orderProps) {
            this.orderRepository = orderRepository;
            this.orderProps = orderProps;
        }

    해당 속성이 필요한 다른 빈에서 OrderProps의 속성들을 재사용 할 수 있다. 속성 변경, 추가, 삭제가 필요할 때는 OrderProps만 변경하면 된다.

    5.2.2 구성 속성 메타데이터 선언하기

    구성 속성 메타데이터는 선택적이므로 설사 없더라도 구성 속성이 동작하는 데 문제 없다.
    그러나 메타데이터가 있으면 해당 구성 속성에 대해 최소한의 정보를 제공하므로 유용하다.

    • spring-boot-configuration-processor 스프링부트 구성 처리기 의존성 추가
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    • 커스텀 구성 속성에 관한 메타데이터를 생성하기
      프로젝트의 src/main/resources/META-INF 아래에 additional-spring-configuration-metadata.json이라는 이름의 파일을 생성한다.
    • JSON 형식으로 된 taco.orders.pageSize 속성의 메타데이터
    {
     "properties": [
      {
       "name": "taco.orders.page-size",
       "type": "int",
       "description": "Sets the maximum number of orders to display in a list."
      }
     ]
    }

    5.3 프로파일 사용해서 구성하기

    애플리케이션이 서로 다른 런타임 환경에 배포, 설치될 때는 대개 구성 명세가 달라진다.

    1. 구성 속성을 환경 변수로 지정
    • 환경 변수의 변경을 추적 관리하거나 오류가 있을 경우 변경 전으로 돌리기 힘들다.
    1. 스프링 프로파일 사용
    • 런타임 시에 활성화되는 프로파일에 따라 서로 다른 빈, 구성 클래스, 구성 속성들이 적용 또는 무시 되도록 하는 것이 프로파일이다.

    5.3.1 프로파일 특정 속성 정의하기

    특정 환경의 속성들만 포함하는 또다른 yml 이나 properties를 생성한다.
    즉, application-{프로파일 이름}.yml 또는 application-{프로파일 이름}.properties을 생성한다.

    spring:
      datasource:
        driver-class-name: org.mariadb.jdbc.Driver
        url: jdbc:mariadb://localhost:3306/tacodb
        username: sa
        password: sa
    
      jpa:
        hibernate:
          ddl-auto: update
        properties:
          hibernate:
            show_sql: true #show sql
            format_sql: true #To beautify or pretty print the SQL
    
      schema:
      data:
        - data.sql
    
    logging:
      level:
        tacos: DEBUG
        org.hibernate.type.descriptor.sql: trace

    YAML 구성에서만 가능한 방법으로 프로파일 특정 속성을 정의 할 수도 있다.
    프로파일에 공통으로 적용되는 기본 속성과 함께 프로파일 특정 속성을 application.yml에 지정할 수 있다.
    즉 특정되지 않은 기본 속성 다음에 3개의 하이픈(---) 을 추가하고 그 다음에 프로파일의 이름을 나타내는 spring.profiles 속성을 지정하면 된다.

    logging:
        level:
            tacos: DEBUG
    ---
    spring:
        profiles: prod
        datasource:
            url: jdbc:mysql://localhost/tacocloud
            username: tacouser
            password: tacopassword
    logging:
        level:
            tacos: WARN

    5.3.2 프로파일 활성화 하기

    프로파일 특정 속성들의 설정은 해당 프로파일이 활성화되어야 유효하다.
    spring.profiles.active 속성에 지정하면 활성화 할 수 있다.

    • application.yml 에 지정해서 설정 할 수 있다.
      그러나 이 방법은 개발 속성과 분리하는 장점을 살릴 수 없다.
    spring:
        profiles:
            active:
            - prod
    • 권장하는 방법은 환경 변수를 사용해서 활성화 프로파일을 설정하는 것이다.
    % export SPRING_PROFILES_ACTIVE=prod
    • jar 파일로 애플리케이션을 실행한다면 다음과 같이 명령행 인자로 활성화 프로파일을 설정 가능하다.
    % java -jar taco-cloud.jar --spring.profiles.active=prod
    • 여러 개의 프로파일을 포함하여 활성화 할 때는 쉼표(,) 를 사용해서 지정한다.
    % export SPRING_PROFILES_ACTIVE=prod,audit,ha

    5.3.3 프로파일을 사용해서 조건별로 빈 생성하기

    특정 프로파일이 활성화될 때만 생성되어야 하는 빈들이 있다고 해보자.
    이 경우에는 @Profile 어노테이션을 사용하면 지정된 프로파일에만 적합한 빈들을 나타낼 수 있다.

    • dev 환경일 때만 dataLoader 빈 생성
    @Bean
    @Profile("dev")
    public CommandLineRunner dataLoader(IngredientRepository repo,
        UserRepository userRepo, PasswordEncoder encoder) {
        ...
    }
    • dev, qu 환경일 때만 dataLoader 빈 생성
    @Bean
    @Profile({"dev", "qa"})
    public CommandLineRunner dataLoader(IngredientRepository repo,
            UserRepository userRepo, PasswordEncoder encoder) {
        ...
    }
    • prod 가 활성화 되지 않을 때만 dataLoader 빈 생성
    @Bean
    @Profile("!prod")
    public CommandLineRunner dataLoader(IngredientRepository repo,
                UserRepository userRepo, PasswordEncoder encoder) {
        ...
    }

    @Configuration 이 지정된 클래스 전체에 대해서도 사용 가능하다.

    • prod, qa 가 모두 활성화 되지 않을 때만 CommandLineRunner 빈 생성
    @Profile({"!prod", "!qa"})
    @Configuration
    public class DevelopmentConfig {
        @Bean
        public CommandLineRunner dataLoader(IngredientRepository repo,
                     UserRepository userRepo, PasswordEncoder encoder) {
            ...
        }
    }

    📌 요약

    • 스프링 빈에 @ConfigurationProperties 를 지정하면 여러 가지 원천 속성으로부터 구성 속성값의 주입을 활성화 할 수 있다.
    • 구성 속성은 명령행 인자, 환경 변수, JVM 시스템 속성, 속성 파일, YAML 파일, 커스텀 속성 등에서 설정할 수 있다.
    • 데이터 소스 URL과 로깅 수준의 지정을 포함해서 구성 속성은 스프링의 자동-구성 설정을 변경하는데 사용할 수 있다.
    • 스프링 프로파일은 활성화된 프로파일을 기반으로 구속 속성을 설정하기 위해 사용할 수 있다.

    참고

    반응형

    댓글

    Designed by JB FACTORY