[강의요약] 스프링 부트 개념과 활용 - 4부 스프링 부트 활용 (1)
개인적인 학습을 위한 Inflearn - 스프링부트 개념과 활용(백기선) 강의 요약입니다.
개념과 원리 위주로 요약합니다.
이전 글 에서 이어집니다.
4부 - 스프링부트 활용
1. 스프링 부트 활용 소개
1-1. Springboot 핵심 기능
- SpringApplication
- 외부 설정, 프로파일
- 로깅, 테스트, dev-tools
1-2. 기술 연동
- spring web mvc
- 스프링 데이터, 스프링 시큐리티
- REST API 클라이언트
2. SpringApplication 1부
스프링 로거 기본 값은 INFO 이다.
VMOption = -Ddebug (program argument = —debug) 를 주어서 변경할 수 있다
어떤 자동 설정이 적용 되었는지 debug 로그에 찍힘.
2-1. FailureAnalyzer
2-1-1. 배너 : resources/banner.txt 로 커스텀 가능.
application.properties에서 파일 위치 변경. 인코딩, 파일 형식 변경 등 설정 가능
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.0.RELEASE)
${} 로 변수가 사용 가능. 소스로 실행할 때는 MANIFEST 변수는 불러올 수 없다.
# resources/banner.txt
==============================
Cherrue Banner ${spring-boot.version}
==============================
->
==============================
Cherrue Banner 2.2.0.RELEASE
==============================
배너 소스 구현도 가능
@SpringBootApplication
public class SpringinitApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(SpringinitApplication.class);
app.setBanner(new Banner() {
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
out.println("==================");
out.println("HELLO");
out.println("==================");
}
});
app.run(args);
}
}
2-1-2. SpringApplicationBuilder : builder 패턴이 적용
static method 인 SpringApplication.run을 사용하면 커스텀을 할 수 없다.
인스턴스를 만들어 run 해주거나 builder 를 사용하자
@SpringBootApplication
public class SpringinitApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringinitApplication.class)
.run(args);
// same as
// SpringApplication app = new SpringApplication(SpringinitApplication.class);
// app.run(args);
}
}
3. SpringApplication 2부
spring 에는 다양한 시점(타이밍)이 있다. 이 시점에 트리거를 줄 수 있는 리스너를 만들어보자
3-1. Listener
3-1-1. 어플리케이션 컨텍스트 생성 이전의 이벤트
// @Component 이 리스너는 빈일 필요가 없다. 어플리케이션 컨텍스트 생성 이후의 리스너에는 필요
public class SampleListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
System.out.println("=======================");
System.out.println("Application is Starting");
System.out.println("=======================");
}
}
⚠️ 어플리케이션 컨텍스트 생성 전의 이벤트는 빈으로 된 리스너 사용이 불가능해서 직접 등록을 해주어야 한다.
@SpringBootApplication
public class SpringinitApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(SpringinitApplication.class);
app.addListeners(new SampleListener()); // 등록
app.run(args);
}
}
결과
me.cherrue.SpringinitApplication --debug
=======================
Application is Starting
=======================
2022-02-03 21:10:59.341 DEBUG 27529 --- [ main] .c.l.ClasspathLoggingApplicationListener : Application started with classpath: [ ...
==============================
Cherrue Banner 2.2.0.RELEASE
==============================
2022-02-03 21:10:59.418 INFO 27529 --- [ main] me.cherrue.SpringinitApplication
2-1-2. 어플리케이션 컨텍스트 생성 이후의 이벤트
아래와 같이 @Component 를 붙여서 Bean 으로 만들어주면 알아서 등록이 된다.
@Component
public class SampleListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
System.out.println("=======================");
System.out.println("Application is Starting");
System.out.println("=======================");
}
}
3-2. WebApplicationType
REACTIVE : servlet이 있어도 web flux를 쓰겠다는 설정
app.setWebApplicationType(WebApplicationType.REACTIVE);
3-3. Application arguments
Application arguments : Program arguments 로 들어온 값
- Bean 생성자에 Bean 하나만 받으면 스프링이 알아서 주입해준다.
@Component
public class ArgumentsPrinter {
public ArgumentsPrinter(ApplicationArguments args) {
System.out.println("foo : " + args.containsOption("foo"));
System.out.println("bar : " + args.containsOption("bar"));
}
}
$ java -jar target/springinit-1.0-SNAPSHOT.jar -Dfoo --bar
2022-02-03 21:21:21.164 DEBUG 27635 --- [ main] o.s.boot.SpringApplication : Loading source class me.cherrue.SpringinitApplication
foo : false
bar : true
3-4. ApplicationRunner
어플리케이션을 실행한 후 무언가 실행하고 싶을 때 사용.
CommandLineRunner 이런 애들도 있다. (조금 무식하게 접근해야 해서 불편함)
@Order(숫자) 를 통해 실행 순서를 정할 수 있음
// ApplicationRunner
@Component
public class ArgumentsRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("foo : " + args.containsOption("foo"));
System.out.println("bar : " + args.containsOption("bar"));
}
}
// CommandLineRunner
@Component
public class ArgumentsRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
Arrays.stream(args).forEach(System.out::println);
}
}
4. 외부 설정 1부
4-1. 설정 값 우선순위
application.properties의 우선순위는 15위 (총 17위까지 있음)
커맨드 라인 아규먼트는 4위 (java -jar target/aa.jar —myArguments)
test properties : test/resources 생성 + Project Structure > Modules > Test Resources 지정하면 기존 프로퍼티 덮어씀
- main의 빌드 결과를 클래스패스에 넣은 후 테스트의 빌드 결과를 부어버리기 때문
- 하지만 test/resources/application.properties를 쓰면 원래 프로퍼티와 필드를 같게 가져야 하니 주의
- @TestPropertySource와 @SpringBootTest(properties=…) 가 같은 역할이 가능하니 활용할 것
⚠️ application.properties 의 위치도 우선 순위가 다르다. (./config, ./, classpath:/, classpath:/config)
4-2. 설정 값을 불러오는 방법
-
@Value 어노테이션 사용
// application.properties cherrue.name = "Cherrue" // SampleRunner.java @Component public class SampleRunner implements ApplicationRunner { @Value("${cherrue.name") private String name; @Override public void run(ApplicationArguments args) throws Exception { System.out.println("======================"); System.out.println(name); System.out.println("======================"); } }
5. 외부 설정 2부
5-1. 설정 값을 한 번에 불러오자
- @ConfigurationProperties에 관련 설정 값 선언
- getter / setter 구현
- SpringbootApplication에 @EnableConfigurationProperties(CherrueProperties.class) : 빈 등록과 어노테이션 처리 - 안 해도 이미 포함되어 있음
-
ConfigurationProperties에 @Component 붙이기
// application.properties cherrue.name = Cherrue cherrue.age = ${random.int(0,100)} cherrue.fullName = ${cherrue.name} Lee // CherrueProperties.java @Component @ConfigurationProperties("cherrue") public class CherrueProperties { private String name; private int age; private String fullName; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } }
-
설정값 사용처에서 @Autowired로 불러온 프로퍼티 호출 = getter/setter 반환형 명시로 type safe
@Component public class SampleRunner implements ApplicationRunner { @Autowired CherrueProperties cherrueProperties; @Override public void run(ApplicationArguments args) throws Exception { System.out.println("======================"); System.out.println(cherrueProperties.getName()); System.out.println("======================"); } }
📌 Tips. dependency에 spring-boot-configuration-processor를 추가하면 properties 파일에서 자동 완성이 된다
6. 외부 설정 3부
6-1. 융통성 있는 바인딩
full-name, full_name, fullName 모두 바인딩 알아서 됨
6-2. 프로퍼티 타입 컨버전
cherrue.age = 100 ⇒ @Value(”${cherrue.age}”) int age;
Properties에는 자료형이 모두 문자열이지만, spring이 제공하는 기본적인 타입 컨버전을 통해 알아서 붙여준다.
-
특수한 컨버전 : @DurationUnit
// application.properties cherrue.name = Cherrue cherrue.age = ${random.int(0,100)} cherrue.fullName = ${cherrue.name} Lee cherrue.sessionTimeout=25 # 또는 cherrue.sessionTimeout=25s # s suffix 를 쓰면 어노테이션 없이도 duration으로 설정 가능 // CherrueProperties.java @Component @ConfigurationProperties("cherrue") public class CherrueProperties { String name; int age; String fullName; @DurationUnit(ChronoUnit.SECONDS) private Duration sessionTimeout = Duration.ofSeconds(30); public Duration getSessionTimeout() { return sessionTimeout; } public void setSessionTimeout(Duration sessionTimeout) { this.sessionTimeout = sessionTimeout; } ... }
실행결과
====================== Cherrue 81 PT25S ======================
6-3. 프로퍼티 값 검증
JSR-303 Bean validation 1.0의 검증이 가능(@NotEmpty, @Size 등)
// application.properties
cherrue.name =
cherrue.age = ${random.int(0,100)}
cherrue.fullName = ${cherrue.name} Lee
cherrue.sessionTimeout=25
// CherrueProperties.java
@Component
@ConfigurationProperties("cherrue")
@Validated
public class CherrueProperties {
@NotEmpty
String name;
int age;
String fullName;
...
}
실행결과
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'cherrue' to me.cherrue.CherrueProperties failed:
Property: cherrue.name
Value:
Origin: class path resource [application.properties]:2:0
Reason: 반드시 값이 존재하고 길이 혹은 크기가 0보다 커야 합니다.
댓글남기기