Spring Boot
์ํ๋์ ์คํ๋ง ๋ถํธ - ํต์ฌ ์๋ฆฌ์ ํ์ฉ ๊ฐ์๋ฅผ ์์ฝํ ๋ด์ฉ์ ๋๋ค.
Intro
์คํ๋ง ํ๋ ์์ํฌ๋ฅผ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ฃผ๋ ๋๊ตฌ
ํต์ฌ ๊ธฐ๋ฅ
WAS
: Tomcat ๊ฐ์ ์น ์๋ฒ๋ฅผ ๋ด์ฅํด์ ๋ณ๋์ ์น ์๋ฒ ์ค์น ๋ถํ์๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ด๋ฆฌ
: ์์ฌ์ด ๋น๋ ๊ตฌ์ฑ์ ์ํ ์คํํฐ ์ข ์์ฑ ์ ๊ณต ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ๊ด๋ฆฌ์๋ ๊ตฌ์ฑ
: ํ๋ก์ ํธ ์์์ ํ์ํ ์คํ๋ง๊ณผ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋น์ ์๋ ๋ฑ๋ก์ธ๋ถ ์ค์
: ํ๊ฒฝ์ ๋ฐ๋ผ ๋ฌ๋ผ์ ธ์ผ ํ๋ ์ธ๋ถ ์ค์ ๊ณตํตํํ๋ก๋์ ์ค๋น
: ๋ชจ๋ํฐ๋ง์ ์ํ ๋ฉํธ๋ฆญ, ์ํ ํ์ธ ๊ธฐ๋ฅ ์ ๊ณต
์น ์๋ฒ์ ์๋ธ๋ฆฟ ์ปจํ
์ด๋
๋ฐฉ์์ ๋ณํ
์ ํต ๋ฐฉ์
์๋ฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ์ ์๋ฒ์ ํฐ์บฃ ๊ฐ์ WAS(์น ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ) ์ค์น๊ฐ ํ์
WAS์์ ๋์ํ๋๋ก ์๋ธ๋ฆฟ ์คํ์ ๋ง์ถ์ด ์ฝ๋๋ฅผ ์์ฑํ๊ณ WAR ํ์์ผ๋ก ๋น๋ํด์ .war ํ์ผ์ ์์ฑ
์์ฑ๋ .war ํ์ผ์ WAS์ ์ ๋ฌํด์ ๋ฐฐํฌํ๋ ๋ฐฉ์์ผ๋ก ์ ์ฒด ๊ฐ๋ฐ ์ฃผ๊ธฐ๊ฐ ๋์
๊ณผ๊ฑฐ ๋ฐฉ์์ WAS ๊ธฐ๋ฐ ์์์ ๊ฐ๋ฐํ๊ณ ์คํ์ด ํ์ํ๊ณ , IDE ๊ฐ์ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ WAS์ ์ฐ๋ํด์ ์คํ๋๋๋ก ๋ณต์กํ ์ถ๊ฐ ์ค์ ์ด ํ์
์ต๊ทผ ๋ฐฉ์
์ต๊ทผ์๋ ์คํ๋ง ๋ถํธ๊ฐ ๋ด์ฅ ํฐ์บฃ์ ํฌํจ(์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋ ์์ WAS๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ด์ฅ)
๊ฐ๋ฐ์๋ ์ฝ๋๋ฅผ ์์ฑํ๊ณ JAR๋ก ๋น๋ํ ๋ค์์ ํด๋น JAR๋ฅผ ์ํ๋ ์์น์์ ์คํํ๊ธฐ๋ง ํ๋ฉด WAS๋ ํจ๊ป ์คํ
IDE ๊ฐ๋ฐ ํ๊ฒฝ์์ WAS ์ค์น์ ์ฐ๋ํ๋ ๋ณต์กํ ์ผ์ ๋ถํ์
JAR & WAR
JAR (Java Archive)
java -jar abc.jar
์๋ฐ๋ ์ฌ๋ฌ ํด๋์ค์ ๊ด๋ จ ๋ฆฌ์์ค๋ฅผ ์์ถํ .jar ๋ผ๋ ์์ถ ํ์ผ์ด ์กด์ฌ
JAR ํ์ผ์ JVM ์์์ ์ง์ ์คํ๋๊ฑฐ๋ ๋ค๋ฅธ ๊ณณ์์ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ ๊ณต
์ง์ ์คํํ ๊ฒฝ์ฐ main() ๋ฉ์๋๊ฐ ํ์ํ๊ณ , MANIFEST.MF ํ์ผ์ ์คํํ ๋ฉ์ธ ๋ฉ์๋๊ฐ ์๋ ํด๋์ค ์ง์ ํ์
WAR (Web Application Archive)
.war ํ์ผ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ(WAS)์ ๋ฐฐํฌํ ๋ ์ฌ์ฉํ๋ ํ์ผ
JAR ํ์ผ์ด JVM ์์์ ์คํ๋๋ค๋ฉด, WAR๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ ์์์ ์คํ
์น ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ ์์์ ์คํ๋๊ณ , HTML ๊ฐ์ ์ ์ ๋ฆฌ์์ค์ ํด๋์ค ํ์ผ์ ๋ชจ๋ ํจ๊ป ํฌํจํ๊ธฐ ๋๋ฌธ์ JAR ๋๋น ๊ตฌ์กฐ๊ฐ ๋ณต์กํ๊ณ WAR ๊ตฌ์กฐ๋ฅผ ์ง์ผ์ผ ํจ
์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ
์๋ธ๋ฆฟ์ ์ด๊ธฐํ ์ธํฐํ์ด์ค(ServletContainerInitializer)๋ฅผ ์ ๊ณต
์๋ธ๋ฆฟ ์ปจํ ์ด๋๋ฅผ ์ด๊ธฐํ ํ๋ ๊ธฐ๋ฅ ์ ๊ณต
์๋ธ๋ฆฟ ์ปจํ ์ด๋๋ ์คํ ์์ ์ ์ด๊ธฐํ ๋ฉ์๋์ธ onStartup() ์ ํธ์ถ
์ฌ๊ธฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ํ ๊ธฐ๋ฅ๋ค์ ์ด๊ธฐํ ํ๊ฑฐ๋ ๋ฑ๋ก
public interface ServletContainerInitializer {
/**
* Set<Class<?>> c
* - ๋ ์ ์ฐํ ์ด๊ธฐํ ๊ธฐ๋ฅ ์ ๊ณต
* - @HandlesTypes ์ ๋
ธํ
์ด์
๊ณผ ํจ๊ป ์ฌ์ฉ
*
* ServletContext ctx
* - ์๋ธ๋ฆฟ ์ปจํ
์ด๋ ์์ฒด ๊ธฐ๋ฅ ์ ๊ณต
* - ์ด ๊ฐ์ฒด๋ฅผ ํตํด ํํฐ๋ ์๋ธ๋ฆฟ ๋ฑ๋ก ๊ฐ๋ฅ
*/
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws
ServletException;
}
์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ๋ฅผ ์ํ ์ค์ example

์ด๊ธฐํ ์์
์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ ์คํ
ServletContainerInitializer ๋ฅผ ๊ตฌํํ๊ณ ,
resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
ํ์ผ์ ๋ฑ๋ก๋ ์ปจํ ์ด๋๋ฅผ ์คํ
์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ ์คํ
์ปจํฐ์ด๋๋ฅผ ์คํํ๋ฉด์
@HandlesTypes(AppInit.class)
๊ฐ ์ ์ธ๋์ด ์์ ๊ฒฝ์ฐ AppInit ๊ตฌํ์ฒด๋ฅผ ๋ชจ๋ ์ฐพ์์ ์์ฑ ๋ฐ ์คํ
์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ ๊ฐ๋ ์์ฑ ์ด์
์๋ธ๋ฆฟ ์ปจํ ์ด๋๋ ์ด๊ธฐํ๋ฅผ ์ํด ServletContainerInitializer ์ธํฐํ์ด์ค ๊ตฌํ๊ณผ META-INF/services/ jakarta.servlet.ServletContainerInitializer ํ์ผ์ ํด๋น ํด๋์ค๋ฅผ ์ง์ ์ง์ ํด์ผ ํ์ง๋ง, ์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ๋ ํน์ ์ธํฐํ์ด์ค๋ง ๊ตฌํํ๋ฉด ๋๋ ํธ๋ฆฌํจ
์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ๋ ์๋ธ๋ฆฟ ์ปจํ ์ด๋์ ์๊ด์์ด ์ํ๋ ๋ชจ์์ผ๋ก ์ธํฐํ์ด์ค ์์ฑ์ด ๊ฐ๋ฅํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ ์ฝ๋๊ฐ ์๋ธ๋ฆฟ ์ปจํ ์ด๋์ ๋ํ ์์กด์ ์ค์ผ ์ ์์
์๋ธ๋ฆฟ ์ปจํ ์ด๋ / ์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ example
์คํ๋ง ์ปจํ ์ด๋ ๋ฑ๋ก
์คํ๋ง ์ปจํ ์ด๋ ๋ง๋ค๊ธฐ
์คํ๋งMVC ์ปจํธ๋กค๋ฌ๋ฅผ ์คํ๋ง ์ปจํ ์ด๋์ ๋น์ผ๋ก ๋ฑ๋กํ๊ธฐ
์คํ๋งMVC๋ฅผ ์ฌ์ฉํ๋๋ฐ ํ์ํ ๋์คํจ์ฒ ์๋ธ๋ฆฟ์ ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ๋ฑ๋กํ๊ธฐ
์คํ๋ง ์ปจํ ์ด๋ ๋ฑ๋ก example
์คํ๋ง MVC ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ ์ง์
๋ฒ๊ฑฐ๋กญ๊ณ ๋ฐ๋ณต์ ์ธ ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ ๊ณผ์ ์ ์คํ๋ง MVC์ด ์ง์
๊ฐ๋ฐ์๋ ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ ๊ณผ์ ์ ์๋ตํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ ์ฝ๋๋ง ์์ฑ
WebApplicationInitializer
์ธํฐํ์ด์ค๋ง ๊ตฌํspring-web ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๋ฉด, ์๋ ํ์ผ๋ค์ ์ด๋ฏธ ๋ฑ๋กํด๋ ๊ฒ์ ํ์ธ
META-INF/services/jakarta.servlet.ServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
package org.springframework.web;
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}

์คํ๋ง MVC ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ด๊ธฐํ ์ง์(WebApplicationInitializer ๊ตฌํ) example
์คํ๋ง ๋ถํธ์ ๋ด์ฅ ํฐ์บฃ
Tomcat Library
implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.5'
WAR ๋ฐฐํฌ ๋ฐฉ์์ ๋จ์
WAS(ex. tomcat) ๋ณ๋ ์ค์น ํ์
๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ๋ณต์ก
๋ฐฐํฌ ๊ณผ์ ๋ณต์ก
๋ฒ์ ๋ณ๊ฒฝ ์ WAS ์ฌ์ค์น ํ์
๋ด์ฅ ํฐ์บฃ: ์๋ธ๋ฆฟ
๋ด์ฅ ํฐ์บฃ์ ์ฌ์ฉํ๋ฉด ํฐ์บฃ ์๋ฒ ์ค์น, IDE์ ๋ณ๋์ ๋ณต์กํ ํฐ์บฃ ์ค์ ์์ด main() ๋ฉ์๋๋ง ์คํํ๋ฉด ํฐ์บฃ๊น์ง ๋งค์ฐ ํธ๋ฆฌํ๊ฒ ์คํ
์คํ๋ง ๋ถํธ์ ๋ด์ฅ ํฐ์บฃ: ์๋ธ๋ฆฟ example
๋ด์ฅ ํฐ์บฃ: ์คํ๋ง
๋ด์ฅ ํฐ์บฃ์ ์คํ๋ง ์ฐ๋
์คํ๋ง ๋ถํธ์ ๋ด์ฅ ํฐ์บฃ: ์คํ๋ง ์ปจํ ์ด๋ ์ฐ๊ฒฐ example
๋ด์ฅ ํฐ์บฃ: ๋น๋์ ๋ฐฐํฌ
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ํฌํจ๋ ๋ด์ฅ ํฐ์บฃ์ ๋น๋ ๋ฐฐํฌํ๊ธฐ
main() ๋ฉ์๋๋ฅผ ์คํํ๊ธฐ ์ํด์ jar ํ์์ผ๋ก ๋น๋
jar ์์๋
META-INF/MANIFEST.MF
ํ์ผ์ ์คํํ main() ๋ฉ์๋์ ํด๋์ค๋ฅผ ์ง์ Manifest-Version: 1.0 Main-Class: hello.embed.EmbedTomcatSpringMain
build.gradle
์ ์ฉ ์Jar ์์๋ Jar๋ฅผ ํฌํจํ ์ ์์ผ๋ฏ๋ก, ๋ผ์ด๋ธ๋ฌ๋ฆฌ(jar)์์ ์ ๊ณต๋๋ ํด๋์ค๋ค์ด ํฌํจ๋
fat jar
๋๋uber jar
๋ฅผ ํ์ฉ
task buildFatJar(type: Jar) { manifest { attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain' } // ํ์ผ๋ช ์ค๋ณต ์ ๊ฒฝ๊ณ duplicatesStrategy = DuplicatesStrategy.WARN // ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋๋ฆฌ๋ฉด์ class ํ์ผ๋ค์ ๋ฝ์๋ด๊ณ , ๋น๋ ์ ํฌํจ from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } with jar }
๋น๋:
./gradlew clean buildFatJar
์คํ:
java -jar embed-0.0.1-SNAPSHOT.jar
๋ถํธ ํด๋์ค ๋ง๋ค์ด ๋ณด๊ธฐ
์คํ๋ง ๋ถํธ์ ๋ด์ฅ ํฐ์บฃ: ํธ๋ฆฌํ ๋ถํธ ํด๋์ค ๋ง๋ค๊ธฐ example
์คํ๋ง ๋ถํธ์ ์น ์๋ฒ
์คํ ๊ณผ์
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
์คํ๋ง ๋ถํธ ์คํ์ Java main() ๋ฉ์๋์์ SpringApplication.run() ํธ์ถ
ํ๋ผ๋ฏธํฐ๋ก ๋ฉ์ธ ์ค์ ์ ๋ณด๋ฅผ ๋๊ฒจ์ฃผ๋๋ฐ, ๋ณดํต @SpringBootApplication ์ ๋ ธํ ์ด์ ์ด ์๋ ํ์ฌ ํด๋์ค๋ฅผ ์ง์
@SpringBootApplication ์ ๋ ธํ ์ด์ ์์๋ @ComponentScan์ ํฌํจํ ์ฌ๋ฌ ๊ธฐ๋ฅ์ด ์ค์
๊ธฐ๋ณธ ์ค์ ์ ํ์ฌ ํจํค์ง์ ๊ทธ ํ์ ํจํค์ง ๋ชจ๋๋ฅผ ์ปดํฌ๋ํธ ์ค์บ
SpringApplication.run(BootApplication.class, args);
์ฝ๋ ํ ์ค์์
์คํ๋ง ์ปจํ ์ด๋ ์์ฑ
(new AnnotationConfigServletWebServerApplicationContext())WAS(๋ด์ฅ ํฐ์บฃ) ์์ฑ
(Tomcat tomcat = new Tomcat())
์คํ ๊ฐ๋ฅ Jar(Executable Jar)
Fat Jar์ ๋ฌธ์ ์ (๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ธ ์ด๋ ค์, ํ์ผ๋ช ์ค๋ณต ํด๊ฒฐ ์ด๋ ค์)์ ํด๊ฒฐํ๊ธฐ ์ํด jar ๋ด๋ถ์ jar๋ฅผ ํฌํจํ์ฌ ์คํํ ์ ์๋ ์คํ๋ง ๋ถํธ์์ ์๋กญ๊ฒ ์ ์ํ ํน๋ณํ ๊ตฌ์กฐ์ jar
boot-0.0.1-SNAPSHOT.jar
META-INF
MANIFEST.MF
org/springframework/boot/loader
JarLauncher.class : ์คํ๋ง ๋ถํธ main() ์คํ ํด๋์ค
BOOT-INF
classes : ๊ฐ๋ฐํ class ํ์ผ๊ณผ ๋ฆฌ์์ค ํ์ผ
hello/boot/BootApplication.class
hello/boot/controller/HelloController.class
โฆ
lib : ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
spring-webmvc-6.0.4.jar
tomcat-embed-core-10.1.5.jar
...
classpath.idx : ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชจ์
layers.idx : ์คํ๋ง ๋ถํธ ๊ตฌ์กฐ ์ ๋ณด
Jar ์คํ ์ ๋ณด
java -jar xxx.jar
๋ฅผ ์คํํ๊ฒ ๋๋ฉดMETA-INF/MANIFEST.MF
ํ์ผ์ ์ฐพ๊ณ , ์ฌ๊ธฐ์ ์๋ Main-Class ๋ฅผ ์ฝ์ด์ main() ๋ฉ์๋๋ฅผ ์คํ
Manifest-Version: 1.0
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: hello.boot.BootApplication
Spring-Boot-Version: 3.0.2
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Build-Jdk-Spec: 17
Main-Class
JarLauncher(org/springframework/boot/loader/JarLauncher)๋ ์คํ๋ง ๋ถํธ๊ฐ ๋น๋ ์ ์ฝ์
JarLauncher: ๋ด๋ถ jar(classes, lib)์ ํน๋ณํ ๊ตฌ์กฐ์ ํด๋์ค ์ ๋ณด๋ฅผ ์ฝ์ด๋ค์ด๋ ๊ธฐ๋ฅ
์ดํ Start-Class ์ ์ง์ ๋ main() ํธ์ถ
Start-Class
main() ์ด ์๋ hello.boot.BootApplication
Spring-Boot-Version : ์คํ๋ง ๋ถํธ ๋ฒ์
Spring-Boot-Classes : ๊ฐ๋ฐํ ํด๋์ค ๊ฒฝ๋ก
Spring-Boot-Lib : ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฒฝ๋ก
Spring-Boot-Classpath-Index : ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชจ์
Spring-Boot-Layers-Index : ์คํ๋ง ๋ถํธ ๊ตฌ์กฐ ์ ๋ณด
์คํ๋ง ๋ถํธ ๋ก๋
org/springframework/boot/loader ํ์์ ์๋ ํด๋์ค
JarLauncher ๋ฅผ ํฌํจํ ์คํ๋ง ๋ถํธ๊ฐ ์ ๊ณตํ๋ ์คํ ๊ฐ๋ฅ Jar๋ฅผ ์ค์ ๋ก ๊ตฌ๋์ํค๋ ํด๋์ค๋ค์ด ํฌํจ
์คํ๋ง ๋ถํธ๋ ๋น๋ ์ ์ด ํด๋์ค๋ค์ ํฌํจ
์คํ ๊ณผ์
1.java -jar xxx.jar
2.MANIFEST.MF ์ธ์
3.JarLauncher.main() ์คํ
BOOT-INF/classes/ ์ธ์
BOOT-INF/lib/ ์ธ์
4.BootApplication.main() ์คํ
์คํ๋ง ๋ถํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ๊ด๋ฆฌ
์คํ๋ง ๋ถํธ๋ ์ ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ์ ์ง์ ๊ด๋ฆฌ
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ์ ์๋ตํด๋ ์คํ๋ง ๋ถํธ๊ฐ ๋ถํธ ๋ฒ์ ์ ๋ง์ถ ์ต์ ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ์ ์ ํ
์ ์๋ ค์ง์ง ์๊ฑฐ๋ ๋์ค์ ์ด์ง ์์์ ์คํ๋ง ๋ถํธ๊ฐ ๊ด๋ฆฌํ์ง ์๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฒ์ ์ ์ง์ ๋ช ์
๋ฒ์ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด io.spring.dependency-management ํ๋ฌ๊ทธ์ธ ์ฌ์ฉ ํ์
spring-boot-dependencies BOM(Bill of materials) ์ ๋ณด๋ฅผ ์ฐธ๊ณ ํด์ ๋ฒ์ ๊ด๋ฆฌ
plugins {
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
...
dependencies {
//์คํ๋ง ์น, MVC
implementation 'org.springframework:spring-webmvc'
//๋ด์ฅ ํฐ์บฃ
implementation 'org.apache.tomcat.embed:tomcat-embed-core'
//JSON ์ฒ๋ฆฌ
implementation 'com.fasterxml.jackson.core:jackson-databind'
//์คํ๋ง ๋ถํธ ๊ด๋ จ
implementation 'org.springframework.boot:spring-boot'
implementation 'org.springframework.boot:spring-boot-autoconfigure'
//LOG ๊ด๋ จ
implementation 'ch.qos.logback:logback-classic'
implementation 'org.apache.logging.log4j:log4j-to-slf4j'
implementation 'org.slf4j:jul-to-slf4j'
//YML ๊ด๋ จ
implementation 'org.yaml:snakeyaml'
}
์คํ๋ง ๋ถํธ ์คํํฐ
Spring Boot application starters
๊ฐํธํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ์ํด ํ๋ก์ ํธ ์์์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ์ ๋ชจ์๋ ์คํ๋ง ๋ถํธ ์คํํฐ ์ ๊ณต
dependencies {
// ์คํ๋ง ์น MVC, ๋ด์ฅ ํฐ์บฃ, JSON ์ฒ๋ฆฌ, ์คํ๋ง ๋ถํธ ๊ด๋ จ, LOG, YML ๋ฑ ํฌํจ
implementation 'org.springframework.boot:spring-boot-starter-web'
// ์คํ๋ง ๋ฐ์ดํฐ JPA, ํ์ด๋ฒ๋ค์ดํธ ๋ฑ ํฌํจ
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ๋ณ๊ฒฝ
ext['tomcat.version'] = '10.1.4'
Auto Configuration
์คํ๋ง ๋ถํธ๋ Auto Configuration ๊ธฐ๋ฅ์ ์ ๊ณตํ๋๋ฐ, ์์ฃผ ์ฌ์ฉํ๋ ๋น๋ค์ ์๋์ผ๋ก ๋ฑ๋กํด ์ค๋ค.
JdbcTemplate , DataSource , TransactionManager .. ๋ฑ ์คํ๋ง ๋ถํธ๊ฐ ์๋ ๊ตฌ์ฑ์ ์ ๊ณตํด์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก
spring-boot-autoconfigure ํ๋ก์ ํธ ์์์ ์ ๋ง์ ์๋ ๊ตฌ์ฑ ์ ๊ณต
ex. JdbcTemplateAutoConfiguration
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ DatabaseInitializationDependencyConfigurer.class,
JdbcTemplateConfiguration.class,
NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {
}
@AutoConfiguration
: ์๋ ๊ตฌ์ฑ์ ์ฌ์ฉํ๋ ค๋ฉด ์ด ์ ๋
ธํ
์ด์
๋ฑ๋ก
๋ด๋ถ์ @Configuration ์ผ๋ก ๋น์ ๋ฑ๋กํ๋ ์๋ฐ ์ค์ ํ์ผ๋ก ์ฌ์ฉ
after = DataSourceAutoConfiguration.class
์๋ ๊ตฌ์ฑ์ด ์คํ๋๋ ์์ ์ง์
JdbcTemplate ์ DataSource ๊ฐ ํ์๋ฏ๋ก DataSource ๋ฅผ ์๋์ผ๋ก ๋ฑ๋กํด์ฃผ๋ DataSourceAutoConfiguration ์ดํ ์คํํ๋๋ก ์ค์
@ConditionalOnClass
({ DataSource.class, JdbcTemplate.class })
IF๋ฌธ๊ณผ ์ ์ฌํ ๊ธฐ๋ฅ ์ ๊ณต
ํด๋น ํด๋์ค๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง ์ค์ ์ด ๋์
์๋ค๋ฉด ์ค์ ๋ค์ด ๋ชจ๋ ๋ฌดํจํ ๋๊ณ , ๋น๋ ๋ฑ๋ก๋์ง ์์
JdbcTemplate ์ DataSource, JdbcTemplate ํด๋์ค๊ฐ ์์ด์ผ ๋์์ด ๊ฐ๋ฅ
@Import
: ์คํ๋ง์์ ์๋ฐ ์ค์ ์ถ๊ฐ ์ ์ฌ์ฉ
์ฐธ๊ณ . JdbcTemplateConfiguration
@Configuration
: ์๋ฐ ์ค์ ํ์ผ๋ก ์ฌ์ฉ
@ConditionalOnMissingBean
(JdbcOperations.class)
JdbcOperations(JdbcTemplate ๋ถ๋ชจ ์ธํฐํ์ด์ค) ๋น์ด ์์ ๋ ๋์
๋ด๊ฐ ๋ฑ๋กํ JdbcTemplate ๊ณผ ์ค๋ณต ๋ฑ๋ก๋๋ ๋ฌธ์ ๋ฐฉ์ง
์คํ๋ง ๋ถํธ๊ฐ ์ ๊ณตํ๋ ์๋ ๊ตฌ์ฑ
@Conditional
ํน์ ์ํฉ์ผ ๋๋ง ํน์ ๋น๋ค์ ๋ฑ๋กํด์ ์ฌ์ฉํ๋๋ก ๋์์ฃผ๋ ๊ธฐ๋ฅ
Condition ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ์ฌ์ฉ
/** * ConditionContext : ์คํ๋ง ์ปจํ ์ด๋, ํ๊ฒฝ ์ ๋ณด๋ฑ์ด ๋ด์ ํด๋์ค * AnnotatedTypeMetadata : ์ ๋ ธํ ์ด์ ๋ฉํ ์ ๋ณด๋ฅผ ๋ด์ ํด๋์ค */ package org.springframework.context.annotation; public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
์๋ฅผ ๋ค์ด, @Conditional(MemoryCondition.class) ์ ์ธ์ด ๋์ด ์์ ๊ฒฝ์ฐ
MemoryCondition matches() ์คํ
๊ฒฐ๊ณผ๊ฐ true ์ผ ๊ฒฝ์ฐ
MemoryConfig ๋ ์ ์ ๋์ -> memoryController, memoryFinder ๋น ๋ฑ๋ก
๊ฒฐ๊ณผ๊ฐ false ์ผ ๊ฒฝ์ฐ
MemoryConfig ๋ ๋ฌดํจํ -> memoryController, memoryFinder ๋น์ ๋ฑ๋ก๋์ง ์์
์ ์ฝ๋๋
@ConditionalOnProperty(name = "memory", havingValue = "on")
ํ ์ค๋ก ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅ
์คํ๋ง์ด ์ ๊ณตํ๋ ๋ค์ํ Condition Annotations
@ConditionalOnClass, @ConditionalOnMissingClass
ํด๋์ค๊ฐ ์๋ ๊ฒฝ์ฐ ๋์. ๋๋จธ์ง๋ ๊ทธ ๋ฐ๋
@ConditionalOnBean, @ConditionalOnMissingBean
๋น์ด ๋ฑ๋ก๋์ด ์๋ ๊ฒฝ์ฐ ๋์. ๋๋จธ์ง๋ ๊ทธ ๋ฐ๋
@ConditionalOnProperty
ํ๊ฒฝ ์ ๋ณด๊ฐ ์๋ ๊ฒฝ์ฐ ๋์.
@ConditionalOnResource
๋ฆฌ์์ค๊ฐ ์๋ ๊ฒฝ์ฐ ๋์.
@ConditionalOnWebApplication, @ConditionalOnNotWebApplication
์น ์ ํ๋ฆฌ์ผ์ด์ ์ธ ๊ฒฝ์ฐ ๋์.
@ConditionalOnExpression
SpEL ํํ์์ ๋ง์กฑํ๋ ๊ฒฝ์ฐ ๋์.
์ฐธ๊ณ . Condition Annotations
์ฃผ๋ก ์คํ๋ง ๋ถํธ ์๋ ๊ตฌ์ฑ์ ์ฌ์ฉ
์๋ ๊ตฌ์ฑ
์๋ ๊ตฌ์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ง๋ค๊ธฐ
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ฑ ํ๋ก์ ํธ
Config ํ์ผ์ ์๋ ๊ตฌ์ฑ ์ถ๊ฐ
@AutoConfiguration @ConditionalOnProperty(name = "memory", havingValue = "on") public class MemoryAutoConfig { @Bean public MemoryController memoryController() { return new MemoryController(memoryFinder()); } @Bean public MemoryFinder memoryFinder() { return new MemoryFinder(); } }
์๋ ๊ตฌ์ฑ ๋์ ํด๋์ค ์ง์
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
memory.MemoryAutoConfig
์คํ๋ง ๋ถํธ๋ ์์ ์์ ์ ํด๋น ํ์ผ์ ์ ๋ณด๋ฅผ ์ฝ์ด์ ์๋ ๊ตฌ์ฑ์ผ๋ก ์ฌ์ฉ
๋ด๋ถ์ ์๋ MemoryAutoConfig๊ฐ ์๋์ผ๋ก ๋น ๋ฑ๋ก
๋น๋:
./gradlew clean build
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ํ๋ก์ ํธ
dependencies ์ถ๊ฐ:
implementation files('libs/memory-v1.jar')
์คํ๋ง ๋ถํธ ์๋ ๊ตฌ์ฑ์ด ์ ์ฉ๋์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์ ์ํ ๋น๋ค์ด ์๋์ผ๋ก ๋ฑ๋ก
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์ ํ์ ์ VM ์ต์ ์ถ๊ฐ:
-Dmemory=on
์คํ๋ง ๋ถํธ์ ์๋ ๊ตฌ์ฑ
์คํ๋ง ๋ถํธ๋ ์๋ ๊ฒฝ๋ก์ ์๋ ํ์ผ์ ์ฝ์ด์ ์คํ๋ง ๋ถํธ ์๋ ๊ตฌ์ฑ์ผ๋ก ์ฌ์ฉ
resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
์คํ๋ง ๋ถํธ๊ฐ ์ ๊ณตํ๋ spring-boot-autoconfigure ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ์๋ ๊ตฌ์ฑ์ ์ฌ์ฉ
org.springframework.boot.autoconfigure.AutoConfiguration.imports
ํด๋น ํ์ผ์ ์ฝ๊ณ ๋์ํ๋ ๋ฐฉ์
@SpringBootApplication
์คํ ->@EnableAutoConfiguration
(์๋ ๊ตฌ์ฑ ํ์ฑํ) ->@Import(AutoConfigurationImportSelector.class)
(์คํ๋ง ์ค์ ์ ๋ณด) ->resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
ํ์ผ์ ์ด์ด์ ์ค์ ์ ๋ณด ์ ํ
ImportSelector
@Import์ ์ค์ ์ ๋ณด๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ
์ ์ ์ธ ๋ฐฉ๋ฒ: ์ฝ๋์ ๋์์ ์ง์
@Configuration @Import({AConfig.class, BConfig.class}) public class AppConfig {...}
๋์ ์ธ ๋ฐฉ๋ฒ: ์ค์ ์ผ๋ก ์ฌ์ฉํ ๋์์ ๋์ ์ผ๋ก ์ ํ
ImportSelector ์ธํฐํ์ด์ค ๊ตฌํ -> ๋จ์ํ hello.selector.HelloConfig ์ค์ ์ ๋ณด ๋ฐํ
package org.springframework.context.annotation; public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); //... }
๋ฐํ๋์ด ์ค์ ์ ๋ณด๋ก ์ฌ์ฉํ ํด๋์ค๋ฅผ ๋์ ์ผ๋ก ํ๋ก๊ทธ๋๋ฐ
public class HelloImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"hello.selector.HelloConfig"}; } } ... @Configuration @Import(HelloImportSelector.class) public static class SelectorConfig { }
์๋๊ตฌ์ฑ์ ์ฌ์ฉ
๋ณดํต ํ์ํ ๋น๋ค์ ์ปดํฌ๋ํธ ์ค์บํ๊ฑฐ๋ ์ง์ ๋ฑ๋กํ๊ธฐ ๋๋ฌธ์, ์๋๊ตฌ์ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ ์ ๊ณตํ ๋ ์ฌ์ฉ
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ ๊ฒฝ์ฐ ๋์ฒ๋ฅผ ์ํด ์คํ๋ง ๋ถํธ์ ์๋ ๊ตฌ์ฑ ์ฝ๋๋ฅผ ์ฝ๊ณ , ํน์ ๋น๋ค์ด ์ด๋ป๊ฒ ๋ฑ๋ก๋ ๊ฒ์ธ์ง ํ์ธ์ ํ ์ ์์ด์ผ ํ๋ค.
์ธ๋ถ์ค์
ํ๊ฒฝ์ ๋ฐ๋ผ ๋ณํ๋ ์ค์ ๊ฐ์ ์คํ ์์ ์ ์ฃผ์
์ค์ ๊ฐ ์ธ๋ถ ์ค์ ์ ํ๋ ์ผ๋ฐ์ ์ธ ๋ค ๊ฐ์ง ๋ฐฉ๋ฒ
OS ํ๊ฒฝ ๋ณ์
: OS์์ ์ง์ํ๋ ์ธ๋ถ ์ค์ . ํด๋น OS๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ํ๋ก์ธ์ค์์ ์ฌ์ฉ์๋ฐ ์์คํ ์์ฑ
: ์๋ฐ์์ ์ง์ํ๋ ์ธ๋ถ ์ค์ . ํด๋น JVM ์์์ ์ฌ์ฉjava -Durl=devdb -Dusername=dev_user -Dpassword=dev_pw -jar app.jar
์๋ฐ ์ปค๋งจ๋ ๋ผ์ธ ์ธ์
: ์ปค๋งจ๋ ๋ผ์ธ์์ ์ ๋ฌํ๋ ์ธ๋ถ ์ค์ . ์คํ์ main(args) ๋ฉ์๋์์ ์ฌ์ฉjava -jar app.jar dataA dataB
์๋ฐ ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์
: ์คํ๋ง์์ ์ปค๋งจ๋ ๋ผ์ธ ์ธ์๋ฅผ key=value ํ์์ผ๋ก ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ํ์ค ๋ฐฉ์(--) ์ ์java -jar app.jar --url=devdb --username=dev_user --password=dev_pw mode=on
์ธ๋ถ ํ์ผ(์ค์ ๋ฐ์ดํฐ)
: ํ๋ก๊ทธ๋จ์์ ์ธ๋ถ ํ์ผ์ ์ง์ ์ฝ์ด์ ์ฌ์ฉ๋ก๋ฉ ์์ ์ ํ์ผ(
.properties
,.yml
)์ ์๋์ผ๋ก ์ฝ์ด์ ๊ทธ ์์ ๊ฐ๋ค์ ์ธ๋ถ ์ค์ ๊ฐ์ผ๋ก ์ฌ์ฉํ๋กํ:
spring.profiles.active={profile}
์ค์ ์ผ๋ก ํ๋กํ ์ง์ ์๋ ๊ท์น์ผ๋ก ์ค์ ํ๋กํ์ ๋ง๋ ๋ด๋ถ ํ์ผ(์ค์ ๋ฐ์ดํฐ) ์กฐํ
application-{profile}.properties
์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์ ์คํ:
--spring.profiles.active=dev
์๋ฐ ์์คํ ์์ฑ ์คํ:
-Dspring.profiles.active=dev
์ถ์ํ(Environment, PropertySource)๋ฅผ ํตํด ์ธ๋ถ ์ค์ ๊ฐ์ด ์ด๋์ ์์นํ๋ ์ผ๊ด์ฑ ์๊ณ , ํธ๋ฆฌํ๊ฒ ์ค์ ๊ฐ์ ์ฝ์ ์ ์์
PropertySource: ์คํ๋ง์ ๋ก๋ฉ ์์ ์ ํ์ํ PropertySource ๋ค์ ์์ฑํ๊ณ , Environment ์์ ์ฌ์ฉํ ์ ์๊ฒ ์ฐ๊ฒฐ
Environment: ๋ชจ๋ ์ธ๋ถ ์ค์ (์ปค๋ฉ๋ ๋ผ์ธ ์ต์ ์ธ์, ์๋ฐ ์์คํ ์์ฑ, OS ํ๊ฒฝ๋ณ์, ์ค์ ํ์ผ)์ Environment ๋ฅผ ํตํด ์กฐํ
์ฐ์ ์์
๋ ์ ์ฐํ ๊ฒ์ด ์ฐ์ ๊ถ
๋ฒ์๊ฐ ๋์ ๊ฒ ๋ณด๋ค ์ข์ ๊ฒ์ด ์ฐ์ ๊ถ
url=local.db.com
username=local_user
password=local_pw
#---
spring.config.activate.on-profile=dev
url=dev.db.com
username=dev_user
password=dev_pw
#---
spring.config.activate.on-profile=prod
url=prod.db.com
username=prod_user
password=prod_pw
๋จ์ํ๊ฒ ๋ฌธ์๋ฅผ ์์์ ์๋๋ก ์์๋๋ก ์ฝ์ผ๋ฉด์ ๊ฐ์ ์ค์ . ๊ธฐ์กด ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ๋ฎ์ด์ฐ๊ธฐ
๋ ผ๋ฆฌ ๋ฌธ์์
spring.config.activate.on-profile
์ต์ ์ด ์์ผ๋ฉด ํด๋น ํ๋กํ์ ์ฌ์ฉํ ๋(--spring.profiles.active)๋ง ๋ ผ๋ฆฌ ๋ฌธ์ ์ ์ฉ
(๋ด๋ ค๊ฐ์๋ก ์ฐ์ ์์ ๋์์ง)
์ค์ ๋ฐ์ดํฐ(application.properties)
OS ํ๊ฒฝ๋ณ์
์๋ฐ ์์คํ ์์ฑ
์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์
@TestPropertySource(in Test)
(๋ด๋ ค๊ฐ์๋ก ์ฐ์ ์์ ๋์์ง)
jar ๋ด๋ถ application.properties
jar ๋ด๋ถ ํ๋กํ ์ ์ฉ ํ์ผ application-{profile}.properties
jar ์ธ๋ถ application.properties
jar ์ธ๋ถ ํ๋กํ ์ ์ฉ ํ์ผ application-{profile}.properties
applicaiton.properties์ ์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๋ค๊ฐ ์ผ๋ถ ์์ฑ์ ๋ณ๊ฒฝํ ํ์๊ฐ ์๊ธฐ๋ฉด ๋ ๋์ ์ฐ์ ์์๋ฅผ ๊ฐ์ง๋ ์๋ฐ ์์คํ ์์ฑ์ด๋ ์ปค๋งจ๋ ๋ผ์ธ ์ต์ ์ธ์๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค.
์ธ๋ถ ์ค์ ์ฌ์ฉ
Environment
Environment๋ก ์ธ๋ถ ์ค์ ์กฐํ
Environment๋ฅผ ์ง์ ์ฃผ์ ๋ฐ๊ณ , env.getProperty(key)๋ฅผ ํตํด ๊ฐ์ ๊บผ๋ด๋ ๊ณผ์ ์ ๋ฐ๋ณตํด์ผ ํ๋ ๋จ์
String url = env.getProperty("my.datasource.url");
String username = env.getProperty("my.datasource.username");
String password = env.getProperty("my.datasource.password");
int maxConnection = env.getProperty("my.datasource.etc.max-connection", Integer.class);
Duration timeout = env.getProperty("my.datasource.etc.timeout", Duration.class);
List<String> options = env.getProperty("my.datasource.etc.options", List.class);
@Value
์ธ๋ถ ์ค์ ๊ฐ์ ํธ๋ฆฌํ๊ฒ ์ฃผ์
๋ด๋ถ์์๋ Environment ์ฌ์ฉ
ํ๋, ํ๋ผ๋ฏธํฐ์ ์ฌ์ฉ ๊ฐ๋ฅ
ํ์ ์ปจ๋ฒํ ์ ์๋์ผ๋ก ์ํ
์ธ๋ถ ์ค์ ์ ๋ณด์ ํค ๊ฐ์ ํ๋ํ๋ ์ ๋ ฅ, ์ฃผ์ ๋ฐ์์ผ ํ๋ ๋จ์
๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ ์:
@Value("${my.datasource.etc.max-connection:1}")
@Value("${my.datasource.url}")
private String url;
@Value("${my.datasource.username}")
private String username;
@Value("${my.datasource.password}")
private String password;
@Value("${my.datasource.etc.max-connection}")
private int maxConnection;
@Value("${my.datasource.etc.timeout}")
private Duration timeout;
@Value("${my.datasource.etc.options}")
private List<String> options;
@ConfigurationProperties
Type-safe Configuration Properties
์ธ๋ถ ์ค์ ์ ๋ฌถ์, ๊ณ์ธต ์ ๋ณด๋ฅผ ๊ฐ์ฒด๋ก ๋ณํํด์ ์ฌ์ฉ
ํ์ ์์ ํ ์ค์ ์์ฑ ์ฌ์ฉ(ํ์ ์ด ๋ค๋ฅด๋ฉด ์ค๋ฅ ๋ฐ์)
์บ๋ฐฅ ํ๊ธฐ๋ฒ์ ๋ํ ํ๊ธฐ๋ฒ์ผ๋ก ์ค๊ฐ์ ์๋์ผ๋ก ๋ณํ
๊ฐ ๋ณ๊ฒฝ ๋ฐฉ์ง๋ฅผ ์ํด ์ธํฐ ๋์ ์์ฑ์๋ฅผ ์ฌ์ฉํ์.
@DefaultValue: ํด๋น ๊ฐ์ ์ฐพ์ ์ ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉ
์ซ์์ ๋ฒ์, ๋ฌธ์์ ๊ธธ์ด ๊ฒ์ฆ์ ์ํด ์๋ฐ ๋น ๊ฒ์ฆ๊ธฐ(java bean validation) ์ฌ์ฉ ๊ฐ๋ฅ
dependency: implementation 'org.springframework.boot:spring-boot-starter-validation
๊ฐ์ฅ ์ข์ ์์ธ๋ ์ปดํ์ผ ์์ธ, ๊ทธ๋ฆฌ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ๋ก๋ฉ ์์ ์ ๋ฐ์ํ๋ ์์ธ.
๊ฐ์ฅ ๋์ ์์ธ๋ ๊ณ ๊ฐ ์๋น์ค ์ค์ ๋ฐ์ํ๋ ๋ฐํ์ ์์ธ
my.datasource.url=local.db.com
my.datasource.username=username
my.datasource.password=password
my.datasource.etc.max-connection=1
my.datasource.etc.timeout=3500ms
my.datasource.etc.options=CACHE,ADMIN
...
@Getter
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertiesV2 {
private String url;
private String username;
private String password;
private Etc etc;
public MyDataSourcePropertiesV2(String url, String username, String password, @DefaultValue Etc etc) {
this.url = url;
this.username = username;
this.password = password;
this.etc = etc;
}
@Getter
public static class Etc {
private int maxConnection;
private Duration timeout;
private List<String> options;
public Etc(int maxConnection, Duration timeout, @DefaultValue("DEFAULT") List<String> options) {
this.maxConnection = maxConnection;
this.timeout = timeout;
this.options = options;
}
}
}
YAML
YAML(YAML Ain't Markup Language)์ ์ฝ๊ธฐ ์ข์ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ชฉํ
ํ์ฅ์๋ yaml, yml(์ฃผ๋ก ์ฌ์ฉ)
application.properties, application.yml ๋์ ์ฌ์ฉ ์ application.properties ์ฐ์ ๊ถ
---
๋ก ๋ ผ๋ฆฌ ํ์ผ ๊ตฌ๋ถspring.config.active.on-profile ๋ก ํ๋กํ ์ ์ฉ
--spring.profiles.active=dev
@Profile
@Profile ์ ๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฉด ํด๋น ํ๋กํ์ด ํ์ฑํ๋ ๊ฒฝ์ฐ์๋ง ๋น ๋ฑ๋ก
ํน์ ์กฐ๊ฑด์ ๋ฐ๋ผ ํด๋น ๋น์ ๋ฑ๋กํ ์ง ๋ง์ง ์ ํ
๊ฐ ํ๊ฒฝ ๋ณ๋ก ์ธ๋ถ ์ค์ ๊ฐ, ๋ฑ๋ก๋๋ ์คํ๋ง ๋น ๋ถ๋ฆฌ
์คํ๋ง์ @Conditional ๊ธฐ๋ฅ์ ํ์ฉํด์ @Profile ๊ธฐ๋ฅ์ ์ ๊ณต
์ก์ธ์์ดํฐ
๋ชจ๋ํฐ๋ง ๋์์ ์ํด ์๋น์ค์ ๋ฌธ์ ๊ฐ ์๋์ง ๋ชจ๋ํฐ๋งํ๊ณ , ์งํ๋ค์ ์ฌ์ด์ ๊ฐ์ํ๋ ํ๋์ด ์ค์
ํ๋ก๋์ ์ค๋น ๊ธฐ๋ฅ: ํ๋ก๋์ ์ ์ด์์ ๋ฐฐํฌํ ๋ ์ค๋นํด์ผ ํ๋ ๋น ๊ธฐ๋ฅ์ ์์
์ ํ๋ฆฌ์ผ์ด์ ์ด ์ด์์๋์ง, ๋ก๊ทธ ์ ๋ณด๋ ์ ์ ์ค์ ๋์๋์ง, ์ปค๋ฅ์ ํ์ ์ผ๋ง๋ ์ฌ์ฉ๋๊ณ ์๋์ง ๋ฑ ํ์ธ ํ์
์งํ(metric): CPU ์ฌ์ฉ๋
์ถ์ (trace): ์ด์ ์ฝ๋ ์ถ์
๊ฐ์ฌ(auditing): ๊ณ ๊ฐ ๋ก๊ทธ์ธ, ๋ก๊ทธ์์ ์ด๋ ฅ ์ถ์
๋ชจ๋ํฐ๋ง: ์์คํ ์ํ
Dependency
implementation 'org.springframework.boot:spring-boot-starter-actuator'
http://localhost:8080/actuator
๋ก ํ์ธ ๊ฐ๋ฅ์ ํ๋ฆฌ์ผ์ด์ ์ํ ์ ๋ณด:
http://localhost:8080/actuator/health
๊ฐ ์๋ํฌ์ธํธ๋ /actuator/{์๋ํฌ์ธํธ๋ช } ํ์์ผ๋ก ์ ๊ทผ
๋ ๋ง์ ๊ธฐ๋ฅ์ ์ ๊ณต๋ฐ๊ธฐ ์ํด ์๋ํฌ์ธํธ ๋ ธ์ถ ์ค์ ์ถ๊ฐ(๋ชจ๋ ์๋ํฌ์ธํธ๋ฅผ ์น์ ๋ ธ์ถ)
์๋ํฌ์ธํธ๋ shutdown ์ ์ธํ๊ณ ๋๋ถ๋ถ ๊ธฐ๋ณธ์ผ๋ก ํ์ฑํ
ํน์ ์๋ํฌ์ธํธ ํ์ฑํ ์
management.endpoint.{์๋ํฌ์ธํธ๋ช }.enabled=true
management: endpoints: web: exposure: include: "*"
/actuator:
/actuator/
beans
: ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋ก๋ ์คํธ๋ง ๋น ๋ชฉ๋ก/actuator/
caches
:/actuator/caches/{cache}:
/actuator/
health
: ์ ํ๋ฆฌ์ผ์ด์ ๋ฌธ์ ๋ฅผ ๋น ๋ฅด๊ฒ ์ธ์ง(์ ์ฒด ์ํ, db, mongo, redis, diskspace, ping ๋ฑ ํ์ธ ๊ฐ๋ฅ)/actuator/health/{*path}:
ํฌ์ค ์ปดํฌ๋ํธ ์ค ํ๋๋ผ๋ ๋ฌธ์ ๊ฐ ์์ผ๋ฉด ์ ์ฒด ์ํ๋ DOWN
ํฌ์ค ์ ๋ณด๋ฅผ ๋ ์์ธํ ๋ณด๊ธฐ ์ํ ์ต์
management.endpoint.health.show-details=always
๊ฐ๋ตํ ๋ณด๊ธฐ ์ํ ์ต์
management.endpoint.health.show-components=always
/actuator/
info
: ์ ํ๋ฆฌ์ผ์ด์ ๊ธฐ๋ณธ ์ ๋ณด (default ๋นํ์ฑํ)management.info.<id>.enabled=true
java : ์๋ฐ ๋ฐํ์ ์ ๋ณด
os : OS ์ ๋ณด
env : Environment ์์ info. ๋ก ์์ํ๋ ์ ๋ณด
build : ๋น๋ ์ ๋ณด (META-INF/build-info.properties ํ์ผ ํ์)
// build.gradle ์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ฉด ์๋์ผ๋ก ๋น๋ ์ ๋ณด ํ์ผ ์์ฑ springBoot { buildInfo() }
git : git ์ ๋ณด (git.properties ํ์ผ ํ์)
// git.properties plugin ์ถ๊ฐ id "com.gorylenko.gradle-git-properties" version "2.4.1"
/actuator/
conditions
: condition์ ํตํด ๋น ๋ฑ๋ก ์ ํ๊ฐ ์กฐ๊ฑด๊ณผ ์ผ์นํ๊ฑฐ๋ ์ผ์นํ์ง ์๋ ์ด์ ํ์/actuator/
configprops
: @ConfigurationProperties ๋ชฉ๋ก/actuator/configprops/{prefix}:
/actuator/
env
: Environment ์ ๋ณด/actuator/env/{toMatch}:
/actuator/
loggers
: ๋ก๊น ๊ด๋ จ ์ ๋ณด ํ์ธ. ์ค์๊ฐ ๋ณ๊ฒฝํน์ ํจํค์ง์ ๋ก๊ทธ ๋ ๋ฒจ ์ค์ (default. INFO)
logging.level.hello.controller: debug
ํน์ ๋ก๊ฑฐ ์ด๋ฆ ๊ธฐ์ค์ผ๋ก ์กฐํ. /actuator/
loggers/{name}
/actuator/loggers/hello.controller
์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ ์์ํ์ง ์๊ณ , (๋ฉ๋ชจ๋ฆฌ์) ์ค์๊ฐ์ผ๋ก ๋ก๊ทธ ๋ ๋ฒจ ๋ณ๊ฒฝ
POST http://localhost:8080/actuator/loggers/hello.controller { "configuredLevel": "TRACE" }
/actuator/
heapdump
:/actuator/
threaddump
: ์ฐ๋ ๋ ๋คํ ์ ๋ณด/actuator/
metrics
: : ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉํธ๋ฆญ ์ ๋ณด/actuator/metrics/{requiredMetricName}
/actuator/metrics/jvm.memory.used : JVM ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ --- availableTags /actuator/metrics/jvm.memory.used?tag=area:heap /actuator/metrics/http.server.requests : HTTP ์์ฒญ์ --- availableTags /actuator/metrics/http.server.requests?tag=uri:/log /actuator/metrics/http.server.requests?tag=uri:/log&tag=status:200
/actuator/
scheduledtasks
:/actuator/
mappings
: @RequestMapping ์ ๋ณด ๋ชฉ๋ก/
httpexchanges
: HTTP ํธ์ถ ์๋ต ์ ๋ณด. HttpExchangeRepository ๊ตฌํ ๋น ๋ฑ๋ก ํ์์ต๋ 100๊ฐ์ HTTP ์์ฒญ ์ ๊ณต(์ต๋ ์์ฒญ ์ด๊ณผ ์ ๊ณผ๊ฑฐ ์์ฒญ์ ์ญ์
setCapacity() ๋ก ์ต๋ ์์ฒญ์๋ฅผ ๋ณ๊ฒฝ ๊ฐ๋ฅ
๋จ์ํ๊ณ ์ ํ์ด ๋ง์ ๊ธฐ๋ฅ์ด๋ฏ๋ก ๊ฐ๋ฐ ๋จ๊ณ์์๋ง ์ฃผ๋ก ์ฌ์ฉํ๊ณ , ์ค์ ์ด์ ์๋น์ค์์๋ ๋ชจ๋ํฐ๋ง ํด์ด๋ ํํฌ์ธํธ, Zipkin ๊ฐ์ ๋ค๋ฅธ ๊ธฐ์ ์ฌ์ฉ ์ถ์ฒ
/
shutdown
: ์ ํ๋ฆฌ์ผ์ด์ ์ข ๋ฃ. ๊ธฐ๋ณธ์ผ๋ก ๋นํ์ฑํ
๋ณด์์ ์ํด ๋ด๋ถ๋ง์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ํฌํธ ์ค์
management.server.port=9292
์ธ๋ถ๋ง์ ํตํด ์ ๊ทผ์ด ํ์ํ๋ค๋ฉด /actuator ๊ฒฝ๋ก์ ์๋ธ๋ฆฟ ํํฐ, ์คํ๋ง ์ธํฐ์ ํฐ, ์คํ๋ง ์ํํฐ๋ฆฌ๋ฅผ ํตํด ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ์ค์ ํ์
๋ชจ๋ํฐ๋ง
์๋น์ค ์ด์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ CPU, ๋ฉ๋ชจ๋ฆฌ, ์ปค๋ฅ์ ์ฌ์ฉ, ๊ณ ๊ฐ ์์ฒญ ์ ๊ฐ์ ์ ๋ง์ ์งํ๋ค์ ํ์ธํ๋ ๊ฒ์ด ํ์ํ๋ค.
๊ทธ๋์ผ ์ด๋์ ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง ์ฌ์ ๋์์ด ๊ฐ๋ฅํ๊ณ , ์ค์ ๋ฌธ์ ๊ฐ ๋ฐ์ํด๋ ์์ธ์ ๋น ๋ฅด๊ฒ ํ์ ํ๊ณ ๋์ฒํ ์ ์๋ค.
๋ง์ดํฌ๋ก๋ฏธํฐ
์ ๋ง์ ๋ชจ๋ํฐ๋ง ํด์ด ์๊ณ , ๊ฐ ํด๋ง๋ค ์ ๋ฌ ๋ฐฉ์์ด ๋ค๋ฅธ๋ฐ ์ด ๋ชจ๋ ๊ฒ๋ค์ ์ถ์ํํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ง์ดํฌ๋ก๋ฏธํฐ(Micrometer)

๋ง์ดํฌ๋ก๋ฏธํฐ๋ application metric facade ๋ผ๊ณ ๋ถ๋ฆฌ๋๋ฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉํธ๋ฆญ(์ธก์ ์งํ)์ ๋ง์ดํฌ๋ก๋ฏธํฐ๊ฐ ์ ํ ํ์ค ๋ฐฉ๋ฒ์ผ๋ก ๋ชจ์์ ์ ๊ณต (์ถ์ํ๋ ๋ง์ดํฌ๋ก๋ฏธํฐ๋ก ๊ตฌํ์ฒด๋ฅผ ์ฝ๊ฒ ๊ฐ์๋ผ์ธ ์ ์์)
spring boot actuator ๋ ๋ง์ดํฌ๋ก๋ฏธํฐ๋ฅผ ๊ธฐ๋ณธ ๋ด์ฅํด์ ์ฌ์ฉ
๊ฐ๋ฐ์๋ ๋ง์ดํฌ๋ก๋ฏธํฐ๊ฐ ์ ํ ํ์ค ๋ฐฉ๋ฒ์ผ๋ก ๋ฉํธ๋ฆญ(์ธก์ ์งํ)๋ฅผ ์ ๋ฌ
์ฌ์ฉํ๋ ๋ชจ๋ํฐ๋ง ํด์ ๋ง๋ ๊ตฌํ์ฒด ์ ํ
์ดํ ๋ชจ๋ํฐ๋ง ํด์ด ๋ณ๊ฒฝ๋์ด๋ ํด๋น ๊ตฌํ์ฒด๋ง ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋
์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๋ ๋ชจ๋ํฐ๋ง ํด์ด ๋ณ๊ฒฝ๋์ด๋ ๊ทธ๋๋ก ์ ์ง ๊ฐ๋ฅ
๋ฉํธ๋ฆญ
๋ง์ดํฌ๋ก๋ฏธํฐ์ ์ก์ธ์์ดํฐ๊ฐ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ๋ค์ํ ๋ฉํธ๋ฆญ
JVM ๋ฉํธ๋ฆญ
jvm.*
๋ฉ๋ชจ๋ฆฌ ๋ฐ ๋ฒํผ ํ ์ธ๋ถ ์ ๋ณด
๊ฐ๋น์ง ์์ง ๊ด๋ จ ํต๊ณ
์ค๋ ๋ ํ์ฉ
๋ก๋ ๋ฐ ์ธ๋ก๋๋ ํด๋์ค ์
JVM ๋ฒ์ ์ ๋ณด
JIT ์ปดํ์ผ ์๊ฐ
์์คํ ๋ฉํธ๋ฆญ
system.*, process.*, disk.*
CPU ์งํ
ํ์ผ ๋์คํฌ๋ฆฝํฐ ๋ฉํธ๋ฆญ
๊ฐ๋ ์๊ฐ ๋ฉํธ๋ฆญ
์ฌ์ฉ ๊ฐ๋ฅํ ๋์คํฌ ๊ณต๊ฐ
์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฉํธ๋ฆญ
application.*
application.started.time: ์ ํ๋ฆฌ์ผ์ด์ ์์์ ๊ฑธ๋ฆฌ๋ ์๊ฐ (ApplicationStartedEvent๋ก ์ธก์ - ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์์ ํ ์คํ๋ ์ํ. ์ดํ์ ์ปค๋งจ๋ ๋ผ์ธ ๋ฌ๋๊ฐ ํธ์ถ)
application.ready.time : ์ ํ๋ฆฌ์ผ์ด์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ค๋น๊ฐ ๋๋๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ (ApplicationReadyEvent ๋ก ์ธก์ - ์ปค๋งจ๋ ๋ผ์ธ ๋ฌ๋๊ฐ ์คํ๋ ์ดํ์ ํธ์ถ)
์คํ๋ง MVC ๋ฉํธ๋ฆญ
http.server.requests: ์คํ๋ง MVC ์ปจํธ๋กค๋ฌ๊ฐ ์ฒ๋ฆฌํ๋ ๋ชจ๋ ์์ฒญ ์ ๋ณด
TAG ๋ก ์ ๋ณด๋ฅผ ๋ถ๋ฅํด์ ํ์ธ ๊ฐ๋ฅ
uri : ์์ฒญ URI
method : HTTP ๋ฉ์๋(GET, POST..)
status : HTTP Status ์ฝ๋(200, 400, 500..)
exception : ์์ธ
outcome : ์ํ์ฝ๋ ๊ทธ๋ฃน(1xx:INFORMATIONAL, 2xx:SUCCESS, 3xx:REDIRECTION, 4xx:CLIENT_ERROR, 5xx:SERVER_ERROR)
ํฐ์บฃ ๋ฉํธ๋ฆญ
tomcat.
ํฐ์บฃ์ ์ต๋ ์ฐ๋ ๋, ์ฌ์ฉ ์ฐ๋ ๋ ์๋ฅผ ํฌํจํ ๋ค์ํ ๋ฉํธ๋ฆญ ํ์ธ ๊ฐ๋ฅ
server: tomcat: mbeanregistry: enabled: true
๋ฐ์ดํฐ ์์ค ๋ฉํธ๋ฆญ: DataSource, Connection Pool ๊ด๋ จ ๋ฉํธ๋ฆญ ์ ๋ณด
jdbc.connections.
์ต๋ ์ปค๋ฅ์ , ์ต์ ์ปค๋ฅ์ , ํ์ฑ ์ปค๋ฅ์ , ๋๊ธฐ ์ปค๋ฅ์ ์ ๋ฑ ํ์ธ ๊ฐ๋ฅ
๋ก๊ทธ ๋ฉํธ๋ฆญ : logback ๋ก๊ทธ์ ๋ํ ๋ฉํธ๋ฆญ ์ ๋ณด
trace, debug, info, warn, error ๊ฐ ๋ก๊ทธ ๋ ๋ฒจ์ ๋ฐ๋ฅธ ๋ก๊ทธ ์ ํ์ธ ๊ฐ๋ฅ
๊ธฐํ
HTTP ํด๋ผ์ด์ธํธ ๋ฉํธ๋ฆญ(RestTemplate , WebClient)
์บ์ ๋ฉํธ๋ฆญ
์์ ์คํ๊ณผ ์ค์ผ์ค ๋ฉํธ๋ฆญ
์คํ๋ง ๋ฐ์ดํฐ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉํธ๋ฆญ
๋ชฝ๊ณ DB ๋ฉํธ๋ฆญ
๋ ๋์ค ๋ฉํธ๋ฆญ
์ปค์คํ ๋ฉํธ๋ฆญ
ํ๋ก๋ฉํ
์ฐ์ค & ๊ทธ๋ผํ๋
ํ๋ก๋ฉํ ์ฐ์ค
: ๋ฉํธ๋ฆญ์ ์ง์ํด์ ์์งํ๊ณ DB์ ์ ์ฅํ๋ ์ญํ๊ทธ๋ผํ๋
: ํ๋ก๋ฉํ ์ฐ์ค์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ํ๋ก ๋ณด์ฌ์ฃผ๋ ํด๋ค์ํ ๊ทธ๋ํ๋ฅผ ์ ๊ณตํ๊ณ , ํ๋ก๋ฉํ ์ฐ์ค๋ฅผ ํฌํจํ ๋ค์ํ ๋ฐ์ดํฐ์์ค ์ง์

\1. ์คํ๋ง ๋ถํธ ์ก์ธ์์ดํฐ, ๋ง์ดํฌ๋ก๋ฏธํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ง์ ๋ฉํธ๋ฆญ ์๋ ์์ฑ
๋ง์ดํฌ๋ก๋ฏธํฐ ํ๋ก๋ฉํ ์ฐ์ค ๊ตฌํ์ฒด๋ ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์ฝ์ ์ ์๋ ํฌ๋ฉง์ผ๋ก ๋ฉํธ๋ฆญ์ ์์ฑ
\2. ํ๋ก๋ฉํ ์ฐ์ค๋ ์ด๋ ๊ฒ ๋ง๋ค์ด์ง ๋ฉํธ๋ฆญ์ ์ง์ํด์ ์์ง
\3. ํ๋ก๋ฉํ ์ฐ์ค๋ ์์งํ ๋ฉํธ๋ฆญ์ ๋ด๋ถ DB์ ์ ์ฅ
\4. ์ฌ์ฉ์๋ ๊ทธ๋ผํ๋ ๋์๋ณด๋ ํด์ ํตํด ๊ทธ๋ํ๋ก ํธ๋ฆฌํ๊ฒ ๋ฉํธ๋ฆญ์ ์กฐํ(ํ์ํ ๋ฐ์ดํฐ๋ ํ๋ก๋ฉํ ์ฐ์ค๋ฅผ ํตํด ์กฐํ)
ํ๋ก๋ฉํ
์ฐ์ค
๋ฉํธ๋ฆญ์ ์์งํ๊ณ ๋ณด๊ดํ๋ DB
.
์ค์น
https://prometheus.io/download/
https://github.com/prometheus/prometheus/releases/download/v2.42.0/prometheus-2.42.0.darwin-amd64.tar.gz
Mac OS: darwin
์คํ
์์คํ ํ๊ฒฝ์ค์ - ๋ณด์ ๋ฐ ๊ฐ์ธ ์ ๋ณด ๋ณดํธ - ์ผ๋ฐ - ํ์ธ ์์ด ํ์ฉ
terminal -
./prometheus
http://localhost:9090/
์ ํ๋ฆฌ์ผ์ด์ ์ค์
์ ํ๋ฆฌ์ผ์ด์ ์ค์
ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉํธ๋ฆญ์ ๊ฐ์ ธ๊ฐ ์ ์๋๋ก, ํ๋ก๋ฉํ ์ฐ์ค ํฌ๋ฉง์ ๋ง์ถ์ด ๋ฉํธ๋ฆญ ์์ฑ
๊ฐ ๋ฉํธ๋ฆญ๋ค์ ๋ด๋ถ์์ ๋ง์ดํฌ๋ก๋ฏธํฐ ํ์ค ๋ฐฉ์์ผ๋ก ์ธก์ ๋์ด ์ด๋ค ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ ์ง๋ง ์ง์
// ์คํ๋ง ๋ถํธ์ ์ก์ธ์์ดํฐ๊ฐ ์๋์ผ๋ก ๋ง์ดํฌ๋ก๋ฏธํฐ ํ๋ก๋ฉํ ์ฐ์ค ๊ตฌํ์ฒด๋ฅผ ๋ฑ๋กํด์ ๋์ํ๋๋ก ์ค์ implementation 'io.micrometer:micrometer-registry-prometheus'
์ก์ธ์์ดํฐ์ ํ๋ก๋ฉํ ์ฐ์ค ๋ฉํธ๋ฆญ ์์ง ์๋ํฌ์ธํธ๊ฐ ์๋ ์ถ๊ฐ
/actuator/prometheus
ํ๋ก๋ฉํ ์ฐ์ค ์ค์
ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉํธ๋ฆญ์ ์ฃผ๊ธฐ์ ์ผ๋ก ์์งํ๋๋ก ์ค์
์์ง ์ค์
prometheus.yml
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
# ํ๋จ ์ถ๊ฐ
- job_name: "spring-actuator" # ์์งํ๋ ์์ ์ด๋ฆ
metrics_path: '/actuator/prometheus' # ์์ง ๊ฒฝ๋ก ์ง์ (1์ด์ ํ ๋ฒ์ฉ ํธ์ถํด์ ๋ฉํธ๋ฆญ ์์ง)
scrape_interval: 1s # ์์ง ์ฃผ๊ธฐ (10s~1m ๊ถ์ฅ)
static_configs: # ์์งํ ์๋ฒ ์ ๋ณด(IP, PORT)
- targets: ['localhost:8080']
http://localhost:9090/
ํ๋ก๋ฉํ ์ฐ์ค ๋ฉ๋ด -> Status -> Configuration, Targets ์์ ์ถ๊ฐํ ์ค์ ํ์ธ
์์ฃผ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ
๊ธฐ๋ณธ๊ธฐ๋ฅ
Table: Evaluation time ์ ์์ ํด์ ๊ณผ๊ฑฐ ์๊ฐ ์กฐํ
Graph: ๋ฉํธ๋ฆญ์ ๊ทธ๋ํ๋ก ์กฐํ
ํํฐ: ๋ ์ด๋ธ ๊ธฐ์ค์ผ๋ก ํํฐ ์ฌ์ฉ. ์ค๊ดํธ(
{}
) ๋ฌธ๋ฒ ์ฌ์ฉ๋ ์ด๋ธ ์ผ์น ์ฐ์ฐ์
=
: ์ ๊ณต๋ ๋ฌธ์์ด๊ณผ ์ ํํ ๋์ผํ ๋ ์ด๋ธ ์ ํ!=
: ์ ๊ณต๋ ๋ฌธ์์ด๊ณผ ๊ฐ์ง ์์ ๋ ์ด๋ธ ์ ํ=~
: ์ ๊ณต๋ ๋ฌธ์์ด๊ณผ ์ ๊ท์ ์ผ์นํ๋ ๋ ์ด๋ธ ์ ํ!~
: ์ ๊ณต๋ ๋ฌธ์์ด๊ณผ ์ ๊ท์ ์ผ์นํ์ง ์๋ ๋ ์ด๋ธ ์ ํ
example. uri=/log , method=GET ์กฐ๊ฑด์ผ๋ก ํํฐ -> http_server_requests_seconds_count{uri="/log", method="GET"} /actuator/prometheus ๋ ์ ์ธํ ์กฐ๊ฑด์ผ๋ก ํํฐ -> http_server_requests_seconds_count{uri!="/actuator/prometheus"} method ๊ฐ GET, POST ์ธ ๊ฒฝ์ฐ๋ฅผ ํฌํจํด์ ํํฐ -> http_server_requests_seconds_count{method=~"GET|POST"} /actuator ๋ก ์์ํ๋ uri ๋ ์ ์ธํ ์กฐ๊ฑด์ผ๋ก ํํฐ -> http_server_requests_seconds_count{uri!~"/actuator.*"}
์ฐ์ฐ์ ์ฟผ๋ฆฌ์ ํจ์
+
(๋ง์ ),-
(๋นผ๊ธฐ),*
(๊ณฑ์ ),/
(๋ถํ ),%
(๋ชจ๋๋ก),^
(์น์/์ง์)sum: ํฉ๊ณ
sum(http_server_requests_seconds_count)
sum by: SQL group by ์ ์ ์ฌ
sum by(method, status)(http_server_requests_seconds_count)
count: ๋ฉํธ๋ฆญ ์์ฒด์ ์ ์นด์ดํธ
count(http_server_requests_seconds_count)
topk: ์์ ๋ฉํธ๋ฆญ ์กฐํ
topk(3, http_server_requests_seconds_count)
์คํ์ ์์ ์
http_server_requests_seconds_count offset 10m
ํ์ฌ ๊ธฐ์ค ํน์ ๊ณผ๊ฑฐ ์์ ์ ๋ฐ์ดํฐ ๋ฐํ
๋ฒ์ ๋ฒกํฐ ์ ํ๊ธฐ
http_server_requests_seconds_count[1m]
์ง๋ 1๋ถ๊ฐ์ ๋ชจ๋ ๊ธฐ๋ก๊ฐ ์ ํ
์ฐจํธ์ ๋ฐ๋ก ํํํ ์ ์๊ณ , ๋ฐ์ดํฐ๋ก๋ ํ์ธ ๊ฐ๋ฅ
๊ฒฐ๊ณผ๋ฅผ ์ฐจํธ์ ํํํ๊ธฐ ์ํด์๋ ์ฝ๊ฐ์ ๊ฐ๊ณต ํ์
๊ฒ์ด์ง์ ์นด์ดํฐ
๊ฒ์ด์ง
(Gauge)
์์๋ก ์ค๋ฅด๋ด์ผ ์ ์๋ ๊ฐ(ex. CPU ์ฌ์ฉ๋, ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋, ์ฌ์ฉ์ค์ธ ์ปค๋ฅ์ )
์นด์ดํฐ
(Counter)
๋จ์ํ๊ฒ ์ฆ๊ฐํ๋ ๋จ์ผ ๋์ ๊ฐ(ex. HTTP ์์ฒญ ์, ๋ก๊ทธ ๋ฐ์ ์)
increase()
์ง์ ํ ์๊ฐ ๋จ์๋ณ๋ก ์ฆ๊ฐ ํ์ธ
increase(http_server_requests_seconds_count{uri="/log"}[1m])
rate()
๋ฒ์ ๋ฐฑํฐ์์ ์ด๋น ํ๊ท ์ฆ๊ฐ์จ ๊ณ์ฐ
irate()
rate ์ ์ ์ฌ. ๋ฒ์ ๋ฒกํฐ์์ ์ด๋น ์๊ฐ ์ฆ๊ฐ์จ ๊ณ์ฐ
๊ทธ๋ผํ๋
https://grafana.com/grafana/download
https://dl.grafana.com/enterprise/release/grafana-enterprise-9.3.6.darwin-amd64.tar.gz
์คํ
์์ถ์ ํ๊ณ bin ํด๋ ์ด๋ ํ
./grafana-server
http://localhost:3000/
์ด๊ธฐ ๊ณ์ -> admin/admin
์ฐ๋
๋ฐ์ดํฐ์์ค ์ถ๊ฐ
์ค์ (Configuration) -> Data sources -> Add data source -> Prometheus
URL ์ ๋ณด: http://localhost:9090
Save & test
๋์๋ณด๋
์ ํ๋ฆฌ์ผ์ด์ , ํ๋ก๋ฉํ ์ฐ์ค, ๊ทธ๋ผํ๋๊ฐ ๋ชจ๋ ์คํ์ค์ธ ์ํ์ฌ์ผ ํ๋ค.
\1. ์ผ์ชฝ Dashboards ๋ฉ๋ด
\2. New ๋ฒํผ -> New Dashboard
\3. ์ค๋ฅธ์ชฝ ์๋จ Save dashboard ์ ์ฅ
\4. Dashboard name ์ ๋ ฅ
๋งคํธ๋ฆญ ํ์ฉ
๋น์ฆ๋์ค์ ํนํ๋ ๋ถ๋ถ(์ฃผ๋ฌธ์, ์ทจ์์, ์ฌ๊ณ ์๋ ๋ฑ)์ ๋ชจ๋ํฐ๋งํ๊ธฐ ์ํด ์ง์ ๋ฉํธ๋ฆญ ๋ฑ๋ก ๊ฐ๋ฅ
MeterRegistry
๋ง์ดํฌ๋ก๋ฏธํฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํต์ฌ ์ปดํฌ๋ํธ
์คํ๋ง์ ํตํด์ ์ฃผ์ ๋ฐ์์ ์ฌ์ฉํ๊ณ , ์นด์ดํฐ, ๊ฒ์ด์ง ๋ฑ์ ๋ฑ๋ก
Counter
๋จ์กฐ๋กญ๊ฒ ์ฆ๊ฐํ๋ ๋จ์ผ ๋์ ์ธก์ ํญ๋ชฉ
๋จ์ผ ๊ฐ, ๋ณดํต ํ๋์ฉ ์ฆ๊ฐ, ๋์ ์ด๋ฏ๋ก ์ ์ฒด ๊ฐ์ ํฌํจ(total)
๊ฐ์ ์ฆ๊ฐํ๊ฑฐ๋ 0์ผ๋ก ์ด๊ธฐํ ํ๋ ๊ธฐ๋ฅ๋ง ๊ฐ๋ฅ
๋ง์ดํฌ๋ก๋ฏธํฐ์์ ๊ฐ์ ๊ฐ์ํ๋ ๊ธฐ๋ฅ๋ ์ง์ํ์ง๋ง, ๋ชฉ์ ์ ๋ง์ง ์์
์) HTTP ์์ฒญ์ (increase() , rate() ํ์ฉ)
Gauge
์์๋ก ์ค๋ฅด๋ด๋ฆด ์ ์๋ ๋จ์ผ ์ซ์ ๊ฐ์ ๋ํ๋ด๋ ๋ฉํธ๋ฆญ
๊ฐ์ ํ์ฌ ์ํ๋ฅผ ๋ณด๋๋ฐ ์ฌ์ฉ(๊ฐ์ด ์ฆ๊ฐํ๊ฑฐ๋ ๊ฐ์)
์นด์ดํฐ, ๊ฒ์ด์ง์ ์ฌ์ด ๊ตฌ๋ถ์ ์ํด ๊ฐ์ด ๊ฐ์ํ ์ ์๋๊ฐ๋ฅผ ๊ณ ๋ฏผํด๋ณด์
ex. ์ฐจ๋ ์๋, CPU ์ฌ์ฉ๋, ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ..
Timer
์๊ฐ ์ธก์ ์ ์ฌ์ฉ
์คํ ์๊ฐ๋ ํจ๊ป ์ธก์ ๊ฐ๋ฅ(์นด์ดํฐ์ ์ ์ฌ)
์๋ ๋ด์ฉ์ ํ ๋ฒ์ ์ธก์
seconds_count : ๋์ ์คํ ์() - counter
increase(my_order_seconds_count{method="order"}[1m])
seconds_sum : ์คํ ์๊ฐ์ ํฉ - sum
seconds_max : ์ต๋ ์คํ ์๊ฐ(๊ฐ์ฅ ์ค๋๊ฑธ๋ฆฐ ์คํ ์๊ฐ) - gauge
1~3๋ถ ๋ง๋ค ์ต๋ ์คํ ์๊ฐ์ ๋ค์ ๊ณ์ฐ(๋ด๋ถ์ ํ์ ์๋์ฐ๋ผ๋ ๊ฐ๋ ์กด์ฌ)
my_order_seconds_max
seconds_sum / seconds_count = ํ๊ท ์คํ์๊ฐ
increase(my_order_seconds_sum[1m]) / increase(my_order_seconds_count[1m])
Tag, ๋ ์ด๋ธ
๋ฐ์ดํฐ๋ฅผ ๋๋ ์ ํ์ธ ๊ฐ๋ฅ
์นด๋๋๋ฆฌํฐ(ํน์ ๋ฐ์ดํฐ ์งํฉ์ ์ ๋ํฌํ ๊ฐ์ ๊ฐ์)๊ฐ ๋ฎ์ผ๋ฉด์ ๊ทธ๋ฃนํ ํ ์ ์๋ ๋จ์์ ์ฌ์ฉ
ex. ์ฑ๋ณ, ์ฃผ๋ฌธ ์ํ, ๊ฒฐ์ ์๋จ[์ ์ฉ์นด๋, ํ๊ธ] ..
์นด๋๋๋ฆฌํฐ๊ฐ ๋์ผ๋ฉด ๋ถ๊ฐ - ex. ์ฃผ๋ฌธ๋ฒํธ, PK ..
๋ชจ๋ํฐ๋ง ํ๊ฒฝ ๊ตฌ์ฑ ํ
๋ชจ๋ํฐ๋ง 3๋จ๊ณ
๋์๋ณด๋
์ ํ๋ฆฌ์ผ์ด์ ์ถ์
๋ก๊ทธ
.
๋์๋ณด๋
์ ์ฒด๋ฅผ ํ๋์ ๋ณผ ์ ์๋ ๊ฐ์ฅ ๋์ ๋ทฐ
์ ํ
๋ง์ดํฌ๋ก๋ฏธํฐ, ํ๋ก๋ฉํ ์ฐ์ค, ๊ทธ๋ผํ๋ ๋ฑ
๋ชจ๋ํฐ๋ง ๋์
์์คํ ๋ฉํธ๋ฆญ (CPU, ๋ฉ๋ชจ๋ฆฌ)
์ ํ๋ฆฌ์ผ์ด์ ๋ฉํธ๋ฆญ (ํฐ์บฃ ์ฐ๋ ๋ ํ, DB ์ปค๋ฅ์ ํ, ์ ํ๋ฆฌ์ผ์ด์ ํธ์ถ ์)
๋น์ฆ๋์ค ๋ฉํธ๋ฆญ (์ฃผ๋ฌธ์, ์ทจ์์)
.
์ ํ๋ฆฌ์ผ์ด์
์ถ์
์ฃผ๋ก ๊ฐ๊ฐ์ HTTP ์์ฒญ์ ์ถ์ , ์ผ๋ถ๋ ๋ง์ดํฌ๋ก์๋น์ค ํ๊ฒฝ์์ ๋ถ์ฐ ์ถ์
์ ํ
ํํฌ์ธํธ(์คํ์์ค) - ์ถ์ฒ
์ค์นด์ฐํธ(์คํ์์ค)
์ํญ(์์ฉ)
์ ๋ํผ(์์ฉ)
.
๋ก๊ทธ
๊ฐ์ฅ ์์ธํ ์ถ์ , ์ํ๋๋ฐ๋ก ์ปค์คํ ๊ฐ๋ฅ
๊ฐ์ HTTP ์์ฒญ์ ๋ฌถ์ด์ ํ์ธํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์ค์(MDC ์ ์ฉ)
ํ์ผ๋ก ์ง์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ๊ฒฝ์ฐ
์ผ๋ฐ ๋ก๊ทธ์ ์๋ฌ ๋ก๊ทธ๋ ํ์ผ์ ๊ตฌ๋ถํด์ -> ์๋ฌ ๋ก๊ทธ๋ง ํ์ธํด์ ๋ฌธ์ ๋ฅผ ๋ฐ๋ก ์ ๋ฆฌ ๊ฐ๋ฅ
ํด๋ผ์ฐ๋์ ๋ก๊ทธ๋ฅผ ์ ์ฅํ๋ ๊ฒฝ์ฐ
๊ฒ์์ด ์ ๋๋๋ก ๊ตฌ๋ถ
์ฐธ๊ณ ..
๋ชจ๋ํฐ๋ง์ ๊ฐ๊ฐ ์ฉ๋๊ฐ ๋ค๋ฅด๋ค. ๊ด์ฐฐ์ ํ ๋๋ ์ ์ฒด์์ ์ ์ ์ข๊ฒ
ํํฌ์ธํธ ๊ฐ์ถ. ๋ง์ดํฌ๋ก ์๋น์ค ๋ถ์ฐ ๋ชจ๋ํฐ๋ง, ๋์ฉ๋ ํธ๋ํฝ ๋์ ๊ฐ๋ฅ
์๋
๋ชจ๋ํฐ๋ง ํด์์ ์ผ์ ์ด์ ์์น๊ฐ ๋์ด๊ฐ๋ฉด, ์ฌ๋, ๋ฌธ์ ๋ฑ ์ฐ๋
์๋์ 2๊ฐ์ง ์ข ๋ฅ(๊ฒฝ๊ณ , ์ฌ๊ฐ)๋ก ๊ผญ ๊ตฌ๋ถํด์ ๊ด๋ฆฌ
๊ฒฝ๊ณ ๋ ํ๋ฃจ 1๋ฒ ์ ๋ ์ฌ๋์ด ์ง์ ํ์ธํด๋ ๋๋ ์์ค (์ฌ๋์ด ๋ค์ด๊ฐ์ ํ์ธ)
์ฌ๊ฐ์ ์ฆ์ ํ์ธ ํ์. ์ฌ๋ ์๋ฆผ, ๋ฌธ์, ์ ํ
ex. ๊ฒฝ๊ณ , ์ฌ๊ฐ ์๋ฆผ ๊ธฐ์ค
๋์คํฌ ์ฌ์ฉ๋ 70% ๊ฒฝ๊ณ
๋์คํฌ ์ฌ์ฉ๋ 80% ์ฌ๊ฐ
CPU ์ฌ์ฉ๋ 40% ๊ฒฝ๊ณ
CPU ์ฌ์ฉ๋ 50% ์ฌ๊ฐ
๊ฒฝ๊ณ ์ ์ฌ๊ฐ์ ์ ๋๋์ด์ ์ ๋ฌด์ ์ถ์ ๋ฐฉํด๊ฐ ๋์ง ์๋๋ก ํ์.
Last updated