Toby Spring Boot
ํ ๋น๋์ ํ ๋น์ ์คํ๋ง ๋ถํธ - ์ดํด์ ์๋ฆฌ ๊ฐ์๋ฅผ ์์ฝํ ๋ด์ฉ์ ๋๋ค.
Intro
์คํ๋ง ๋ถํธ๋ ์คํ๋ง ๊ธฐ๋ฐ์ผ๋ก ์ค๋ฌด ํ๊ฒฝ์ ์ฌ์ฉ ๊ฐ๋ฅํ ์์ค์ ๋ ๋ฆฝ์คํํ ์ ํ๋ฆฌ์ผ์ด์ ์
๋ณต์กํ ๊ณ ๋ฏผ ์์ด ๋น ๋ฅด๊ฒ ์์ฑํ ์ ์๊ฒ ๋์์ฃผ๋ ์ฌ๋ฌ๊ฐ์ง
๋๊ตฌ์ ๋ชจ์
ํน์ง
๋ ๋ฆฝํ ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์์ฑ
Tomcat, Jetty, Undertow ๋ฑ์ ํฌํจ(WAR ํ์ผ ๋ฐฐํฌ ๋ถํ์)
๋น๋ ๊ตฌ์ฑ์ ๋จ์ํํ๊ธฐ ์ํด ๋ ์์ ์ธ 'startor' ์ข ์์ฑ ์ ๊ณต
๊ฐ๋ฅํ ๋๋ง๋ค ์๋์ผ๋ก ์คํ๋ง ๋ฐ ํ์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌ์ฑ
๋ฉํธ๋ฆญ, ์ํ ํ์ธ ๋ฐ ์ธ๋ถํ๋ ๊ตฌ์ฑ๊ณผ ๊ฐ์ ํ๋ก๋์ ์ค๋น ๊ธฐ๋ฅ ์ ๊ณต
์ฝ๋ ์์ฑ ๋ฐ XML ๊ตฌ์ฑ์ ๋ํ ์๊ตฌ ์ฌํญ ๋ถํ์
.
Containerless
์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์๊ตฌ๋๋ Servlet Container ๊ด๋ จ ์ค์ ์ง์์ ์ํ ๊ฐ๋ฐ ๋๊ตฌ์ ์ํคํ ์ฒ ์ง์
์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ํต์ฌ์ด ์๋ ๋จ์ ๋ฐ๋ณต ์์ ์ ๊ฑฐ
์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ค์น, WAR ํด๋ ๊ตฌ์กฐ, web.xml, WAR ๋น๋, ์ปจํ ์ด๋๋ก ๋ฐฐ์น, ํฌํธ ์ค์ , ํด๋์ค ๋ก๋, ๋ก๊น ...
๋ ๋ฆฝ์คํํ(standalone) ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๋์
main ๋ฉ์๋ ์คํ๋ง์ผ๋ก Servlet Container ๊ด๋ จ ๋ชจ๋ ํ์ ์์ ์ด ์ํ

.
Opinionated Tool
Spring
๊ทน๋จ์ ์ ์ฐํจ์ ์ถ๊ตฌํ๊ณ ๋ค์ํ ๊ด์ ์ ์์ฉํ๋ ๊ฒ์ด ์ค๊ณ ์ฒ ํ์ด์์ง๋ง..
๊ฐ์ข ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์กด๊ด๊ณ์ ๋ฒ์ ํธํ์ฑ์ ์ฒดํฌํ๋ ์์ ์ ๊ณ ๋๊ณ ์ฝ์ง ์์ ์์
Spring Boot
opinionated ์ค๊ณ: ์๊ธฐ ์ฃผ์ฅ์ด ๊ฐํ๊ณ ์์ ์ ์๊ฒฌ์ ๊ณ ์งํ ์ค๊ณ ์ฒ ํ
์ผ๋จ ์ ํด์ฃผ๋ ๋๋ก ๋น ๋ฅด๊ฒ ๊ฐ๋ฐํ๊ณ ๋์ค์ ๊ณ ๋ฏผ. ์คํ๋ง์ ์ ํ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ ์ ๊ณต
์คํ๋ง ๋ถํธ๋ ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฒ์ ๋ง๋ค ์ฌ์ฉํ ๊ธฐ์ ์ ์ข ๋ฅ ์ ์
์ฌ์ ๊ฒ์ฆ๋ ์ถ์ฒ ๊ธฐ์ , ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌ์ฑ, ์์กด ๊ด๊ณ์ ์ ์ฉ ๋ฒ์ , ์ธ๋ถ ๊ตฌ์ฑ(DI)๊ณผ ๋ํดํธ ์ค์ ๋ฑ ์ ๊ณต
๋ํดํธ ๊ตฌ์ฑ์ ์ปค์คํฐ๋ง์ด์ง ํ ์ ์๋ ์ ์ฐํ ๋ฐฉ๋ฒ ์ ๊ณต
Start to Develop
JDK
SDK-MAN(The Software Development Kit Manager)
Unix ๊ธฐ๋ฐ ์์คํ ์์ ์ฌ๋ฌ ์ํํธ์จ์ด ๊ฐ๋ฐ ํคํธ์ ๋ณ๋ ฌ ๋ฒ์ ์ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋๊ตฌ
java ๋ฒ์ ํ์ธ โ
sdk list java
ํน์ identifier ์ค์น โ
sdk install java {id}
ํด๋น ๋๋ ํ ๋ฆฌ์์ ํน์ ๋ฒ์ ์ java ์ฌ์ฉ โ
sdk use java {id}
Java ๋ฒ์ ๊ด๋ฆฌ์
.
API Test Method
์น ๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ - Network
curl
Intellij IDEA Ultimate- http request
JUnit Test
...
.
HTTP Request and Response
โฏ http -v ":8080/hello?name=Spring"
GET /hello?name=Spring HTTP/1.1
Accept: */* --> ํด๋ผ์ด์ธํธ๊ฐ ์ ํธํ๋ ๋ฏธ๋์ด ํ์
Accept-Encoding: gzip, deflate --> ํด๋ผ์ด์ธํธ๊ฐ ์ ํธํ๋ ์์ถ ์ธ์ฝ๋ฉ
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/3.2.1
HTTP/1.1 200
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8 --> ํํ ๋ฐ์ดํฐ์ ํ์
Date: Thu, 01 Dec 2022 01:45:15 GMT
Keep-Alive: timeout=60
Hello Spring
Standalone Servlet Application
Start Servlet Container
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
public class HellobootApplication {
public static void main(String[] args) {
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer();
webServer.start();
}
}
.
Register Servlet in ServletContext
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("hello", new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
resp.setStatus(HttpStatus.OK.value());
resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
resp.getWriter().println("hello " + name);
}
}).addMapping("/servlet/hello");
});
webServer.start();
.
Front Controller Pattern
์ฌ๋ฌ ์์ฒญ์ ์ฒ๋ฆฌํ๋๋ฐ ๋ฐ๋ณต์ ์ผ๋ก ๋ฑ์ฅํ๋ ๊ณตํต ์์ ์ ํ๋์ ์ค๋ธ์ ํธ์์ ์ผ๊ด์ ์ผ๋ก ์ฒ๋ฆฌํ๊ฒ ๋ง๋๋ ๋ฐฉ์
๋ชจ๋ ์์ฒญ, ํน์ ์ผ์ ํจํด์ ๊ฐ์ง ์์ฒญ์ ํ๋์ ์๋ธ๋ฆฟ์ด ๋ด๋นํ๋๋ก ๋งคํ
ํ๋ก ํธ ์ปจํธ๋กค๋ฌ์ ๋ ๊ฐ์ง ์ค์ํ ๊ธฐ๋ฅ์ ๋งคํ๊ณผ ๋ฐ์ธ๋ฉ
๋งคํ:ํ๋ก ํธ ์ปจํธ๋กค๋ฌ๊ฐ HTTP ์์ฒญ์ ์ฒ๋ฆฌํ ํธ๋ค๋ฌ๋ฅผ ๊ฒฐ์ ํ๊ณ ์ฐ๋ํ๋ ์์
๋ฐ์ธ๋ฉ: ํธ๋ค๋ฌ์๊ฒ ์น ์์ฒญ ์ ๋ณด๋ฅผ ์ถ์ถํ๊ณ ์๋ฏธ์๋ ์ค๋ธ์ ํธ์ ๋ด์์ ์ ๋ฌํ๋ ์์
ํ๋ก ํธ ์ปจํธ๋กค๋ฌ๋ก ์ ํ
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory(); WebServer webServer = serverFactory.getWebServer(servletContext -> { HelloController helloController = new HelloController(); servletContext.addServlet("frontController", new HttpServlet() { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ์ธ์ฆ, ๋ณด์, ๋ค๊ตญ์ด, ๊ณตํต ๊ธฐ๋ฅ .. if (req.getRequestURI().equals("/servlet/hello") && req.getMethod().equals(HttpMethod.GET.name())) { String name = req.getParameter("name"); String ret = helloController.hello(name); resp.setStatus(HttpStatus.OK.value()); resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE); resp.getWriter().println(ret); } else if (req.getRequestURI().equals("/user")) { // ... } else { resp.setStatus(HttpStatus.NOT_FOUND.value()); } } }).addMapping("/*"); }); webServer.start();
Standalone Spring Application
์คํ๋ง ์ปจํ ์ด๋๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง์ด ๋ด๊ธด ํ๋ฒํ ์๋ฐ ์ค๋ธ์ ํธ(POJO)์ ๊ตฌ์ฑ ์ ๋ณด(Configuration Metadata)๋ฅผ ๋ฐํ์์ ์กฐํฉํด์ ๋์ํ๋ ์ต์ข ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑ
// ์คํ๋ง ์ปจํ
์ด๋ ์์ฑ
GenericApplicationContext applicationContext = new GenericApplicationContext();
// ๋น ์ค๋ธ์ ํธ ํด๋์ค ์ ๋ณด ๋ฑ๋ก
applicationContext.registerBean(HelloController.class);
// ๊ตฌ์ฑ ์ ๋ณด๋ก ์ปจํ
์ด๋ ์ด๊ธฐํ(๋น ์ค๋ธ์ ํธ๋ฅผ ์ง์ ์์ฑ)
applicationContext.refresh();
...
// ์ปจํ
์ด๋๊ฐ ๊ด๋ฆฌํ๋ ๋น ์ค๋ธ์ ํธ ํ๋
HelloController helloController = applicationContext.getBean(HelloController.class);
์คํ๋ง ์ปจํ
์ด๋๋ ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ
๋ผ๊ณ ๋ ๋ถ๋ฆฐ๋ค.
์ฑ๊ธํค ํจํด๊ณผ ์ ์ฌํ๊ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋์ํ๋ ๋์ ๋จ ํ๋์ ์ค๋ธ์ ํธ๋ง์ ๋ง๋ค๊ณ ์ฌ์ฉ๋๋๋ก ์ง์
.
Dependency Injection

DI
๋ฅผ ์ํด N๊ฐ์ ์ค๋ธ์ ํธ(์ธํฐํ์ด์ค ๊ตฌํ์ฒด)๊ฐ ๋์ ์ผ๋ก ์์กด๊ด๊ณ๋ฅผ ๊ฐ์ง๋๋ก ๋์์ฃผ๋์ด์ ๋ธ๋ฌ
๊ฐ ํ์์คํ๋ง ์ปจํ ์ด๋
(์ด์ ๋ธ๋ฌ)๋ DI๋ฅผ ๊ฐ๋ฅํ๋๋ก ๋์์ฃผ๋์ด์ ๋ธ๋ฌ
๋ก ๋์์์กด๊ด๊ณ๊ฐ ์๋ ํด๋์ค๋ค์ ์ค๋ธ์ ํธ๋ก ์๋ก ๊ด๊ณ๋ฅผ ์ฐ๊ฒฐ์์ผ์ฃผ๊ณ ์ฌ์ฉํ ์ ์๋๋ก ์ค์
์คํ๋ง ์ปจ๋ฐ์ด๋๋ ๋ฉํ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ํด๋์ค์ ์ฑ๊ธํค ์ค๋ธ์ ํธ๋ฅผ ์์ฑ
์์ฑ๋ ์ค๋ธ์ ํธ๊ฐ ์ฌ์ฉํ ๋ค๋ฅธ ์์กด ์ค๋ธ์ ํธ๊ฐ ์๋ค๋ฉด ์์กด์ฑ ์ฃผ์
์์กด์ฑ ์ฃผ์ ๋ฐฉ๋ฒ์ผ๋ก๋ ์์ฑ์ ์ฃผ์ , ํฉํฐ๋ฆฌ ๋ฉ์๋ ๋ฑ ์กด์ฌ
.
DispatcherServlet
์คํ๋ง์ ํ๋ก ํธ ์ปจํธ๋กค๋ฌ์ ๊ฐ์ ์ญํ ์ ๋ด๋นํ๋ DispatcherServlet ์ ๊ฐ์ง๊ณ ์๋ค.
DispatcherServlet ์ ์๋ธ๋ฆฟ์ผ๋ก ๋ฑ๋ก๋์ด์ ๋์ํ๋ฉด์, ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์ด์ฉํด์ ์์ฒญ์ ์ ๋ฌํ ํธ๋ค๋ฌ์ธ ์ปจํธ๋กค๋ฌ ์ค๋ธ์ ํธ๋ฅผ ๊ฐ์ ธ์ ์ฌ์ฉ
DispatcherServlet ์ด ์ฌ์ฉํ๋ ์คํ๋ง ์ปจํ ์ด๋๋ GenericWebApplicationContext ๋ฅผ ์ด์ฉํด์ ์์ฑ
.
์ ๋
ธํ
์ด์
๋งคํ ์ ๋ณด
DispatcherServlet ์ ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋ก๋ ๋น ํด๋์ค์ ์๋ ๋งคํ ์ ๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ์ฐธ๊ณ ํด์ ์น ์์ฒญ์ ์ ๋ฌํ ์ค๋ธ์ ํธ์ ๋ฉ์๋๋ฅผ ์ ์
ํด๋์ค ๋ ๋ฒจ์ @RequestMapping ๊ณผ ๋ฉ์๋ ๋ ๋ฒจ์ @GetMapping ๋ ๊ฐ์ง์ ์ ๋ณด๋ฅผ ์กฐํฉํด์ ๋งคํ์ ์ฌ์ฉํ ์ต์ข ์ ๋ณด ์์ฑ
์ปจํธ๋กค๋ฌ ๋ฉ์๋์ ๋ฆฌํด๊ฐ์ ์น ์์ฒญ์ ๋ฐ๋์ ์ ์ฉํ๋๋ก @ResponseBody ์ ์ธ
๊ทธ๋ ์ง ์์ผ๋ฉด String ํ์ ์ ์๋ต์ ๋ทฐ ์ด๋ฆ์ผ๋ก ํด์ํ๊ณ Thymeleaf ๊ฐ์ ๋ทฐ ํ ํ๋ฆฟ์ ํ์(์ด ๊ฒฝ์ฐ 404 ์๋ฌ ๋ฐ์)
@RestController ๋ @ResponseBody ๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฏ๋ก ๋ฉ์๋ ๋ ๋ฒจ์ @ResponseBody ๋ฅผ ๋ฃ์ง ์์๋ ์ ์ฉ๋ ๊ฒ์ฒ๋ผ ๋์
@RequestMapping("/hello") public class HelloController { ... @GetMapping @ResponseBody public String hello(String name) { return helloService.sayHello(Objects.requireNonNull(name)); } }
.
์คํ๋ง ์ปจํ
์ด๋๋ก ํตํฉ
์คํ๋ง ์ปจํ ์ด๋์ ์ด๊ธฐํ ์์ ์ค์ ํธ์ถ๋๋ ํ ๋ฉ์๋์ ์๋ธ๋ฆฟ ์ปจํ ์ด๋(ํฐ์บฃ)์ ์ด๊ธฐํํ๊ณ ๋์ฐ๋ ์ฝ๋ ์ฝ์
GenericWebApplicationContext applicationContext = new GenericWebApplicationContext() {
@Override
protected void onRefresh() {
super.onRefresh();
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("dispatcherServlet",
new DispatcherServlet(this)
).addMapping("/*");
});
webServer.start();
}
};
.
@Component Scan
ํด๋์ค์ ์ ๋ ธํ ์ด์ ์ ์ ์ธํ๊ณ , ์ด๋ฅผ ์ค์บํด์ ์คํ๋ง ์ปจํ ์ด๋์ ๋น์ผ๋ก ์๋ ๋ฑ๋ก
์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉ์ธ ํด๋์ค์๋ @ComponentScan ์ ์ธ
๋ฑ๋ก ๋์์ด ๋ ํด๋์ค์๋ @Component ์ ์ธ
@Component ๋ ๋ฉํ ์ ๋ ธํ ์ด์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ ์ ๋ ธํ ์ด์ ๋ ์ฌ์ฉ ๊ฐ๋ฅ
@Controller, @RestController, @Service ๋ฑ..
@RestController
@RestController ๋ @Controller ๋ฅผ ๋ฉํ ์ ๋ ธํ ์ด์ ์ผ๋ก ๊ฐ์ง๊ณ ์๊ณ ,
@Controller ๋ @Component๋ฅผ ๋ฉํ ์ ๋ ธํ ์ด์ ์ผ๋ก
์ด ๊ฒฝ์ฐ @RestController ๋ @Component ์ ๋ ธํ ์ด์ ์ด ๋ถ์ ๊ฒ๊ณผ ๊ฐ์ ํจ๊ณผ
@RestController ๊ฐ ๋ถ์ ๊ฒฝ์ฐ DispatcherServlet ์ ๋งคํ ์ ๋ณด ํ์ ๋์์ด ๋๋ฏ๋ก ํด๋์ค ๋ ๋ฒจ์ ๋งคํ ์ ๋ ธํ ์ด์ (@RequestMapping) ๋ถํ์
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController {}
.
Bean์ ์๋ช
์ฃผ๊ธฐ ๋ฉ์๋
/**
* @Bean ๋ฉ์๋์์ ๋
๋ฆฝ์ ์ผ๋ก ์์ฑ
*
* DispatcherServlet ์ด ํ์๋ก ํ๋ WebApplicationContext ํ์
์ปจํ
์ด๋ ์ค๋ธ์ ํธ๋ -> dispatcherServlet.setApplicationContext(this);
* ์คํ๋ง ์ปจํ
์ด๋์ ๋น ์์ ์ฃผ๊ธฐ ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ฃผ์
๋น๊ฒ ๋๋ค.
*/
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
DispatcherServlet ์
ApplicationContextAware
๋ผ๋ ์คํ๋ง ์ปจํ ์ด๋๋ฅผ setter ๋ฉ์๋๋ก ์ฃผ์ ํด์ฃผ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํ์ด๋ฌํ
์์ ์ฃผ๊ธฐ ๋น ๋ฉ์๋
๋ฅผ ๊ฐ์ง ๋น์ด ๋ฑ๋ก๋๋ฉด ์คํ๋ง์ ์์ ์ ์ง์ ์ฃผ์๋น ์์ ์ฃผ๊ธฐ ๋ฉ์๋
๋ฅผ ํตํด ์ฃผ์ ๋๋ ์ค๋ธ์ ํธ๋ ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์ค์ค๋ก ๋น์ผ๋ก ๋ฑ๋กํด์ ๋น์ผ๋ก ๊ฐ์ ธ์ ์ฌ์ฉํ ์๋ ์๋๋ก ์ง์๊ทธ๋ฐ์ ์คํ๋ง์ด ์ ๊ณตํ๋ ์์ ์ฃผ๊ธฐ ๋ฉ์๋
BeanNameAware's setBeanName, BeanClassLoaderAware's setBeanClassLoader, BeanFactoryAware's setBeanFactory, EnvironmentAware's setEnvironment, EmbeddedValueResolverAware's setEmbeddedValueResolver, ResourceLoaderAware's setResourceLoader (only applicable when running in an application context), ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context), MessageSourceAware's setMessageSource (only applicable when running in an application context), ApplicationContextAware's setApplicationContext (only applicable when running in an application context), ServletContextAware's setServletContext (only applicable when running in a web application context), postProcessBeforeInitialization methods of BeanPostProcessors, InitializingBean's afterPropertiesSet, a custom init-method definition, postProcessAfterInitialization methods of BeanPostProcessors
TEST
TestRestTemplate
์น ์๋ฒ์ HTTP ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ์์ ๊ฒ์ฆํ๋ ํ ์คํธ์์๋
TestRestTemplate
๋ฅผ ์ฌ์ฉํด ๋ณด์.
@Test
void hello() {
TestRestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<String> res =
restTemplate.getForEntity("http://localhost:8080/hello?name={name}", String.class, "Spring");
assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(res.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.TEXT_PLAIN_VALUE)).isTrue();
assertThat(res.getBody().trim()).isEqualTo("Hello Spring");
}
.
๋จ์ ํ
์คํธ
์์กด ์ค๋ธ์ ํธ๊ฐ ์๋ ๊ฒฝ์ฐ, ํ ์คํธ๊ฐ ์คํ๋๋ ๋์์ ์ํ๋ ์ต์ํ์ ๊ธฐ๋ฅ์ ๊ฐ์ง ์์กด ์ค๋ธ์ ํธ ์ฝ๋๋ฅผ ํ ์คํธ์ฉ์ผ๋ก ๋ง๋ค์ด์ ์ฌ์ฉ
@Test
void helloController() {
HelloController helloController = new HelloController(name -> name);
String ret = helloController.hello("Test");
Assertions.assertThat(ret).isEqualTo("Test");
}
.
Decorator Pattern and Proxy Pattern

Decorator Pattern
๊ธฐ์กด ์ฝ๋์ ๋์ ์ผ๋ก ์ฑ ์์ ์ถ๊ฐํ ๋ ์ฌ์ฉํ๋ ํจํด
์ค๋ธ์ ํธ ํฉ์ฑ ๊ตฌ์กฐ๋ก ํ์ฅ์ด ๊ฐ๋ฅํ๋๋ก ์ค๊ณ
DI๋ฅผ ์ ์ฉํด์ ์์กด๊ด๊ณ๋ฅผ ๋ฐํ์์ ์ฃผ์ ํ ์ ์๋ค๋ฉด ์์กด ์ค๋ธ์ ํธ์ ๋์ผํ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํ์ฅ๊ธฐ๋ฅ(๋ฐ์ฝ๋ ์ดํฐ)์ ๋์ ์ผ๋ก ์ถ๊ฐ ๊ฐ๋ฅ
์ฌ๊ท์ ์ธ ๊ตฌ์กฐ๋ก ์ฌ๋ฌ ๊ฐ์ ์ฑ ์์ ๋ถ๊ฐํ๋ ๊ฒ๋ ๊ฐ๋ฅ

Proxy Pattern
ํ๋ก์๋ ๋ค๋ฅธ ์ค๋ธ์ ํธ์ ๋๋ฆฌ์ ํน์ ํ๋ ์ด์ค ํ๋ ์ญํ
ํ๋ก์๋ ๋ฆฌ๋ชจํธ ์ค๋ธ์ ํธ์ ๋ํ ๋ก์ปฌ ์ ๊ทผ์ด ๊ฐ๋ฅํ๊ฒ ํ๊ฑฐ๋, ํ์๊ฐ ์์ ๋๋ง ๋์ ์ค๋ธ์ ํธ๋ฅผ ์์ฑ
๋ณด์์ด๋ ์ ์ ์ ์ด ๋ฑ์ ์ฌ์ฉ
Auto Configuration
Meta-annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Meta Annotation
public @interface Service {
}
์ ๋ ธํ ์ด์ ์ ์ ์ฉํ ์ ๋ ธํ ์ด์
์คํ๋ง์ ๋ฉํ ์ ๋ ธํ ์ด์ ์ ํจ๋ ฅ์ ์ ์ฉ
Composed-annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller // Meta Annotation
@ResponseBody // Meta Annotation
public @interface RestController {
...
}
ํ๋ ์ด์์ ๋ฉํ ์ ๋ ธํ ์ด์ ์ด ์ ์ฉ๋ ์ ๋ ธํ ์ด์
๋ชจ๋ ๋ฉํ ์ ๋ ธํ ์ด์ ์ด ์ ์ฉ๋ ๊ฒ๊ณผ ๋์ผํ ํจ๊ณผ

Application Logic Bean
์ ํ๋ฆฌ์ผ์ด์ ์
๋น์ฆ๋์ค ๋ก์ง
์ ๋ด๊ณ ์๋ ๋น์ปดํฌ๋ํธ ์ค์บ๋
์ ์ํด์ ๋น ๊ตฌ์ฑ ์ ๋ณด๊ฐ ์์ฑ๋๊ณ ๋น ์ค๋ธ์ ํธ๋ก ๋ฑ๋กex. HelloController, HelloDecorator ..
Application Infrastructure Bean
์คํ๋ง ๋ถํธ์์
์๋ ๊ตฌ์ฑ ์ ๋ณด
์ ์ํด ์ปจํ ์ด๋์ ๋ฑ๋ก๋๋ ๋น์ ํ๋ฆฌ์ผ์ด์ ์ด ๋์ํ๋๋ฐ ๊ผญ ํ์ํ ๊ธฐ์ ๊ธฐ๋ฐ์ ์ ๊ณตํ๋ ๋น
ex. ServletWebServerFactory, DispatcherServlet..
Container Infrastructure(Infra) Bean
์คํ๋ง ์ปจํ ์ด๋์ ๊ธฐ๋ฅ์ ํ์ฅํด์ ๋น ๋ฑ๋ก๊ณผ ์์ฑ, ๊ด๊ณ์ค์ , ์ด๊ธฐํ ๋ฑ์ ์์ ์ ์ฐธ์ฌํ๋ ๋น
์ปจํ ์ด๋๊ฐ ์ง์ ๋ง๋ค๊ณ ์ฌ์ฉํ๋ ๋น์ด๋ฏ๋ก ์ ํ๋ฆฌ์ผ์ด์ ๋น๊ณผ ๊ตฌ๋ถ
ํ์ํ ๊ฒฝ์ฐ ์ฃผ์ ๋ฐ์์ ํ์ฉ ๊ฐ๋ฅ
.
์๋ ๊ตฌ์ฑ ์ ๋ณด ๋์ ๋ฑ๋ก
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
...
}
...
public class MyAutoConfigImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {
"tobyspring.config.autoconfig.DispatcherServletConfig",
"tobyspring.config.autoconfig.TomcatWebServerConfig"
};
}
}
ImportSelector
๊ตฌํ ํด๋์ค๋ฅผ @Import ํด์ฃผ๋ฉดselectImports
๊ฐ ๋ฆฌํดํ๋ ํด๋์ค ์ด๋ฆ์ผ๋ก @Configuration ํด๋์ค๋ฅผ ์ฐพ์์ ๊ตฌ์ฑ ์ ๋ณด๋ก ์ฌ์ฉ@import ๋์์ ์ธ๋ถ์์ ์ฝ๋๋ก ๊ฐ์ ธ์ค๊ณ ์ ํํ ์ ์๋ ๋์ ์ธ ๋ฐฉ๋ฒ ์ ๊ณต
.
์๋ ๊ตฌ์ฑ ์ ๋ณด ํ์ผ ๋ถ๋ฆฌ
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
ArrayList<Object> autoConfigs = new ArrayList<>();
ImportCandidates.load(MyAutoConfiguration.class, classLoader).forEach(autoConfigs::add);
return autoConfigs.toArray(new String[0]);
}
@MyAutoConfiguration ์ ๋ ธํ ์ด์ ์์ฑ
tobyspring.config.MyAutoConfiguration.imports
ํ์ผ์META-INF/spring
ํด๋ ์๋ ์์ฑselectImports() ์์ ํ์ผ์ ์์ฑ๋ ํด๋์ค ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ์ปจํ ์ด๋์ ๋ฑ๋ก์ํฌ @Configuration ํด๋์ค ๋ชฉ๋ก ์ ์ฅ
.
@Configuration ๋์ ๋ฐฉ์
@Configuration(proxyBeanMethods = false)
proxyBeanMethods = true
(default, ์คํ๋ง 5.2 ๋ฒ์ ๋ถํฐ ์ง์)true ์ผ ๊ฒฝ์ฐ, @Configuration ํด๋์ค๋ CGLib ๋ฅผ ์ด์ฉํด์ ํ๋ก์ ํด๋์ค๋ก ํ์ฅ ํ @Bean ์ด ๋ถ์ ๋ฉ์๋์ ๋์ ๋ฐฉ์์ ๋ณ๊ฒฝ
@Bean ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํด์ ๋ค๋ฅธ ๋น์ ์์กด ๊ด๊ณ๋ฅผ ์ค์ ํ ๋ ์ฌ๋ฌ๋ฒ ํธ์ถ๋๋๋ผ๋ ์ฑ๊ธํค ๋น์ฒ๋ผ ์ฐธ์กฐํ ์ ์๋๋ก ๋งค๋ฒ ๊ฐ์ ์ค๋ธ์ ํธ๋ฅผ ๋ฆฌํด
/** * Spring ์ ํ๋์ ๋น์ ๋ ๊ฐ ์ด์์ ๋ค๋ฅธ ๋น์์ ์์กดํ๊ณ ์์ ๊ฒฝ์ฐ, * Factory Method ํธ์ถ ์๋ง๋ค ์๋ก์ด ๋น์ด ์์ฑ๋๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด * @Configuration class ๋ ๊ธฐ๋ณธ์ ์ผ๋ก proxy ๋ฅผ ๋ง๋ค์ด์ ๊ธฐ๋ฅ ํ์ฅ * (proxyBeanMethods = true) */ static class MyConfigProxy extends MyConfig { private Common common; @Override Common common() { if (this.common == null) { this.common = super.common(); } return this.common; } } ... @Configuration static class MyConfig { @Bean Common common() { return new Common(); } @Bean Bean1 bean1() { return new Bean1(common()); } @Bean Bean2 bean2() { return new Bean2(common()); } }
๋จ, @Bean ๋ฉ์๋ ์ง์ ํธ์ถ๋ก ๋น ์์กด๊ด๊ณ ์ฃผ์ ์ ํ์ง ์๋๋ค๋ฉด ๊ตณ์ด ๋ณต์กํ ํ๋ก์๋ฅผ ์์ฑํ ํ ํ์๊ฐ ์์
์ด ๊ฒฝ์ฐ
proxyBeanMethods = false
๋ก ์ง์ ํด ๋ณด์.@Bean ๋ฉ์๋๋ ํ๋ฒํ ํฉํ ๋ฆฌ ๋ฉ์๋์ฒ๋ผ ๋์
์กฐ๊ฑด๋ถ ์๋ ๊ตฌ์ฑ
Spring Boot AutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore
@AutoConfigureAfter
public @interface AutoConfiguration {
...
}
META-INF.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports
์ฝ 144 ๊ฐ์ autoConfiguration ์ด Spring Boot ๊ธฐ๋ณธ ๋ฑ๋ก
Spring Boot application starters
.
@Conditional๊ณผ Condition
/**
* ์คํ๋ง 4.0์ ์ถ๊ฐ๋ ์ ๋
ธํ
์ด์
์ผ๋ก ๋ชจ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๊ฒฝ์ฐ์๋ง ์ปจํ
์ด๋์ ๋น์ผ๋ก ๋ฑ๋ก
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
...
/**
* @Conditional ์ ์ง์ ๋์ด์ ๊ตฌ์ฒด์ ์ธ ๋งค์นญ ์กฐ๊ฑด์ ๊ฐ์ง ํด๋์ค๊ฐ ๊ตฌํํด์ผํ ์ธํฐํ์ด์ค
*/
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
...
/**
* Condition interface ๊ตฌํ์ฒด
*/
static class BooleanCondition implements Condition {
/**
* @Conditional ์ ๋
ธํ
์ด์
์ ์๋ฆฌ๋จผํธ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ AnnotatedTypeMetadata ์ ๋ฌ
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(BooleanConditional.class.getName());
Boolean value = (Boolean) annotationAttributes.get("value");
return value;
}
}
@Conditional
์ @Configuration ํด๋์ค์ @Bean ๋ฉ์๋์ ์ ์ฉ ๊ฐ๋ฅํด๋์ค ์กฐ๊ฑด์ ๋ง์กฑํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ ๋ฉ์๋๋ ๋ฌด์
.
Costume @Conditional
@Conditional
์ ๊ฐ์ฅ ๋ํ์ ์ธ ๋ฐฉ๋ฒ์ ํด๋์ค ์กด์ฌ ํ์ธ์ด๋ค ๊ธฐ์ ์ ํด๋์ค๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฌ์ฉํ๋๋ก ํฌํจ์์ผฐ๋ค๋ฉด, ์ด ๊ธฐ์ ์ ์ฌ์ฉํ ์๋๊ฐ ์๋ค๋ ๊ฒ์ผ๋ก ๋ณด๊ณ ๊ด๋ จ ์๋ ๊ตฌ์ฑ ํด๋์ค๋ฅผ ๋ฑ๋ก
.
์๋ ๊ตฌ์ฑ ์ ๋ณด ๋์ฒด
์๋ ๊ตฌ์ฑ ์ ๋ณด๋ ๋ค์์ ๊ณผ์ ์ผ๋ก ๋ฑ๋ก
imports ํ์ผ์์ ์๋ ๊ตฌ์ฑ ์ ๋ณด ํด๋์ค ํ๋ณด ๋ก๋ฉ
@Conditional ์กฐ๊ฑด ์ฒดํฌ๋ฅผ ํตํด ์ ํ๋ ํด๋์ค๊ฐ ๋น์ผ๋ก ๋ฑ๋ก
/**
* ์๋ ๊ตฌ์ฑ์ผ๋ก ๋ฑ๋ก๋๋ ๋น๊ณผ ๋์ผํ ํ์
์ ๋น์ ์ง์ ์ ์(@Configuration/@Bean)ํ๋ ๊ฒฝ์ฐ,
* ์ง์ ์ ์ ๋น ๊ตฌ์ฑ์ด ์๋ ๊ตฌ์ฑ์ ๋์ฒด
*/
@Configuration(proxyBeanMethods = false)
public class WebServerConfiguration {
@Bean ServletWebServerFactory customerWebServerFactory() {
TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
serverFactory.setPort(9090);
return serverFactory;
}
}
...
/**
* ์๋ ๊ตฌ์ฑ ํด๋์ค์ @Bean ๋ฉ์๋์ @ConditionalOnMissingBean ์ด ์ ์๋ ๊ฒฝ์ฐ,
* ์ ์ ๊ตฌ์ฑ์ ์ง์ ํ ํ์
์ ๋น์ด ์ ์๋์ด์์ผ๋ฉด ์๋ ๊ตฌ์ฑ ๋น์ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋์ง ์์ ๋ฑ๋ก๋์ง ์์.
*/
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
์๋ ๊ตฌ์ฑ ์ ๋ณด ๋์ฒดํ๊ธฐ
.
Spring Boot @Conditional
์คํ๋ง ๋ถํธ์ ์๋ ๊ตฌ์ฑ์ ๋ค์ํ @Conditional ์ ์ด์ฉ.
Class Conditions
@ConditionalOnClass
@ConditionalOnMissingClass
โข ํ๋ก์ ํธ ๋ด ์ง์ ํ ํด๋์ค์ ์กด์ฌ๋ฅผ ํ์ธํด์ ํฌํจ ์ฌ๋ถ ๊ฒฐ์ โข ์ฃผ๋ก @Configuration ์ ํด๋์ค ๋ ๋ฒจ์์ ์ฌ์ฉํ์ง๋ง, @Bean ๋ฉ์๋์๋ ์ ์ฉ ๊ฐ๋ฅ โข (๋จ, ํด๋์ค ๋ ๋ฒจ์ ๊ฒ์ฆ ์์ด @Bean ๋ฉ์๋์๋ง ์ ์ฉํ๋ฉด ๋ถํ์ํ @Configuration ํด๋์ค๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋๊ธฐ ๋๋ฌธ์ ํด๋์ค ๋ ๋ฒจ ์ฌ์ฉ์ ์ฐ์ )
Bean Conditions
@ConditionalOnBean
@ConditionalOnMissingBean
โข ๋น์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ๊ธฐ์ค์ผ๋ก ํฌํจ ์ฌ๋ถ ๊ฒฐ์ โข ๋น์ ํ์ ๋๋ ์ด๋ฆ์ ์ง์ ํ ์ ์๊ณ , ์ง์ ๋ ๋น ์ ๋ณด๊ฐ ์์ผ๋ฉด ๋ฉ์๋์ ๋ฆฌํด ํ์ ์ ๊ธฐ์ค์ผ๋ก ๋น ์กด์ฌ ์ฌ๋ถ ์ฒดํฌ โข ์ปจํ ์ด๋์ ๋ฑ๋ก๋ ๋น ์ ๋ณด๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฒดํฌํ๊ธฐ ๋๋ฌธ์ ์๋ ๊ตฌ์ฑ ์ฌ์ด์ ์ ์ฉํ๋ ค๋ฉด @Configuration ํด๋์ค์ ์ ์ฉ ์์๊ฐ ์ค์ โข ๊ฐ๋ฐ์๊ฐ ์ง์ ์ ์ํ ์ปค์คํ ๋น ๊ตฌ์ฑ ์ ๋ณด๊ฐ ์๋ ๊ตฌ์ฑ ์ ๋ณด ์ฒ๋ฆฌ๋ณด๋ค ์ฐ์ ํ๊ธฐ ๋๋ฌธ์ ์ด ๊ด๊ณ์ ์ ์ฉํ๋ ๊ฒ์ ์์ ํ์ง๋ง, ๋ฐ๋๋ก ์ปค์คํฐ ๋น ๊ตฌ์ฑ ์ ๋ณด์ ์ ์ฉํ๋ ๊ฑด ํผํ์.
@Configuration ํด๋์ค ๋ ๋ฒจ์
@ConditionalOnClass
@Bean ๋ฉ์๋ ๋ ๋ฒจ์
@ConditionalOnMissingBean
์กฐํฉ์ ๊ฐ์ฅ ๋ํ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ฐฉ์
ํด๋์ค ์กด์ฌ๋ก ํด๋น ๊ธฐ์ ์ฌ์ฉ ์ฌ๋ถ ํ์ธ โ ์ปค์คํ ๋น ๊ตฌ์ฑ ์กด์ฌ๋ฅผ ํ์ธํด์ ์๋ ๊ตฌ์ฑ์ ๋น ์ค๋ธ์ ํธ๋ฅผ ์ด์ฉํ ์ง ์ต์ข ๊ฒฐ์
Property Conditions
@ConditionalOnProperty
โข ์คํ๋ง์ ํ๊ฒฝ ํ๋กํผํฐ ์ ๋ณด๋ฅผ ์ด์ฉ โข ์ง์ ๋ ํ๋กํผํฐ๊ฐ ์กด์ฌํ๊ณ ๊ฐ์ด false ๊ฐ ์๋๋ฉด ํฌํจ ๋์ โข ํน์ ๊ฐ์ ๊ฐ์ง ๊ฒฝ์ฐ๋ฅผ ํ์ธํ๊ฑฐ๋ ํ๋กํผํฐ๊ฐ ์กด์ฌํ์ง ์์ ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋๋ก ์ค์ ๊ฐ๋ฅ โข ํ๋กํผํฐ์ ์กด์ฌ๋ฅผ ํ์ธํด์ ๋น ์ค๋ธ์ ํธ๋ฅผ ์ถ๊ฐํ๊ณ , ํด๋น ๋น ์ค๋ธ์ ํธ์์ ํ๋กํผํฐ ๊ฐ์ ์ด์ฉํด์ ์ธ๋ฐํ๊ฒ ๋น ๊ตฌ์ฑ ๊ฐ๋ฅ
Resource Conditions
@ConditionalOnResource
โข ์ง์ ๋ ๋ฆฌ์์ค(ํ์ผ) ์กด์ฌ ํ์ธ
Web Application Conditions
@ConditionalOnWebApplication
@ConditionalOnNotWebApplication
โข ์น ์ ํ๋ฆฌ์ผ์ด์ ์ฌ๋ถ ํ์ธ โข ex. ์น ๊ธฐ์ ์ ์ฌ์ฉํ์ง ์๋ ๋ฐฐ์น
SpEL Expression Conditions
@ConditionalOnExpression
โข ์คํ๋ง SpEL(์คํ๋ง ํํ์) ์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๊ธฐ์ค์ผ๋ก ํ๋จ โข ๋งค์ฐ ์์ธํ ์กฐ๊ฑด ์ค์ ๊ฐ๋ฅ
์ธ๋ถ ์ค์ ์ ์ด์ฉํ ์๋ ๊ตฌ์ฑ
์คํ๋ง์ Environment ์ถ์ํ

Environment.getProperty("property.name")
Environment ํ์ ์ ์ค๋ธ์ ํธ๋ฅผ ๊ฐ์ ธ์์ ํ๋กํผํฐ ์ด๋ฆ์ ์ ๊ณตํ์ฌ ์ค์ ๊ฐ ์กฐํ
ํ๋กํผํฐ ์ฐ์ ์์
ServletConfig Parameters
ServletContext Parameters
JNDI
System Properties
System Environment Variables
@PropertySource
xml, yml
์ ๊ณต๋๋ ํ๋กํผํฐ ์ด๋ฆ์ ๋ณํํด์ ์ค์ ๊ฐ ํ์
property.name
property_name
PROPERTY.NAME
PROPERTY_NAME
.
์๋ ๊ตฌ์ฑ์ Environment ํ๋กํผํฐ ์ ์ฉ
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory(Environment env) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(env.getProperty("contextPath"));
return factory;
}
์๋ ๊ตฌ์ฑ ํด๋์ค์ ๋ฉ์๋์
Environment
๋ฅผ ์ฃผ์ ๋ฐ์์ ๋น ์์ฑ์ผ๋ก ์ง์ ํ ํ๋กํผํฐ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์์
.
@Value
@Value
๋ element ๋ก placeholder(์นํ์)๋ฅผ ์ง์ ํ๊ณ ์ปจํ ์ด๋ ์ด๊ธฐํ์ ํ๋กํผํฐ ๊ฐ์ผ๋ก ์ด๋ฅผ ๋์ฒด์นํ์๋ฅผ ํ๋กํผํฐ ๊ฐ์ผ๋ก ๊ต์ฒดํ๋ ค๋ฉด
PropertySourcesPlaceholderConfigurer
ํ์ ์ ๋น ๋ฑ๋ก ํ์ํฉํ ๋ฆฌ์ ํ์ฒ๋ฆฌ๊ธฐ๋ก ๋์ํด์ ์ด๊ธฐ ๊ตฌ์ฑ ์ ๋ณด์์ ์นํ์๋ฅผ ์ฐพ์์ ๊ต์ฒดํ๋ ๊ธฐ๋ฅ ๋ด๋น
@MyAutoConfiguration
public class PropertyPlaceholderConfig {
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
.
ํ๋กํผํฐ ํด๋์ค์ ๋ถ๋ฆฌ
.
ํ๋กํผํฐ ๋น์ ํ์ฒ๋ฆฌ๊ธฐ ๋์
Spring JDBC ์๋ ๊ตฌ์ฑ ๊ตฌํ
์๋ ๊ตฌ์ฑ ํด๋์ค์ ๋น ์ค๊ณ
์๋ก์ด ๊ธฐ์ ์ ์๋ ๊ตฌ์ฑ ํด๋์ค๋ฅผ ์์ฑํ ๊ฒฝ์ฐ ์๋ ๊ตฌ์ฑ ํด๋์ค์ ์ ์ฉํ ์กฐ๊ฑด๊ณผ ๋ง๋ค์ด์ง๋ ๋น ์ค๋ธ์ ํธ ์ข ๋ฅ ๋ฑ์ ๋จผ์ ์ค๊ณ

๋ ๊ฐ์ DataSource ๊ตฌํ ํด๋์ค๋ฅผ ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ฑ๋ก
DataSourceProperties ํ๋กํผํฐ ํด๋์ค ์ด์ฉ
.
DataSource ์๋ ๊ตฌ์ฑ ํด๋์ค
@TestPropertySource
application.properties ํ์ผ ๋ฑ๋ก์ ์คํ๋ง ํ๋ ์์ํฌ ๊ธฐ๋ณธ ๋์ ๋ฐฉ์์ด ์๋
์คํ๋ง ๋ถํธ ์ด๊ธฐํ ๊ณผ์ ์์ ์ถ๊ฐํด ์ฃผ๋ ๊ฒ์ด๋ฏ๋ก ํ ์คํธ์์ ๋ณ๋ ์ถ๊ฐ ํ์
@TestPropertySource("classpath:/application.properties") ๋ก properties ์ ๋ณด๋ฅผ ์ฝ์ด์ค๋๋ก ์ค์
.
JdbcTemplate๊ณผ ํธ๋์ญ์
๋งค๋์ ๊ตฌ์ฑ
@ConditionalOnSingleCandidate
๋น ๊ตฌ์ฑ์ ๋ณด์ ํด๋น ํ์ ๋น์ด ํ ๊ฐ๋ง ๋ฑ๋ก๋์ด์๋ ๊ฒฝ์ฐ ์กฐ๊ฑด ๋งค์นญ
@EnableTransactionManagement
์ ๋ ธํ ์ด์ ์ ์ด์ฉํ๋ ํธ๋์ญ์ ๊ธฐ๋ฅ์ ์ด์ฉ
.
Hello Repository
์ธํฐํ์ด์ค์ default, static ๋ฉ์๋๋ฅผ ๋ฃ์ด์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์
์๋ฐ์ Comparator
<T
> ์ธํฐํ์ด์ค๋ฅผ ์ฐธ๊ณ ํด ๋ณด์.
Spring Boot
์คํ๋ง ๋ถํธ์ ์๋ ๊ตฌ์ฑ๊ณผ ํ
์คํธ
@SpringBootApplication
Spring Boot Main Annotation
@JdbcTest
Spring Boot Test ์ค๋น
์๋๊ตฌ์ฑ ์ค JDBC ๋ฅผ ์ด์ฉํ๊ธฐ ์ํด ํ์ํ ๋น๋ค๋ง ๋ก๋ฉ โ ๋น ๋ฅธ ํ ์คํธ
embedded db ๋ก dataSource ๊ต์ฒด โ ํ๋กํผํฐ๋ก ์ค์ ํ DB ์ ์ ์ ๋ณด ์ฌ์ฉ X
@SpringBootTest
์คํ๋ง ์ปจํ ์ด๋๋ฅผ ๋์ฐ๊ณ ์๋ ๊ตฌ์ฑ๊น์ง ์ ์ฉํด์ ํ ์คํธ
์๋ธ๋ฆฟ ์ปจํ ์ด๋ ํ๊ฒฝ ์ฌ๋ถ ์ค์
์น ํ๊ฒฝ ์ธํ ์ ์ธ: webEnvironment = SpringBootTest.WebEnvironment.NONE
์น ํ๊ฒฝ ์ธํ : webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT
.
์คํ๋ง ๋ถํธ ์์ธํ ์ดํด๋ณด๊ธฐ

์คํ๋ง ๋ถํธ์
๋์ ๋ฐฉ์ ์ดํด
์ฌ์ฉ ๊ธฐ์
๊ณผ ๊ด๋ จ๋์๋ ๊ตฌ์ฑ ๋น
๊ณผ๊ตฌ์ฑ
,์์ฑ
,ํ๋กํผํฐ ์ค์
๋ฑ์ ๋ถ์์๋ ๊ตฌ์ฑ ๋น๊ณผ ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ๋น ์ ํ ๊ธฐ์ค
์ด๋ฅผ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ํ์
.
์๋ ๊ตฌ์ฑ ๋ถ์ ๋ฐฉ๋ฒ
์๋ ๊ตฌ์ฑ ํ๋ณด ๋ชฉ๋ก๊ณผ ์กฐ๊ฑด ํ๋จ ๊ฒฐ๊ณผ ์กฐํํ๊ธฐ

-Ddebug
or--debug
์ธ์๋ฅผ ์ด์ฉํด์ ์คํ๋ง ๋ถํธ ์ ํ๋ฆฌ์ผ์ด์ ์คํConditionEvaluationReport
ํ์ ์ ๋น์ ์ฃผ์ ๋ฐ๊ณ , ํ์ํ ์ ๋ณด๋ง ์ ํํด์ ์๋ ๊ตฌ์ฑ ๊ฒฐ๊ณผ ํ์ธListableBeanFactory
ํ์ ์ ๋น์ ์ฃผ์ ๋ฐ๊ณ , ๋น ์ด๋ฆ์ ๊ฐ์ ธ์์(ํ์ ์ ๋น ์ค๋ธ์ ํธ๋) ๋ฑ๋ก๋ ๋น ๋ชฉ๋ก ํ์ธ์๋ ๊ตฌ์ฑ ์ ์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์ค์ผ๋ก
์คํ๋ง ๋ถํธ ๋ ํผ๋ฐ์ค ๋ฌธ์
,์๋ ๊ตฌ์ฑ ํด๋์ค ์์ค ์ฝ๋
,ํ๋กํผํฐ ํด๋์ค
,Customizer
๋ฑ์ ์ดํด๋ณด๋ฉฐ ์ด๋ป๊ฒ ์ด๋ค ์กฐ๊ฑด์ผ๋ก ๋์ํ ์ง ๋ถ์
VM options: -Ddebug
Positive matches:
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
AopAutoConfiguration.ClassProxyingConfiguration matched:
- @ConditionalOnMissingClass did not find unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition)
- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)
ApplicationAvailabilityAutoConfiguration#applicationAvailability matched:
- @ConditionalOnMissingBean (types: org.springframework.boot.availability.ApplicationAvailability; SearchStrategy: all) did not find any beans (OnBeanCondition)
...
Negative matches:
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
AopAutoConfiguration.AspectJAutoProxyingConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.aspectj.weaver.Advice' (OnClassCondition)
ArtemisAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
...
ConditionEvaluationReport
์กฐ๊ฑด์ด ๋งค์นญ๋ ์๋ ๊ตฌ์ฑ ํด๋์ค์ ๋ฉ์๋๋ฅผ ์ถ๋ ฅ
@Bean
ApplicationRunner run(ConditionEvaluationReport report) {
return args -> {
long result = report.getConditionAndOutcomesBySource().entrySet().stream()
.filter(co -> co.getValue().isFullMatch()) // ์ปจ๋์
์กฐ๊ฑด์ ๋ชจ๋ ํต๊ณผํ ๋น ๋ชฉ๋ก
.filter(co -> co.getKey().indexOf("Jmx") < 0) // Jmx ๊ด๋ จ ๊ตฌ์ฑ ์ ๋ณด ์ ์ธ
.map(co -> {
System.out.println(co.getKey());
co.getValue().forEach(c -> {
System.out.println("\t" + c.getOutcome()); // ์ปจ๋์
๋ ํต๊ณผ ์กฐ๊ฑด
});
System.out.println();
return co;
}).count();
System.out.println(result);
};
}
org.springframework.boot:spring-boot-starter
(springboot core)
spring boot code ์์ Jmx ์ ์ธ ๋น์ด ๋ชฉ๋ก
.
org.springframework.boot:spring-boot-starter-web
spring boot web ์์ Jmx ์ ์ธ ๋น ๋ชฉ๋ก
org.springframework.boot:spring-boot-starter-jdbc
jdbc ๊ด๋ จ ๋น ๋ชฉ๋ก
Last updated