
Spring의 구조와 사용법을 이해하고 DB에 접근하는 기술까지 알아봤다.
이 과정에서 수 많은 객체를 Bean으로 등록하고 의존성을 주입하는 모습을 들여다봤다.
이번에는 이러한 과정을 자동으로 해결해주고
Spring을 더욱 편리하게 사용할 수 있는 Spring Boot에 대해 알아보자.

개발은 '시작이 반이다'라는 말이 잘 어울리는 분야가 아닐까 싶다.
무언가를 개발하기 위해서는 언어를 알아야하고 이후에는 프레임워크에 대해서도 알아야 한다.
하지만 이런 굵직한 개념들 전에 해야될 일이 바로 환경 설정이다.
내가 총을 잘 쏴도 총이 없다면 무용 지물이듯,
좋은 기술을 사용하기 위해서는 기술을 사용할 환경이 마련되어 있어야 한다.
// WAR 파일 구성
- WEB-INF
classes : 실행 클래스 모음
lib : 라이브러리 모음
- WEB-INF 외 : HTML, CSS와 같은 정적 리소스
Spring을 사용하기 전에는 환경 설정을 위해 많은 시간을 투자해야 했다.
Tomcat과 같은 WAS를 직접 설치하고 WAS에 WAR 파일을 직접 전달하여 배포해야 했으며
서블릿 컨테이너를 직접 초기화하여 WAS에 초기화 클래스에 대한 정보를 전달해야 했다.
또한 생성한 Servlet을 직접 등록해야 했고
서블릿 컨테이너에 Servlet을 등록하는 애플리케이션 초기화 인터페이스를 전달해야 했다.
서비스 로직 개발 전, 개발 후 구분 없이 설정과 관련된 문제를 해결해야만 작업이 마무리됐다.

Spring은 이렇게 환경 설정에 필요한 복잡한 과정을 일부 자동화해준다.
서블릿 컨테이너를 초기화하여 WAS에 자동으로 정보를 전달해주고
생성한 Servlet을 객체에 간단하게 등록하면 마찬가지로 서블릿 컨테이너에 자동으로 전달해준다.
// JAR 파일 구성
- META-INF
MANIFEST.MF : main 메서드 실행 클래스 설정
- project : 모든 package 및 class
또한 WAS를 직접 설치할 필요 없이 Spring은 Tomcat을 라이브러리로 내장할 수 있다.
하지만 build한 jar 파일 내부에는 동일한 jar 파일을 포함할 수 없기 때문에
톰캣 jar 내부의 class를 추출하여 기존 jar에 포함하는 Fat Jar 방식으로 내장하는데,
이 방식은 라이브러리 목록을 쉽게 확인하기 어렵고
파일명을 중복해서 저장할 수 없다는 치명적인 문제가 있다.
거기에 Tomcat 객체를 활용하여 직접 설정하는 작업까지 추가된다.
이러한 작업을 별도의 클래스를 만들고 main() 메서드에 객체를 생성하여
실행과 동시에 동작하도록 하고 하위 패키지를 자동으로 스캔하는 애노테이션을 생성하여
컴포넌트 스캔 방식을 사용하도록 설계할 수 있다.

Spring을 통해 상당 부분에서 자동화 기능을 사용할 수 있게 됐지만
Tomcat 설정이라는 여전히 번거로운 문제가 남아있다.
Spring Boot에서는 나머지 번거로운 작업까지 모두 자동으로 처리해준다.
// Executable Jar 구조
- META-INF
MANIFEST.MF : main 메서드 실행 클래스(주로 JarLauncer) 설정
- org/springframework/boot/loader : JarLauncher.class, Spring Boot Application BootStrap
- BOOT-INF
classes : 구현한 class 파일
lib : 외부 라이브러리
classpath.idx : 외부 라이브러리 경로
layers.idx : Spring Boot 구조 경로
Spring Boot는 Excutable Jar(실행 가능한 Jar)이라는 특별한 형식을 가지며,
Tomcat을 포함한 라이브러리 역시 실행 가능한 Jar 형태로 내장한다.
SpringApplication.run(Application.class, args)
내장한 Tomcat 라이브러리를 통해 설정을 처리해주는 객체를 생성하고
main 메서드 실행 시 호출하여 자동으로 설정 문제를 해결해주는데
main 메서드에 보이는 run 메서드의 정체가 바로 이것이다.
Jar 실행 → MANIFEST.MF 조회 → JarLauncher main 메서드 실행 →
BOOT-INF classes, lib 조회 → 실제 클래스 main 메서드 실행
Spring Boot에서 Jar 파일을 실행하면 먼저 MANIFEST.MF 파일을 조회하여
실행할 main 메서드의 클래스를 조회하는데 주로 JarLauncher로 설정되어 있다.
이후 애플리케이션 초기화를 담당하는 JarLauncher의 main 메서드가 실행되는데
BOOT-INF의 class의 실제 코드 위치와 라이브러리를 함께 로딩한다.
(IDE에서는 라이브러리를 자동으로 인식하여 JarLauncher가 필요하지 않다)
이후 실제 클래스의 main 메서드가 실행된다.

Spring Boot는 컴포넌트 스캔을 위해 @SpringBootApplication 애노테이션을 지원한다.
이 외에도 Spring Boot가 지원하는 핵심 기능으로,
자주 사용하는 수많은 빈들을 자동으로 등록하는 자동 구성을 지원한다.
@SpringBootApplication 내부에 구성된 @EnableAutoConfiguration 애노테이션이
바로 자동 구성을 활성화하는 애노테이션이다.
@AutoConfiguration은 내부에 @Configuration 애노테이션을 포함하고 있어서
순수하게 자동 구성을 위한 애노테이션처럼 보이지만,
실제로는 자동으로 등록되는 구성의 순서만을 지정하기 위한 애노테이션이다.
@Configuration이 선언된 클래스는 컴포넌트 스캔 대상이 되는데,
컴포넌트 스캔 스프링 설정과 자동 구성은 Life Cycle이 달라서
Spring Boot는 @AutoConfiguration 애노테이션을 컴포넌트 스캔 대상에서 제외시킨다.
Spring Boot 내부에서는 @AutoConfiguration을 사용하지 않는 것이 좋다.
@EnableAutoConfiguration은 애노테이션 내부에 @Import 애노테이션을 포함하고 있다.
@Import 애노테이션은 @Import(Configuration.class)를 사용하여
자동 구성할 클래스를 직접 지정할 수도 있지만,
@Import(ImportSelector.class)를 통해
@Configuration 애노테이션이 선언된 클래스를 동적으로 추가할 수 있다.
@Import(ImportSelector.class)를 사용하기 위해서는
ImportSelector interface를 구현하여 selectImports() 메서드를 오버라이딩해야 한다.
selectImports() 메서드는 추가할 Configuration 클래스 문자열 배열을 반환하고,
Spring Boot는 문자열 배열의 Configuration 클래스를 자동으로 구성에 추가한다.
@Conditional 애노테이션은 자동 구성 시 조건을 등록하는 애노테이션이다.
Condition interface를 구현하여 matches() 메서드를 오버라이딩해야 한다.
matches() 메서드가 true를 반환하는 경우에만 자동 구성 대상에 포함된다.
Spring Boot는 @Conditional 애노테이션을 편리하게 사용하기 위해서
대부분의 조건을 @ConditionalOnXXX라는 애노테이션으로 만들어두었다.
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@SpringBootApplication 애노테이션 내부에는
@EnableAutoConfiguration 애노테이션이 선언되어 있어 자동 구성 기능이 활성화되고,
@EnableAutoConfiguration 내부의 @Import 애노테이션이 동작하여
위 파일 경로의 class를 자동 구성 대상으로 적용한다.
JdbcTemplate, DataSource, TransactionManager 등
다양한 class가 Spring Boot의 자동 구성에 포함된다.

외부 라이브러리를 사용하기 위해서는 사용해야할 라이브러리를 하나하나 알고 있어야 하고,
심지어 버전 정보도 알고 있어야 한다.
가장 심각한 문제는 라이브러리 간에 호환이 되지 않는 버전이 있다는 점이다.
// build.gradle
plugins {
id 'io.spring.dependency-management' version '1.1.0'
}
Spring Boot는 BOM(Bill of materials)에 라이브러리 버전을 명시하여 관리한다.
Spring Boot로 생성한 Project의 build.gradle을 확인하면 버전이 명시되어 있지 않은데,
해당 라이브러리들은 Spring Boot가 적절한 버전을 자동으로 적용해준다.
버전 관리 기능을 사용하기 위해서 build.gradle의 plugins에 해당 코드를 추가해야 한다.
// build.gradle
ext {
library = 'version'
}
물론 외부 라이브러리의 특정 버전이 필요한 경우 ext를 설정해서 사용할 수 있다.
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
Spring Boot는 Spring Boot Starter로 필요한 라이브러리를 자동으로 추가해준다.
jdbc, jpa, thymeleaf 등 다수의 라이브러리가 포함되어 있다.
// build.gradle
dependencies {
implementation files('libs/file.jar')
}
직접 설계한 jar 파일을 실행할 jar 파일의 lib에 추가하여 적용할 수도 있다.
build.gradle에 라이브러리를 추가할 때 직접 추가한 라이브러리는 files 키워드를 사용한다.
라이브러리의 객체를 Bean으로 등록하여 사용할 수 있다.

Spring Boot가 Library를 자동으로 추가해주고 자주 사용하는 Bean을 자동으로 등록할 때,
Bean 객체 초기화에 필요한 정보를 함께 전달해주어야 하는 경우가 있다.
예를 들어 DB 접근 시 필요한 DataSource의 경우,
DB url, username, password와 같은 정보를 전달해주어야 한다.
문제는 환경에 따라 필요한 정보가 다르다는 점이다.
개발 환경에서 사용하는 DB 정보가 다르고 운영 환경에서 사용하는 DB 정보가 다르다.
설정 데이터에 맞게 코드를 작성하여 각각 build하는 것은 번거로울 뿐더러,
build 결과물이 다르기 때문에 검증이 어렵다.
따라서 build 이후에 외부에서 설정한 데이터를 주입하여 사용하는 방식을 사용한다.
외부에서 데이터를 설정하는 방법을 살펴보자.

Map<String, String> resultMap = System.getenv();
String result = System.getenv("key");
운영 체제의 환경 변수 값을 설정하는 방식이다.
System의 getenv() 메서드를 호출하면 운영체제에 저장된 환경 변수의 값을 조회할 수 있다.
운영체제에서 사용하는 모든 프로세스에 적용된다.
Properties properties = System.getProperties();
String result = System.getProperty("key");
자바에서 지원하는 외부 데이터 설정 방식으로 JVM에 적용된다.
Properties라는 Map과 유사한 형태의 객체에 외부 설정 데이터 값이 저장된다.
System의 getProperty() 메서드를 호출하여 조회할 수 있다.
// IntelliJ
Modify Options → Add VM Options → -Dkey=value
외부 설정 데이터를 추가하는 방법은
key=value 형태 앞에 '-D'를 추가하여 작성하면 된다.
// cmd
java -Dkey=value -jar file.jar
jar 파일을 실행할 때에도 '-D'를 추가하여 외부 설정 데이터를 추가할 수 있다.
커맨드 라인을 전달하여 외부 데이터를 설정하는 방식이다.
main(String[] args)의 args에서 설정한 데이터를 조회할 수 있다.
// IntelliJ
Edit Configuration → Program arguments → key=value
IntelliJ에서 Program arguments 입력 란에 작성한다.
key=value 형태로 작성하면 문자열 형태로 조회할 수 있는데
보통 key=value 형태를 파싱하여 사용할 수 있는 코드를 구현하여 사용한다.
// cmd
java -jar file.jar key=value
jar 파일을 실행할 때에도 커맨드 라인을 전달하여 외부 데이터를 설정할 수 있다.
자바 커맨드라인에서 key=value 형태의 문자열을 직접 파싱하는 작업은 번거롭다.
자바 커맨드라인 옵션 인수는 key=value로 작성하면 파싱하여 사용할 수 있도록 지원해준다.
// IntelliJ
Edit Configuration → Program arguments → --key=value
key=value 형태 앞에 '--' 키워드를 포함한 형태로 작성한다.
ApplicationArguments appArgs = new DefaultApplicationArguments(args);
List<String> value = appArgs.getOptionValues("key");
Spring Boot에서 ApplicationArguments 객체를 자동 Bean 등록해준다.
ApplicationArguments의 getOptionValues() 메서드로 key=value 값을 조회할 수 있다.
'--'를 작성하지 않은 커맨드라인 옵션 인수는 인식하지 않는다.

지금까지 소개한 방식은 인수 값이 늘어날수록 사용이 불가능에 가까워진다.
외부 File은 application.properties 또는 application.yml이라는
설정 데이터(Config data) 외부 파일을 생성하여 key=value 형태로인수를 관리하는 방식이다.
파일은 자바 실행 위치에 생성한다.
// application.properties
key=value1
spring.key=value2
spring.key-name=value3 // ** 캐밥 표기법 사용 **
properties 파일은 낙타 표기법이 아닌 '-'를 사용한 캐밥 표기법을 권장한다.
외부에 데이터 설정 파일을 관리하면 확인이 번거롭고
설정을 변경할 때마다 각 서버의 파일을 하나하나 변경해주어야 한다.
그리고 외부에 위치해있어 변경 이력을 관리하기 어렵다.
내부 File은 외부에 위치한 파일을 내부에 위치시켜 관리하는 방식이다.
환경에 따라 필요한 application.properties 또는 application.yml 파일을
규칙에 맞게 profile을 파일명에 추가하여 내부에 저장한다.
// application.properties 파일명 생성 규칙
application-profile.properties
이후 외부 설정에 key=value 형태의 spring.profiles.active="profile" 값을 전달하면,
Spring Boot는 해당 profile의 설정 데이터를 조회하여 사용한다.

profile에 따라 분리된 내부 파일은 관리가 번거롭다.
내부 File 통합 방식은 분리했던 내부 File을 하나의 File에 통합하여 관리하는 방식이다.
// application.properties
spring.config.active.on-profile=profile1
..
#---
spring.config.active.on-profile=profile2
..
최상위에 spring.config.active.on-profile="profile"처럼 프로필 정보를 입력한다.
외부 설정에 key=value 형태의 spring.profiles.active="profile" 값을 전달하면,
Spring Boot는 해당 profile의 설정 데이터를 조회하여 사용한다.
각 profile 영역은 '#---' 또는 '!---' 구분자를 사용하여 논리적으로 구분할 수 있다.
(application.yml에는 '---'를 구분자로 사용한다)
인식이 안되는 경우가 있기 때문에 구분자 위, 아래에는 주석 사용이 금지된다.
// application.properties
.. // ** 최상위에 spring.config.active.on-profile=profile 없이 작성 **
#---
spring.config.active.on-profile=profile1
..
#---
spring.config.active.on-profile=profile2
만약 외부 설정에 profile 정보를 전달하지 않으면 어떻게 될까?
이 경우 default라는 이름의 프로필이 적용된다.
최상위에 spring.config.active.on-profile=profile을 작성하지 않으면
해당 profile은 default profile로 적용된다.
내부 File은 기본적으로 위에서 아래 순서대로 읽으면서 프로필이 설정된다.
아무것도 전달하지 않거나 전달하더라도 우선 최상위의 default profile을 먼저 설정하고,
아래로 읽어 나가면서 외부에서 설정한 profile이 있다면 profile을 덮어씌우는 형태이다.
default 프로필을 최상위가 아닌 하단에 작성하는 경우,
아래로 읽어 나가면서 외부에서 설정한 profile로 설정했다가
default profile을 조회하면 그대로 덮어씌우기 때문에
default profile은 항상 최상단에 작성해주어야 한다.

@TestPropertySource(테스트 시 사용) > 커맨드 라인 (옵션) 인수 > 자바 시스템 속성
> OS 환경 변수 > application.properties
외부 설정에서는 범위가 넓은 것보다 좁은 것이 우선권을 가진다.
Jar 외부에 위치한 application-profile.properties > Jar 외부에 위치한 application.properties
> Jar 내부에 위치한 application-profile.properties > Jar 내부에 위치한 application.properties
File을 통해 외부 데이터를 설정하는 경우 더 유연한 것이 우선권을 가진다.
함께 패키징되는 내부 File로 외부 데이터를 설정하는 것보다,
재배포가 필요하지 않는 외부 File을 통해 데이터를 설정하는 것이 우선권을 가진다.
실무에서는 보통 application.properties에서 외부 설정 값을 보관하고
변경이 필요한 때에 더 높은 우선순위를 가지는 외부 설정을 사용한다.
@Profile("profile")
public Clazz clazz() {
return new Clazz();
}
@Profile은 작성한 profile이 활성화 됐을 때 Bean으로 등록하는 애노테이션이다.
내부에서 @Conditional을 사용한다.

외부 데이터를 설정하는 방법은 보다시피 정말 다양하다.
문제는 설정 방법을 변경할 때마다 코드를 변경해야 한다는 점이다.
외부 데이터를 설정하고 조회하는 방식의 추상화가 필요하겠다.
외부 데이터 설정 → PropertySource → Environment → 데이터 조회
Environment.getProperty("key", Clazz.class);
PropertySource는 외부 설정 방식에 따라 데이터 조회 방법을 추상화한 클래스이다.
Environment는 PropertySource에서 조회한
key=value 형태의 데이터에 접근하는 방식을 추상화한 클래스이다.
Spring은 내부적으로 외부 설정에 적합한 PropertySource 구현체를 생성하고
Environment를 통해서 접근할 수 있도록 자동으로 연결해준다.
Environment의 getProperty() 메서드를 통해 외부에서 설정한 데이터를 조회할 수 있는데,
Class 정보를 함께 전달하여 데이터를 자동으로 변환하는 것을 지원해준다.
@Value("${key:default}") // ** key가 없는 경우 default 사용 **
int number;
Environment 객체를 생성하여 조회하는 것은 번거롭다.
@Value 애노테이션을 필드 또는 파라미터에 선언하면
외부에서 설정한 데이터를 선언한 타입에 맞게 자동으로 변환할 수 있다.
'${}' 내부에 key 값을 전달하여 value를 조회할 수 있고,
조회하려는 key값이 없는 경우 default값을 사용하도록 설정할 수 있다.

@ConfigurationProperties("필드를 제외한 key")
@Data // ** 자바빈 프로퍼티 방식 **
class User {
String name;
int age;
}
필드마다 @Value 애노테이션을 선언하여 데이터를 조회하는 방식은 매우 번거롭다.
@ConfigurationProperties을 클래스에 선언하고 필드를 제외한 key 값을 입력하면
외부 설정 데이터 묶음을 객체의 필드에 일괄적으로 바인딩할 수 있다.
이 과정에서 타입에 맞지 않는 데이터가 바인딩될 경우 예외를 발생시키기 때문에
@ConfigurationProperties를 타입 안전한 설정 속성이라 부른다.
외부에서 설정한 데이터를 주입할 때 자바빈 프로퍼티 방식을 사용한다.
따라서 getter, setter 메서드가 필요하다.
@ConfigurationProperties("필드를 제외한 key")
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
setter 메서드는 외부에서 잘못 사용될 수 있기 때문에 위험하다.
생성자 주입 방식을 사용하면 setter 메서드 없이도 안전하게 값을 주입받을 수 있다.
@EnableConfigurationProperties(ConfigurationProperties.class)
class Clazz {
// 코드
}
@ConfigurationProperties 애노테이션을 선언한 클래스는
외부에서 설정한 데이터를 객체에 바인딩하는 역할을 수행한다.
해당 애노테이션이 선언된 클래스를 @Component를 선언하여 Bean으로 등록할 경우,
외부에서 설정한 데이터를 객체에 바인딩하는 작업을 정상적으로 수행하지 못할 수 있다.
따라서 @ConfigurationProperties를 선언한 클래스는
@EnableConfigurationProperties 애노테이션을 사용하여 Bean으로 등록해주어야 한다.
이 과정에서 캐밥 표기법으로 작성한 값은 낙타 표기법으로 자동 변환된다.
@ConfigurationPropertiesScan({"package.path"})
class Clazz {
// 코드
}
@EnableConfigurationProperties는
@ConfigurationProperties 애노테이션이 선언된 클래스를 하나하나 Bean으로 직접 등록한다.
클래스 하나가 아닌 특정 범위에 있는 클래스들을 일괄적으로 등록해야 하는 경우,
@ConfigurationPropertiesScan을 사용하면 된다.

// application.properties
spring.config.active.on-profile=profile
// application.yml
spring:
config:
active:
on-profile: profile
application.properties는 한 줄로 입력하기 때문에 가독성이 떨어진다.
application.yml은 다양한 규칙을 사용하여 계층 관계를 보기 좋게 작성할 수 있다.
실무에서는 주로 읽기 쉬운 application.yml을 사용한다.
둘 다 있는 경우 application.properties가 우선권을 가지지만 둘 중 하나만 사용한다.

// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
Spring Boot는 애플리케이션 모니터링을 위해
다양한 지표 데이터를 제공하는 Actuator 기능을 제공한다.
'/actuator', '/actuator/endpoint' url을 입력하여 접근할 수 있다.
// application.properties
// ** endpoint 활성화 **
management.endpoint.endpointName.enabled = true
// ** endpoint 노출 **
management.endpoints.web.exposure.include=value // value 노출
management.endpoints.web.exposure.exclude=value // value 제외
endpoint란 Actuator가 제공하는 기능을 의미한다.
endpoint가 활성화된 상태에서 노출해야 웹에서 확인할 수 있다.
endpoint는 기본적으로 활성화되어 있다.
(애플리케이션을 종료하는 shutdown endpoint만 활성화가 되어있지 않다)
// application.properties
management.endpoints.web.base-path="/string"
위 코드를 입력하면 '/actuator'로 접근하는 url을 '/string'로 변경 가능하다.
Actuator은 내부 정보를 노출하기 때문에 외부 인터넷에서 접근이 불가능하게 막고,
내부에서만 접근 가능한 내부망을 사용해야 한다.
불가피하게 외부 인터넷 망을 통해 접근해야 한다면
Servlet Filter와 같은 기능을 통해 인증된 사용자만 접근하도록 구현해야 한다.

모니터링에 필요한 데이터는 Monitoring Tool이 읽을 수 있는 양식에 맞게 전달하면
모니터링 DB에 저장하여 데이터의 추세를 확인할 수 있다.
다양한 endpoint 중에서도 애플리케이션 성능과 관련된 metric 정보를 전달한다.
문제는 Monitoring Tool이 굉장히 많고, 툴을 변경하면
양식에 맞게 metric을 전달하기 위해 코드를 변경해야한다는 점이다.
데이터를 전달하는 방식의 추상화가 필요하다.
Micrometer는 Monitoring Tool에 데이터를 전달하는 방식을 추상화한 클래스이다.
MeterRegistry는 Micrometer의 핵심 인터페이스로, Metric 수집과 저장 역할을 수행한다.
@Counted("metricName")
public void method() {
// 코드
}
Metric 데이터를 수집하고 저장하는 코드는 서비스 로직으로부터 분리해야 한다.
@Counted을 메서드에 선언하면 AOP를 활용하여 Metric 데이터를 수집하고 저장할 수 있다.
@Bean
public CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry);
}
@Counted 애노테이션은 CountedAspect를 Bean으로 등록해야 동작한다.
@Timed("metricName")
class Clazz {
// 코드
}
@Timed 애노테이션은 클래스 또는 메서드 레벨에 선언할 수 있다.
클래스에 선언하는 경우, 해당 클래스의 모든 public 메서드에 적용된다.
@Timed 애노테이션은 AOP를 활용하여 메서드 실행 시간(타이머)을 기록한다.

Spring Boot → Micrometer → Monitoring DB
Spring Boot에서 Micrometer 객체를 사용하여 metric을 등록하면
Monitoring Tool은 자동으로 데이터를 수집하여 Monitoring DB에 저장한다.
대표적인 Monitoring Tool은 Prometheus가 있다.
Spring Boot → Micrometer → Monitoring DB → DashBoard
metric 데이터를 저장한 Monitoring DB는 한 눈에 이해하기 어렵다.
데이터를 보기 좋게 정리하여 출력하는 도구가 필요한데, 그것을 Dashboard라고 한다.
Dashboard는 Monitoring DB의 데이터를 가져와 보기 좋은 형태로 출력한다.
대표적인 Dashboard Tool로는 Grafana가 있다.
Grafana에는 사람들이 사용하기 좋게 정리한 대시보드를 가져다 사용하면 된다.
Dashboard → 애플리케이션 추적(HTTP Request 추적 등) → log → 알람
Dashboard 외에도 다양한 수단으로 애플리케이션 모니터링 단계를 수립해야 한다.

이번에는 다양한 자동화와 편의 기능을 통하여
Spring을 더욱 쉽게 사용하는 Spring Boot의 특징을 살펴보았다.
Spring 뿐만 아니라 Spring Boot를 알차게 활용할 수 있도록
Spring Boot가 제공하는 다양한 기능을 이해하자.
다음에는 애플리케이션을 운영하는 데에 있어 중요한 모니터링에 관한
Actuator와 Monitoring DB, Dashboard에 대해 더 자세히 알아보자.