ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 5. 자동 구성(Auto Configuration)
    Spring-Boot/스프링부트 - 핵심 원리와 활용 2023. 5. 18. 17:23

    스프링의 자동구성

    - 스프링 부트는 자동 구성(Auto Configuration) 이라는 기능을 제공하는데, 일반적으로 자주 사용하는 수 많은 빈들을 자동으로 등록해주는 기능이다.

    - 'JdbcTemplate', 'DateSource', 'TransactionManager' 모두 스프링 부트가 자동 구성을 제공해서 자동으로 스프링 빈으로 등록된다.

     

    - 스프링 부트는 `spring-boot-autoconfigure` 라는 프로젝트 안에서 수 많은 자동 구성을 제공한다.

    - *주의! 깊게 이해하지 않아도된다. 대략 어떻게 동작하는지 감을 잡을 수 있는 정도면 충분하다.*

     

    @AutoConfiguration(after = DataSourceAutoConfiguration.class)
    @ConditionalOnClass({DataSource.class, JdbcTemplate.class})
    @ConditionalOnSingleCandidate(DataSource.class)
    @EnableConfigurationProperties({JdbcProperties.class})
    @Import({JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class})
    public class JdbcTemplateAutoConfiguration {
    }

    - `@AutoConfiguration` : 자동 구성을 사용하려면 이 애노테이션을 등록해야 한다.

        ㆍ자동 구성도 내부에 `@Configuration` 이 있어서 빈을 등록하는 자바 설정 파일로 사용할 수 있다.

        ㆍ`after = DataSourceAutoConfiguration.class` 

            ○ 자동 구성이 실행되는 순서를 지정할 수 있다.

             `JdbcTemplate`은 `DataSource`가 필요하기 때문에 `DataSource`를 자동으로 등록해서

                `DataSourceAutoConfiguration` 다음에 실행하도록 설정

    - `@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })`

        ㆍIF문과 유사한 기능을 제공한다. 이런 클래스가 있는 경우에만 설정이 동작한다.

        ㆍ만약 없으면 여기 있는 설정들이 모두 무효화되고, 빈도 등록되지 않는다.

        ㆍ`@ConditionalXxx` 시리즈가 있다

        ㆍ`JdbcTemplate` 은 `DataSource`, `JdbcTemplate` 라는 클래스가 있어야 동작할 수 있다.

    - `@Import` : 스프링에서 자바 설정을 추가할 때 사용한다.

     

    @Configuration( proxyBeanMethods = false )
    @ConditionalOnMissingBean({JdbcOperations.class})
    class JdbcTemplateConfiguration {
        JdbcTemplateConfiguration() {
        }
    
        @Bean
        @Primary
        JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            Template template = properties.getTemplate();
            jdbcTemplate.setFetchSize(template.getFetchSize());
            jdbcTemplate.setMaxRows(template.getMaxRows());
            if (template.getQueryTimeout() != null) {
                jdbcTemplate.setQueryTimeout((int)template.getQueryTimeout().getSeconds());
            }
    
            return jdbcTemplate;
        }
    }

    - `@Configuration` : 자바 설정 파일로 사용된다

    - `@ConditionalOnMissingBean(JdbcOperation.class)`

        ㆍ`JdbcOperations` 빈이 없을 때 동작한다.

        ㆍ`JdbcTemplate` 의 부모 인터페이스가 바로 `JdbcOperations` 이다

        ㆍ쉽게 이야기해서 `JdbcTemplate` 이 빈으로 등록되어 있지 않은 경우에만 동작한다.

        ㆍ만약 이런 기능이 없으면 내가 등록한 `JdbcTemplate`과 자동 구성이 등록하는 `JdbcTemplate` 이

            중복 등록되는 문제가 발생할 수 있다.

        ㆍ보통 개발자가 직접 빈을 등록하면 개발자가 등록한 빈을 사용하고, 자동 구성은 동작하지 않는다.

    - `JdbcTemplate` 이 몇가지 설정을 거쳐서 빈으로 등록되는 것을 확인할 수 있다.

     

    *자동 등록 설정*

    - 다음과 같은 자동 구성 기능들이 다음 빈들을 등록해준다

        ㆍ`JdbcTemplateAutoConfiguration` : `JdbcTemplate`

        ㆍ`DataSourceAutoConfiguration` : `DataSource`

        ㆍ`DataSourceTransactionManagerAutoConfiguration` : `TransactionManager`  가 스프링 빈으로 등록된 것이다.

     

    *스프링 부트가 제공하는 자동 구성(AutoConfiguration)*

    - 스프링 부트는 수 많은 자동 구성을 제공하고 `spring-boot-autoconfigure` 에 자동 구성을 모아둔다

    - 스프링 부트 프로젝트를 사용하면 `spring-boot-autoconfigure` 라이브러리는 기본적으로 사용된다.

     

    Auto Configuration - 용어

    - 주로 다음 두 용어로 변역되어 사용된다.

     

    *자동 설정*

    - `Configuration` 이라는 단어가 컴퓨터 용어에서는 환경 설정, 설정이라는 뜻으로 자주 사용된다.

    - Auto Configuration은 크게 보면 빈들을 자동으로 등록해서 스프링이 동작하는 환경을 자동으로 설정해주기 때문에 자동 설정이라는 용어도 맞다.

     

    *자동 구성*

    - `Configuration` 이라는 단어는 구성, 배치라는 뜻도 있다.

    - 예를 들어서 컴퓨터라고 하면 CPU, 메모리등을 배치해야 컴퓨터가 동작한다. 이렇게 배치하는 것을 구성이라고 한다.

    - 스프링도 스프링 실행에 필요한 빈들을 적절하게 배치해야 한다.

    - 자동 구성은 스프링 실행에 필요한 빈들을 자동으로 배치해주는 것이다.

     

    - 자동 설정, 자동 구성 두 용어 모두 맞는 말이다. 자동 설정은 넓게 사용되는 의미이고, 자동 구성은 실행에 필요한 컴포넌트 조각을 자동으로 배치한다는 더 좁은 의미에 가깝다.

        ㆍAuto Configuration 은 *자동 구성*이라는 단어를 주로 사용하고, 문맥에 따라서 자동 설정이라는 단어도 사용하겠다.

        ㆍConfiguration이 단독으로 사용될 때는 설정이라는 단어를 사용하겠다.

     

    *정리*

    - 스프링 부트가 제공하는 자동 구성 기능을 이해하려면 다음 두 가지 개념을 이해해야 한다.

        ㆍ@Conditional : 특정 조건에 맞을 때 설정이 동작하도록 한다.

        ㆍ@AutoConfiguration : 자동 구성이 어떻게 동작하는지 내부 원리 이해

     

    @Conditional

    - 같은 소스 코드인데 특정 상황일 때만 특정 빈들을 등록해서 사용하도록 도와주는 기능이 바로 `@Conditional` 이다

    - 참고로 이 기능은 스프링 부트 자동 구성에서 자주 사용한다.

     

    *Condition*

    public org.springframework.context.annotation;
    
    public interface Condition {
        boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    }

    - `matches()` 메서드가 `true` 를 반환하면 조건에 만족해서 동작하고, `false` 를 반환하면 동작하지 않는다.

    - `ConditionContext` : 스프링 컨테이너, 환경 정보등을 담고 있다.

    - `AnnotatedTypeMetadata` : 애노테이션 메타 정보를 담고 있다.

     

    *참고*

    - 스프링은 외부 설정을 추상화해서 `Environment` 로 통합했다.

    - 그래서 다음과 같은 다양한 외부 환경 설정을 `Environment` 하나로 읽을 수 있다.

    #VM Option
    #java -Dmemory=on -jar project.jar
    -Dmemory=on
    
    #Program argument
    # -- 가 있으면 스프링이 환경 정보로 사용
    #java -jar project.jar --memory=on
    --memory=on
    
    #application.properties
    #application.properties에 있으면 환경 정보로 사용
    memory=on

     

    @Conditional - 다양한 기능

    - `@Conditional(xxx.class)` 대신에 `@ConditionalOnProperty(name = "memory", havingValue = "on") 으로 대체 가능

        ㆍ환경 정보가 `memory=on` 이라는 조건에 맞으면 동작

     

    *@ConditionalOnProperty*

    package org.springframework.boot.autoconfigure.condition;
    
    @Conditional(OnPropertyCondition.class)
    public @interface ConditionalOnProperty {...}

    - `@ConditionalOnProperty` 도 내부에는 `@Conditional` 을 사용한다.

    - 그리고 그 안에는 `Condition` 인터페이스를 구현한 `OnPropertyCondition` 를 가지고 있다

     

    *@ConditionalOnXxx*

    - 스프링은 `@Conditional`과 관련해서 개발자 편리하게 사용할 수 있도록 수 많은 `@ConditionalOnXxx`를 제공

    - `@ConditionalOnClass`, `@ConditionalOnMissingClass`

        ㆍ클래스가 있는 경우 동작한다. 나머지는 그 반대

    - `@ConditionalOnBean`, `@ConditionalOnMissingBean`

        ㆍ빈이 등록되어 있는 경우 동작한다. 나머지는 그 반대

    - `@ConditionalOnProperty`

        ㆍ환경 정보가 있는 경우 동작한다.

    - `@ConditionalOnResource`

        ㆍ리소스가 있는 경우 동작한다.

    - `@ConditionalOnWebApplication`, `@ConditionalOnNotWebApplication`

        ㆍ웹 애플리케이션인 경우 동작한다.

    - `@ConditionalOnExpression`

        ㆍSpEL 표현식에 만족하는 경우 동작한다.

     

    - `@ConditionalOnXxx` 는 주로 스프링 부트 자동 구성에 사용된다.

    - 자동 구성 클래스들을 열어서 소스 코드를 확인해보면 `@ConditionalOnXxx`가 많이 사용되는 것을 확인할 수 있다.

        ㆍ`JdbcTemplateAutoConfiguration`, `DataSourceTransactionManagerAutoConfiguration`,

           `DataSourceAutoConfiguration` 

     

    *참고*

    - `@Conditional` 자체는 스프링 부트가 아니라 스프링 프레임워크의 기능이다.

    - 스프링 부트는 이 기능을 확장해서 `@ConditionalOnXxx` 를 제공한다.

     

    *정리*

    - `@Conditional` : 특정 조건에 맞을 때 설정이 동작하도록 한다.

    - `@AutoConfiguration` : 자동 구성이 어떻게 동작하는지 내부 원리 이해

     

    순수 라이브러리 만들기

    * 정리 *

    - 라이브러리를 사용하는 클라이언트 개발자 입장을 생각해보면, 라이브러리 내부에 있는 어떤 빈을 등록해야하는지 알아야 하고, 그것을 또 하나하나 빈으로 등록해야 한다.

    - 이런 부분을 자동으로 처리해주는 것이 바로 스프링 부트 자동 구성(Auto Configuration) 이다.

     

    자동 구성 라이브러리 만들기

    - `@AutoConfiguration`

        ㆍ스프링 부트가 제공하는 자동 구성 기능을 적용할 때 사용하는 애노테이션이다.

    - `@ConditionalOnProperty`

        ㆍ`memory=on` 이라는 환경 정보가 있을 때 라이브러리를 적용한다. ( 스프링 빈을 등록한다. )

        ㆍ라이브러리를 가지고 있더라도 상황에 따라서 해당 기능을 켜고 끌 수 있게 유연한 기능을 제공한다.

     

    자동 구성 대상 지정

    - 스프링 부트 자동 구성을 적용하려면, 다음 파일에 자동 구성 대상을 꼭 지정해야 한다.

    - *파일 생성*

        ㆍsrc/main/resource/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.import

    - `memory.MemoryAutoConfig` 를 패키지를 포함해서 지정해준다.

    - 스프링 부트는 시작 시점에 `org.springframework.boot.autoconfigure.AutoConfiguration.imports` 의 정보를 읽어서 자동 구성으로 사용한다. 따라서 내부에 있는 `MemoryAutoConfig` 가 자동으로 실행된다.

     

    *정리*

    - 스프링 부트가 제공하는 자동 구성 덕분에 복잡한 빈 등록이나 추가 설정 없이 단순하게 라이브러리의 추가만으로 프로젝트를 편리하게 구성할 수 있다.

    - `@ConditionalOnXxx` 덕분에 라이브러리 설정을 유연하게 제공할 수 있다.

    - 스프링 부트는 수 많은 자동 구성을 제공한다. 그 덕분에 스프링 라이브러리를 포함해서 수 많은 라이브러리를 편리하게 사용할 수 있다.

     

    자동 구성 이해1 - 스프링 부트의  동작

    - 스프링 부트 자동 구성이 동작하는 원리는 다음 순서로 확인할 수 있다.

        ㆍ`@SpringBootApplication` → `@EnableAutoConfiguration` `@Import(AutoConfigurationImportSelector.class)`

     

    *@SpringBootApplication*

    - 이름 그대로 자동 구성을 활성화 하는 기능을 제공한다.

     

    *@EnableAutoConfiguration*

    - `@Import` 는 주로 스프링 설정 정보(`@Configuration`)를 포함할 때 사용한다.

    - 그런데 `AutoConfugurationImportSelector` 를 열어보면 `@Configuration` 이 아니다

     

    자동 구성 이해2 - ImportSelector

    `@Import` 에 설정 정보를 추가하는 방법은 2가지

    - 정적인 방법 : `@Import`(클래스) 이것은 정적이다. 코드에 대상이 딱 박혀 있다. 설정으로 사용할 대상을 동적으로 변경할 수 없다.

    - 동적인 방법 : `@Import`(`ImportSelector`) 코드로 프로그래밍해서 설정으로 사용할 대상을 동적으로 선택할 수 있다.

     

    * 정적인 방법*

    - 스프링에서 다른 설정 정보를 추가하고 싶으면 다음과 같이 `@Import`를 사용하면 된다.

    @Configuraiton
    @Import({AConfig.class, BConfig.class})
    public class AppConfig {...}

     

    * 동적인 방법 *

    - 스프링은 설정 정보 대상을 동적으로 선택할 수 있는 `ImportSelector` 인터페이스를 제공한다.

    *ImportSelector*

    public interface ImportSelector {
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    }

     

    * @EnableAutoConfiguration *

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {...}

    - `AutoConfigurationImportSelector`는 `ImportSelector`의 구현체이다. 따라서 설정 정보를 동적으로 선택할 수 있다.

    - 실제로 이 코드는 모든 라이브러리에 있는 다음 경로의 파일을 확인한다.

        ㆍ`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`

     

    스프링 부트 자동 구성이 동작하는 방식

    - `@SpringBootApplication` → `@EnableAutoConfiguration` → `@Import(AutoConfigurationImportSelector.class)` →

    - `resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 파일을 영어서 설정 정보 선택

    - 해당 파일의 설정 정보가 스프링 컨테이너에 등록되고 사용

     

    정리

    스프링 부트의 자동 구성을 직접 만들어서 사용할 때는 참고

    - `@AutoConfiguration` 에 자동 구성의 순서를 지정할 수 있다.

    - `@AutoConfiguration` 도 설정 파일이다. 내부에 `@Configuration`이 있는 것을 확인할 수 있다.

        ㆍ하지만 일반 스프링 설정과 라이프사이클이 다르기 때문에 컴포넌트 스캔의 대상이 되면 안된다.

        ㆍ파일을 지정해서 사용해야 한다.

        ㆍ`resources/META-INF/spring/org.springframework.boot.autoconfiguration.imports`

        ㆍ그래서 스프링 부트가 제공하는 컴포넌트 스캔에서는 `@AutoConfiguration`을 제외하는

            `AutoConfigurationExcludeFilter` 필터가 포함되어 있다.

    - 자동 구성이 내부에서 컴포넌트 스캔을 사용하면 안된다. 대신에 자동 구성 내부에서 `@Import`는 사용할 수 있다.

     

    자동 구성을 언제 사용하는가?

    - `AutoConfiguration` 은 라이브러리를 만들어서 제공할 때 사용하고, 그 외에는 사용하는 일이 거의 없다.

    - 왜냐하면 보통 필요한 빈들을 컴포넌트 스캔하거나 직접 등록하기 때문이다.

    - 하지만 라이브러리를 만들어서 제공할 떄는 자동 구성이 유용하다.

    - 실제로 다양한 외부 라이브러리들이 자동 구성을 함께 제공한다.

     

    - 보통 이미 만들어진 라이브러리를 가져다 사용하지, 반대로 라이브러리를 만들어서 제공하는 경우는 매우 드물다.

    - 그럼 자동 구성은 왜 알아두어야 할까?

     

    - 자동 구성을 알아야 하는 진짜 이유는 개발을 진행 하다보면 사용하는 특정 빈들이 어떻게 등록된 것인지 확인이 필요할 때가 있다.

    - 이럴 때 스프링 부트의 자동 구성 코드를 읽을 수 있어야 한다.

    - 그래야 문제가 발생했을 때 대처가 가능하다.

    - 자동화는 매우 편리한 기능이지만 자동화만 믿고 있다가 실무에서 문제가 발생했을 때는 파고 들어가서 문제를 확인하는 정도는 이해해야 한다.

     

    * 남은문제 *

    - 그런데 이런 방식으로 빈이 자동 등록되면, 빈을 등록할 때  사용하는 설정 정보는 어떻게 변경해야 하는지 의문이 들 것이다.

    - 예를 들어서 DB 접속 URL, ID, PW 같은 것

    - 데이터소스 빈을 등록할 때 이런 정보를 입력해야 하는데, 빈이 자동으로 다 등록이 되어 버린다면 이런 정보를 어떻게 입력할 수 있을까?

     

     

    댓글

Designed by Tistory.