-
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 같은 것
- 데이터소스 빈을 등록할 때 이런 정보를 입력해야 하는데, 빈이 자동으로 다 등록이 되어 버린다면 이런 정보를 어떻게 입력할 수 있을까?
'Spring-Boot > 스프링부트 - 핵심 원리와 활용' 카테고리의 다른 글
6. 외부설정과 프로필1 (0) 2023.05.24 4. 스프링 부트 스타터와 라이브러리 관리 (0) 2023.05.15 3. 스프링 부트와 내장 톰캣 (0) 2023.05.15 2. 웹 서버와 서블릿 컨테이너 (0) 2023.04.14 1. 스프링 부트 소개 (0) 2023.04.14