3.1 자동 구성
스프링 부트의 핵심 기능 : 자동 구성
스프링 부트의 핵심 기능은 (메이븐은 pom.xml, 그레이들은 build.gradle 파일 내용에 따라) 클래스패스, 애너테이션, 기타 자바 구성 클래스를 보고 적합한 앱으로 맞춤하는 자동 구성이다.
스프링 부트는 코드를 생성하지는 않지만, 실행과 동시에 일부 코드를 덧붙인다.
- org.springframework.web.bind.annotation.RestController 애너테이션 등 빠진 의존체를 하나씩 임포트한다.
- 클래스에 @RestController, 메서드에 @RequestMapping이 달려 있는 것을 보고 웹 스프링 부트 스타터가 필요함을 알아채고 코드에 @Grab("spring-boot-web-starter") 애너테이션을 붙인다.
- @EnableAutoConfiguration을 넣어 자동 구성을 걸고 main 메서드를 제일 마지막에 추가한다.
- 이런 빌드업(build-up)은 메모리상에서 이루어진다.
메이븐 또는 그레이들 프로젝트로 구성하고 의존체를 지정하면, 스프링 부트가 자신이 실행하려는 앱이 어떤 타입인지 판단하는데 도움이 된다.
3.1.1 특정 자동 구성 끄기
@SpringBootApplication은 사실 @Configuration, @ComponentScan, @EnableAutoConfiguration 세 개의 애너테이션을 합친 것과 동등한 애너테이션이다. 만약 어떤 자동 구성을 끄고 싶다면, @EnableAutoConfiguration의 exclude 속성값에 자동 구성 클래스를 지정하면 된다.
굳이 왜 자동 구성을 끌까?
불필요한 자동 구성을 스프링 부트에서 건너뛰기 위해서이다.
예를 들어 비웹 애플리케이션이 있다고 하자. 불필요한 자동 구성이 일어나면 이때 수행하는 의존체 구성때문에 비웹 애플리케이션이 웹 애플리케이션으로 자동 인식되는 문제가 발생할 수 있다. 그래서 아예 처음부터 웹 자동 구성에서 배제하는 것이다.
자바로 작성한 스프링 부트 앱에서는 자동 구성을 어떻게 뺄까?
@SpringBootApplication의 exclude 속성값에 제외시킬 클래스들을 지정하면 된다. @SpringBootApplication은 @Configuration, @ComponentScan, @EnableAutoConfiguration을 상속한 애너테이션이기 때문이다.
@SpringBootApplication(exclude={ActiveMQAutoConfiguration.class, DataSourceAutoConfiguration.class})
public class DemoApplication {
public static void main(String[] args){
SpringApplication.run(DemoApplication.class, args);
}
}
3.2 @EnableAutoConfiguration과 @Enable<기술명>
스프링 프레임워크와 부속 모듈(스프링 코어, 스프링 데이터, 스프링 AMQP, 스프링 통합)은 @Enable<기술명>이라는 애너테이션을 지원한다.
스프링 부트도 마찬가지이다. 자동 구성을 하기 위해 @EnableAutoConfiguration에 이 애너테이션들을 사용한다.
org.springframework.boot.autoconfigure.EnableAutoConfiguration 클래스 중
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
String[] exclude() default {};
}
@EnableAutoCongifuration - 앱에서 필요한 빈을 유추해서 구성하는 클래스
자동 구성 클래스는 클래스패스 및 앱에 정의한 빈에 따라 적용 여부가 결정되는데, org.springframework.boot.autoconfigure.EnableAutoConfigurationImportSelector 클래스가 구성할 클래스를 모두 찾아준다.
getCandidateConfiguration - EnableAutoConfigurationImportSelector 클래스에서 실제로 자동 구성을 유발하는 메서드
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
return SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()
);
}
SpringFactoriesLoader.loadFactoryNames 메서드는 spring-boot-autoconfigure JAR에 있는 META-INF/spring.factories라는 파일에 프로퍼티 형식으로 열거된 구성 클래스를 읽어 들여 List<String>타입으로 반환한다.
spring.factories 파일 - 스프링 부트가 앱 타입을 유추할 때 사용할 자동 구성 클래스를 모아둔 파일
spring.factories에는 CloudAutoConfiguration 클래스가 존재한다.
CloudAutoConfiguration 클래스 - 자동으로 클라우드 애플리케이션을 구성하는 클래스
@Configuration
@Profile("cloud")
@AutoConfigureOrder(CloudAutoConfiguration.ORDER)
@ConditionalOnClass(CloudScanConfiguration.class)
@ConditionalOnMissingBean(Cloud.class)
@ConditionalOnProperty(prefix = "spring.cloud", name = "enabled", havingValue = "true", matchIfMissing = true)
@Import(CloudScanConfiguration.class)
public class CloudAutoConfiguration {
// 클라우드 구성을 (데이터, 몽고보다) 먼저 해야 한다.
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 20;
}
- @ConditionalOnClass, @ConditionalOnMissingBean - 클라우드 앱인지 분간하는 애너테이션 (뒤에서 자세히)
- @ConditionalOnProperty - spring.cloud 프로퍼티가 enabled일 경우에만 적용하기 위해 사용한 애너테이션, @Profile에 cloud라는 값을 지정했기 때문에 이 자동 구성은 cloud 프로파일을 켰을 때만 실행된다.
- @Import - 앞서 나온 다른 애너테이션 조건이 충족될 경우 수행되는 임포트 애너테이션, spring-cloud 클래스가 클래스패스에 있으면 CloudScanConfiguration 클래스를 임포트한다.
3.3 스프링 부트 주요 기능
스프링 부트는 실행할 앱의 타입을 유추해서 자동 구성하고, 어떻게 시작해서 무엇을 보여줄지, 어떤 기능을 켜고 끌지 프로퍼티값으로 커스터마이징 가능한 고도의 맞춤형 프레임워크이다.
3.3.1 SpringApplication 클래스
SpringApplication - main 메서드에서 실행할 스프링 부트 앱의 부트스트랩 역할을 한다. 실행 클래스를 인자로 넘긴다.
커스텀 배너
스프링 부트로 앱을 실행하면 첫 부분에 스프링 부트 배너가 등장한다. org.springframework.boot.Banner 인터페이스를 구현하면 이 배너를 바꿀 수 있다.
@SpringBootApplication
public class SpringBootSimpleApplication{
public static void main(String[] args){
SpringApplication app = new SpringApplication(SpringBootSimpleApplication.class);
app.setBanner(new Banner() {
@Override
public void printBanner(Environment, Class<?> sourceClass, PrintStream out){
out.print("나만의 배너".toUpperCase());
}
});
app.run(args);
}
}
텍스트를 아스키 아트로 꾸며주는 사이트
'Text to ASCII Art Generator'를 클릭한 다음 텍스트 필드에 'My Banner'라고 입력해보자. (아무 문자열이라도 괜찮다.) Test All 버튼을 누르면 갖가지 디자인의 아스키 아트가 나오는데, 가장 마음에 드는 아스키 아트를 골라 Select&Copy 버튼을 눌러 src/main/resources 폴더에 banner.txt라는 이름의 파일로 저장하자.
그리고나서 앱을 다시 실행하면, 기본 배너 대신 클래스패스에 위치한 banner.txt 파일로 재정의하여 배너가 출력된다. 스프링 부트가 banner.txt 파일을 찾는 기본 경로는 클래스패스인데, 다른 경로로 바꿀 수도 있다. src/main/resources/META-INF/폴더에 banner.txt 파일을 만들고 application.properties 파일에 banner.location 프로퍼티를 다음과 같이 설정해주면 된다.
banner.location = classpath:/META-INF/banner.txt
배너를 표시하고 싶지 않으면?
application.properties 파일에 spring.main.banner-mode 프로퍼티를 다음과 같이 설정해주면 된다.
spring.main.banner-mode = off
혹은 org.springframework.boot.Banner.Mode 를 이용하여 setBannerMode(Mode.OFF)를 구현해도 된다.
@SpringBootApplication
public class SpringBootSimpleApplication{
public static void main(String[] args){
SpringApplication app = new SpringApplication(SpringBootSimpleApplication.class);
app.setBannerMode(Mode.OFF);
app.run(args);
}
}
3.3.2 SpringApplicationBuilder
SpringApplicationBuilder는 SpringApplication 및 ApplicationContext 인스턴스를 만드는 빌더 클래스로, 연결형 API와 계층화를 지원한다. SpringApplication이 등장한 코드들은 이 클래스로 설정할 수 있으며, 사실상 스프링 부트 앱을 구성하는 또 다른 수단이다. SpringApplicationBuilder를 꼭 써야 하는 건 아니지만, 연결형 API로 작성한 코드는 가독성이 좋아 보기 편하다.
연결형 API로 작성한 예
@SpringBootApplication
public class SpringBootSimpleApplication{
public static void main(String[] args){
new SpringApplicationBuilder()
.bannerMode(Banner.Mode.OFF)
.sources(SpringBootSimpleApplication.class)
.run(args);
}
}
계층화 예
new SpringApplicationBuilder(SpringBootApplication.class)
.child(MyConfig.class)
.runa(args);
자동 구성 제외 예
new SpringApplicationBuilder(SpringBootSimpleApplication.class)
.web(false)
.run(args);
위에서 자동 구성 대상에서 제외할 클래스는 @EnableAutoConfiguration의 exclude 파라미터에 넣는다고 했는데, 위 코드에서 web(false)도 exclude 파라미터와 같은 뜻이다.
3.3.3 애플리케이션 인자
SpringApplication.run(SpringBootSimpleApplication.class, args);
스프링 부트 앱에 인자를 넘기면, 빈에서 args에 접근할 수 있다.
args.containsOption("인자명") - <인자명>으로 넘긴 인자를 찾는다.
agrs.getNonOptionArgs() - <인자명>으로 넘긴 인자를 제외한 나머지 인자를 가져온다.
3.3.4 ApplicationRunner와 CommandLineRunner
스프링 부트에서는 앱 시동 전에 실행할 코드를 ApplicationRunner 및 CommanLineRunner 인터페이스의 run 메서드에 구현한다.
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("## > ApplicationRunner 구현체...");
log.info("info 빈에 액세스: " + info);
args.getNonOptionArgs().forEach(file -> log.info(file));
}
@Override
public void run(String... args) throws Exception {
log.info("## > CommandLineRunner 구현체...");
log.info("info 빈에 액세스: " + info);
for(String arg:args)
log.info(arg);
}
이 둘은 동일한 메서드라 동시에 다 구현할 필요는 없고, 인자를 더 세세히 조정하고싶으면 ApplicationRunner를 쓴다.
3.4 애플리케이션 구성
URL, IP, 크레덴셜, DB 접속 등 갖가지 애플리케이션 구성 정보들은 잘 보존해야한다. 많이 쓰는 데이터는 애플리케이션 안에 두기도 하지만, 구성 정보를 하드 코딩하는 것은 절대 좋지 않다. 따라서 안전하면서 배포하기 쉽게 구성을 외부화하는 편이 좋다.
앱 구성 저장 (스프링 vs 스프링 부트)
스프링
- XML 파일에 <context:property-placeholder /> 태그 사용
- @PropertySource 애너테이션으로 프로퍼티 직접 선언 또는 구성 정보가 들어 있는 파일을 가리킴
스프링 부트
- 클래스패스에 application.properties 파일을 둔다.
- 클래스패스에 application.yml 파일을 둔다.
- 환경 변수를 사용한다.
- 터미널 창에 인자로 전달한다.
스프링 부트는 앱 구성을 대부분 application.properties나 application.yml 같은 공통 파일에 정의한다. 아무것도 지정하지 않으면 각 프로퍼티는 기본값이 할당된다.
프로퍼티 값 가져오기
@Value("프로퍼티명") 또는 org.springframework.core.env.Environment 인터페이스를 통해 프로퍼티 값 가져오기
application.properties 파일에 다음과 같이 설정하면,
data.server = remoteserver:3030
앱에서 @Value로 data.server 값을 가져올 수 있다.
@Value("${data.server}")
private String server;
3.4.1 프로퍼티 설정 예
3.4.2 커스텀 프로퍼티 접두어
간단한 프로젝트를 만들어 애플리케이션 구성을 자세히 살펴보자.
2021.08.14 - [BOOK REVIEW/실전 스프링 부트 워크북] - [실전 스프링 부트 워크북] 스프링부트 애플리케이션 구성 알아보기 / 스프링 부트 프로젝트 따라하기
'BOOK REVIEW > 실전 스프링 부트 워크북' 카테고리의 다른 글
[실전 스프링 부트 워크북] CH5. 스프링과 스프링 부트 (0) | 2021.08.15 |
---|---|
[실전 스프링 부트 워크북] 스프링부트 애플리케이션 구성 알아보기 / 스프링 부트 프로젝트 따라하기 (0) | 2021.08.14 |
[실전 스프링 부트 워크북] 스프링 부트 일기 / 스프링 부트 프로젝트 따라하기 (0) | 2021.08.10 |
[실전 스프링 부트 워크북] CH2 스프링 부트 앱 첫 개발 (0) | 2021.08.10 |
[실전 스프링 부트 워크북] CH1 스프링 부트 들어가기 (0) | 2021.08.10 |