์ฐ๋ฆฌ๊ฐ ์์ฑํ ์๋ง์ ์๋น์ค ๊ฐ์ฒด๋ค์ด ์ด๋ ์์ ์ ์์ฑ๋๊ณ ์ด๋ค ๊ณผ์ ์ ๊ฑฐ์ณ ์์ฑ๋๋ฉฐ ์ด๋ป๊ฒ ์๋ก ์ํตํ๋์ง๋ฅผ ์๋ ๊ฒ์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๋จ๊ณ์์ ์ ์ดํ๋ ๋จ๊ณ๋ก ๋์ด๊ฐ๋ ํต์ฌ ์ญํ ์ ํ ์ ์๋๋ก ํ๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ Bean์ ์์ฑ ํ ์ด๊ธฐ ์์ (Bean Initialization)๊ณผ Bean ์์ฑ ์์ ์ ํ์๋ฅผ ๊ณต๋ตํ๋ ํ์ฅ ๊ธฐ๋ฒ ๊ทธ๋ฆฌ๊ณ ๋ถํธ์คํธ๋ํ ๊ณผ์ ์์ ๋ฐ์ํ๋ ์ด๋ฒคํธ์ ์ด๋ฒคํธ ์์คํ (Event System)์ ๋ํด์ ์ ๋ฆฌํด ๋ณด๊ณ ์ ํ๋ค.
๋จผ์ ๋ณด๋ฉด ์ข์ ํฌ์คํ
Spring Boot ์์ โ SpringApplication ์์ฑ๊ณผ ์๋ ๊ฐ์ง
Spring Boot โ Application Bootstraping์ ์ํ 13๋จ๊ณ
Bean Initialization ๊ณผ์
BeanPostProcessor์ ์ญํ
์๋ ์ฝ๋๋ BeanPostProcessor ์ธํฐํ์ด์ค๋ฅผ ๋ํ๋ธ๋ค. BeanPostProcessor๋ Spring Boot ๋ด๋ถ์์ ์ฌ์ฉํ๋ ๊ตฌํ์ฒด ์ธ์๋ Bean ์์ฑ ๊ณผ์ ์์ ์ฌ์ฉ์ ์ ์ ์ปค์คํ
๋ก์ง์ ์ถ๊ฐํ ์ ์๋ ํ์ฅ ํฌ์ธํธ ์ญํ ์ ํ๋ค.
BeanPostProcessor๋ ๊ฐ๋ณ Bean์ด ์์ฑ๋ ๋๋ง๋ค ๋งค๋ฒ ์คํ๋๋ค. BeanPostProcessor์์ ๋ฆฌํดํ๋ bean์ด ์ต์ข
์ ์ผ๋ก ApplicationContext์ Bean์ผ๋ก ๋ฑ๋ก๋๋ค.
public interface BeanPostProcessor {
//๋น ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ ์์กด์ฑ ์ฃผ์
์ด ์๋ฃ๋ ํ @PostConstruct๋ afterPropertiesSet์ด ํธ์ถ๋๊ธฐ ์ง์ ์ ์คํ๋๋ค.
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
//์ด๊ธฐํ ๋ฉ์๋(@PostConstruct๋ฑ)๊ฐ ์คํ๋ ์งํ์ ์คํ๋๋ค. ์ฃผ๋ก ํ๋ก์ ๊ฐ์ฒด๋ก ๊ฐ์ธ๊ฑฐ๋(AOP) ์ต์ข
๊ฒ์ฆ์ ํ ๋ ์ฌ์ฉํ๋ค.
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}JavaSpring Boot โ Application Bootstraping์ ์ํ 13๋จ๊ณ์์ 10๋จ๊ณ ApplicationContext Refresh ๊ณผ์ ์์ Spring Boot ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๋ BeanPostProcessor ๊ตฌํ์ฒด๋ค์ ๋ฑ๋กํ๋๋ฐ ๋ํ์ ์ธ ๊ตฌํ์ฒด๋ค์ ๋ค์๊ณผ ๊ฐ๋ค.
- AutowiredAnnotationBeanPostProcessor
- ๋ชฉ์ : ์์กด์ฑ ์ฃผ์ (DI)์ ํต์ฌ ์์ง
- ์ญํ : @Autowired, @Value, @Inject ์ด๋ ธํ ์ด์ ์ ๊ฐ์งํ์ฌ ํด๋น ํ๋๋ ๋ฉ์๋์ ๋น์ ์๋์ผ๋ก ์ฃผ์ ํ๋ค.
- ์คํ์์ : Bean ์ธ์คํด์ค๊ฐ ์์ฑ๋ ์งํ ์ด๊ธฐํ ๋ฉ์๋(@PostConstruct๋ฑ)๊ฐ ์คํ๋๊ธฐ ์ ์ ๋์ํ๋ค.
- CommonAnnotationBeanPostProcessor
- ๋ชฉ์ : ์๋ฐ ํ์ค(JSR-250) ์ด๋ ธํ ์ด์ ์ง์
- ์ญํ : @PostConstruct, @PreDestroy ์ด๋ ธํ ์ด์ ์ ์ฒ๋ฆฌํ๋ค. ๋ํ @Resource ์ด๋ ธํ ์ด์ ์ ํตํ ๋ฆฌ์์ค ์ฃผ์ ๋ ๋ด๋นํ๋ค.
- ์คํ์์ : ์ด๊ธฐํ ์ ํ ๋จ๊ณ์์ ๊ฐ๊ฐ ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
- AnnotationAwareAspectJAutoProxyCreator
- ๋ชฉ์ : AOP ํ๋ก์ ์์ฑ
- ์ญํ : @Aspect ์ด๋ ธํ ์ด์ ์ด ๋ถ์ Bean์ ๋ถ์ํ๊ณ ์ด๋๋ฐ์ด์ค๊ฐ ์ ์ฉ๋ ๋์ ๋น๋ค์ ์ฐพ์ ํ๋ก์ ๊ฐ์ฒด๋ก ๋ฐ๊พผ๋ค.
- ์คํ์์ : postProcessAfterInitialization ๋จ๊ณ์์ ์ค์ ๊ฐ์ฒด๋ฅผ ํ๋ก์๋ก ๋ํํ์ฌ ๋ฐํํ๋ค.
- ApplicationContextAwareProcessor
- ๋ชฉ์ : ์ธํ๋ผ ๊ฐ์ฒด ์ฃผ์
- ์ญํ : Bean์ด ApplicationContextAware, EnvironmentAware, ResourceLoaderAware๋ฑ์ ๊ตฌํํ๊ณ ์๋ค๋ฉด ํด๋น ๋น์๊ฒ ApplicationContext๋, Environment ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ค.
- ConfigurationClassPostProcessor
- ์๋ฐํ ๋งํ๋ฉด BeanFactoryPostProcessor์ด์ง๋ง ๊ฐ์ฅ ๋จผ์ ์คํ๋์ด @Configuration, @Bean, @Component, @Import๋ฅผ ์ค์บํ๊ณ Bean ์ ์๋ฅผ ์์ฑํ๋ ๋งค์ฐ ์ค์ํ ์ญํ ์ ํ๋ค.
InitializingBean๊ณผ @PostConstruct
๋ค์์ Bean ์ด๊ธฐํ๋ฅผ ์ํ ๋๊ฐ์ง ๋ฐฉ๋ฒ์ ๋ํ๋ธ๋ค.
@Component
public class MyService implements InitializingBean {
@PostConstruct
public void postConstruct() {
// @PostConstruct๊ฐ ๋จผ์ ์คํ๋จ
System.out.println("@PostConstruct executed");
}
@Override
public void afterPropertiesSet() throws Exception {
// InitializingBean.afterPropertiesSet()์ด ๋์ค์ ์คํ๋จ
System.out.println("afterPropertiesSet executed");
}
}Java์คํ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ์์ฑ์ ํธ์ถ
- ์์กด์ฑ ์ฃผ์
- BeanPostProcessor.postProcessBeforeInitialization()
- @PostConstruct ๋ฉ์๋ ์คํ
- InitializingBean.afterPropertiesSet() ์คํ. (@PostConstruct๊ฐ ์ฐ์ ์คํ๊ถ์ ๊ฐ์ง๋ค)
- ์ปค์คํ init-method ์คํ (@Bean ์ด๋ ธํ ์ด์ ์ initMethod ์์ฑ์ผ๋ก ์ง์ ๋ ๋ฉ์๋๋ฅผ ์๋ฏธํ๋ค)
- BeanPostProcessor.postProcessAfterInitialization()
SmartInitializingSingleton
SmartInitializingSingleton์ ๋ชจ๋ ์ฑ๊ธํค Bean์ด ๋ง๋ค์ด์ง ํ์ ๋ฑ ํ๋ฒ ์คํ๋์ด์ผ ํ๋ ๋ก์ง์ด ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉํ ์ ์๋ค.
SmartInitializingSingleton์ 10๋จ๊ณ์ ApplicationContext Refresh ๊ณผ์ ์ ๋ง์ง๋ง ์์ ์ธ finishBeanFactoryInitialization() ๋จ๊ณ์์ ๋์ํ๋ค. ๋ง์ง๋ง ๊ณผ์ ์ธ๋งํผ ๋ชจ๋ Non-Lazy ์ฑ๊ธํค Bean์ ์ธ์คํด์คํ ๋ฐ ์์กด์ฑ ์ฃผ์
, ์ด๊ธฐํ ์ฝ๋ฐฑ(@PostConstruct)์ด ์์ ํ ๋๋ ํ์ ์คํ๋๋ค.
@PostConstruct ์์ ์๋ ์์ง ๋ค๋ฅธ Bean๋ค์ด ์์ฑ ์ค์ผ ์๋ ์๊ธฐ ๋๋ฌธ์ ๋ง์ฝ ์ ํ๋ฆฌ์ผ์ด์
์ ์กด์ฌํ๋ ๋ชจ๋ ๋น์ ๋ค ๋ค์ ธ์ ํน์ ์์
์ ํด์ผ ํ๋ค๋ฉด @PostConstruct๋ ์ํํ๋ค. ์์ง ์์ฑ๋์ง ์์ Bean์ด ์์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ ์ฝ๋๋ ์ ํ๋ฆฌ์ผ์ด์
๊ตฌ๋ ์งํ ๋ฑ๋ก๋ ๋ชจ๋ Controller๋ฅผ ์ฐพ์ ํน์ ์์
์ ์ํํ๋ ์ํ ์ฝ๋๋ค.
@Component
public class RouteConfigurationLogger implements SmartInitializingSingleton {
private final ListableBeanFactory beanFactory;
public RouteConfigurationLogger(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterSingletonsInstantiated() {
// ๋ชจ๋ ๋น์ด ์ด๋ฏธ ๋ค ์์ฑ๋์์ผ๋ฏ๋ก ์์ ํ๊ฒ ๋ชจ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ์กฐํํ ์ ์๋ค.
Map<String, Object> controllers = beanFactory.getBeansWithAnnotation(RestController.class);
System.out.println(">>> [์ต์ข
ํ์ธ] ํ์ฌ ๋ฑ๋ก๋ ๋ชจ๋ ์ปจํธ๋กค๋ฌ ์: " + controllers.size());
controllers.forEach((name, bean) -> {
// ์์ธ ๋ก์ง ์ฒ๋ฆฌ...
});
}
}JavaSpring Boot Event System
Spring Boot๋ ์ ํ๋ฆฌ์ผ์ด์ ์๋ช ์ฃผ๊ธฐ ๋์ ๋ค์ํ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ์ฌ ํ์ฅ์ฑ์ ์ ๊ณตํ๋ค.
@EventListener ๋์ ์๋ฆฌ
Spring์ ์ด๋ฒคํธ ์์คํ ์ Observer ํจํด์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉฐ @EventListener ์ด๋ ธํ ์ด์ ์ด ์ง์ ๋ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
@EventListener ๋ฑ๋ก ๊ณผ์
- Bean ์ ์ ๋ก๋ & ์ธ์คํด์ค ์์ฑ
- ApplicationContext๋ฅผ ์ด๊ธฐํ ํ๋ฉด์ ๋ชจ๋ @Component, @Service, @Configuration ๋ฑ์ ๋น์ ์์ฑ ํ๋ค.
- ์ด ์์ ์๋ @EventListener๊ฐ ๋ถ์ ๋ฉ์๋๋ ์์ง ๋ฆฌ์ค๋๋ก ๋ฑ๋ก๋์ง ์์ ์ํ๋ค.
- EventListenerMethodProcessor Bean ํ์ฒ๋ฆฌ๊ธฐ ์คํ (10๋จ๊ณ ApplicationContext Refresh)
- Spring Boot ์๋ ์ค์ ์ ์ํด์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฑ๋ก๋๋ค.
- ๋ชจ๋ Bean์ ์ํํ๋ฉฐ @EventListener ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๋ฉ์๋๋ฅผ ์ค์บํ๋ค.
- ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์๋ ApplicationListener๋ฅผ ๊ตฌํํ ApplicationListenerMethodAdapter ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค. (ApplicationListenerMethodAdapter๊ฐ ๋ฆฌ์ค๋ ์ญํ ์ ํ๋ค๋ ๊ฒ์ด๋ค.)
- ApplicationEventMulticaster์ ApplicationListenerMethodAdapter๋ฅผ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ก ์ถ๊ฐ ๋ฑ๋กํ๋ค.
- ApplicationListenerMethodAdapter ์ด๋ฒคํธ ์์
- ApplicationListenerMethodAdapter๊ฐ ์ฒ๋ฆฌํด์ผ ํ Event๊ฐ ๋ฐ์ํ๋ฉด ApplicationEventMulticaster๊ฐ ApplicationListenerMethodAdapter์๊ฒ ์ด๋ฒคํธ ๋ฐํ ์์์ ์ ๋ฌํ๋ค.
- ApplicationListenerMethodAdapter๋ ์ด๋ฒคํธ๋ฅผ ์์ ํ๋ฉด @EventListener ๋ฉ์๋๋ฅผ ๋ฆฌํ๋ ์ ์ผ๋ก ํธ์ถํ๋ค.
์๋ ์ฝ๋๋ EventListenerMethodProcessor ํด๋์ค๋ฅผ ๋ํ๋ธ๋ค.
// ๊ฐ๋ณ Bean์ด ์์ฑ๋ ๋๋ง๋ค BeanPostProcessor๊ฐ ๋์
public class EventListenerMethodProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// Bean์์ @EventListener ์ด๋
ธํ
์ด์
์ด ๋ถ์ ๋ฉ์๋ ์ค์บ
// ์ฆ @EventListener ์ด๋
ธํ
์ด์
์ด ๋ถ์ ๋ฉ์๋๋ฅผ ๊ฐ์ง ํด๋์ค๋ Bean ์ด์ด์ผ ํจ.
// Bean์ด ์๋๋ฉด @EventListener๋ ๋์ํ์ง ์์.
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(EventListener.class)) {
// ApplicationEventMulticaster์ ๋ฆฌ์ค๋ ๋ฑ๋ก
registerEventListener(bean, method);
}
}
return bean;
}
}Java์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์์ฑ๋ Bean์์ @EventListener ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๋ฉ์๋๋ฅผ ์ฐพ์ registerEventListener๋ก ๋ฑ๋กํ๋ ๊ฒ์ ์ ์ ์๋ค.
@EventListener ๋ฉ์๋ ์คํ ๋ฉ์ปค๋์ฆ
Spring Boot๋ @EventListener ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๋ฉ์๋๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ApplicationListener ์ธํฐํ์ด์ค ๊ตฌํ์ฒด๋ฅผ ์์ฑํ๋ค. (ApplicationListenerMethodAdapter) ํต์ฌ์ ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์ Event ํ์ ์ ๋ถ์ํด์ ํด๋น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋๋ง ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ ๊ฒ์ด๋ค. ์ฆ @EventListener ๋ฉ์๋์ ์๊ทธ๋์ฒ์ ๋ฐ์ํ ์ด๋ฒคํธ๊ฐ ๋์ผํ Event์ธ ๋ฉ์๋๋ฅผ ์ฐพ์์ ํธ์ถ๋๋ค๋ ๊ฒ์ด๋ค.
- ApplicationEventMulticaster์์ ๋ฐํ๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฆฌ์ค๋ ์ฐพ๊ธฐ
- Event๊ฐ ๋ฐํ๋๋ฉด ApplicationEventMulticaster๋ ๋ฑ๋ก๋ ๋ชจ๋ ๋ฆฌ์ค๋์ ๋ช ๋จ์ ํ๋๋ค.
- ์ด ๋ ๋ฆฌ์ค๋์ supportsEventType ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ํด๋น ๋ฆฌ์ค๋๊ฐ ์ฒ๋ฆฌํ๋ ์ด๋ฒคํธ ํ์ ์ ์ป์ด์ ๋ฐํ๋ ์ด๋ฒคํธ ํ์ ๊ณผ ๋น๊ตํ์ฌ ํ์ ์ด ๋ง๋ ๋ฆฌ์ค๋(ApplicationListenerMethodAdapter)์ onApplicationEvent ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
- ApplicationListenerMethodAdapter์ 2์ฐจ ๊ฒ์ฆ(shouldHandle)
- Payload ์ฒดํฌ: PayloadApplicationEvent์ธ ๊ฒฝ์ฐ ์ค์ ๋ฐ์ดํฐ ํ์ ์ด ๋ง๋์ง ๋ค์ ํ์ธํ๋ค.
- Condition ์ฒดํฌ: @EventListener(condition = “#event.success == true”) ์ฒ๋ผ ์กฐ๊ฑด์์ด ๋ถ์ด ์๋ค๋ฉด ์ด ์กฐ๊ฑด๊น์ง ๋ง์กฑํด์ผ @EventListener ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์๋ ApplicationListenerMethodAdapter ํด๋์ค ์ฝ๋์ ์ฃผ์์ ์ฐธ๊ณ ํ๊ธฐ ๋ฐ๋๋ค.
// Spring์ด ๋ด๋ถ์ ์ผ๋ก ์์ฑํ๋ ApplicationListener ๋ํผ
public class ApplicationListenerMethodAdapter implements ApplicationListener<ApplicationEvent> {
private final Object targetBean;
private final Method targetMethod;
private final Class<?> eventType; // ํ๋ผ๋ฏธํฐ์์ ์ถ์ถํ ์ด๋ฒคํธ ํ์
// @EventListener ๋ฉ์๋๊ฐ ํฌํจ๋ Bean๊ณผ method ์ ๋ณด๋ฅผ ์ค์ ํ๋ค.
public ApplicationListenerMethodAdapter(Object bean, Method method) {
this.targetBean = bean;
this.targetMethod = method;
// ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์์ ์ด๋ฒคํธ ํ์
์ถ์ถ
this.eventType = method.getParameterTypes()[0];
}
...
...
// ApplicationEventMulticaster์ 1์ฐจ ํํฐ(๋ฐํ๋ Event ํ์
๊ณผ ํ๋ผ๋ฏธํฐ Event ํ์
์ด ๋ง๋..) ํต๊ณผ์ ํธ์ถ
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (isDefaultExecution()) {
processEvent(event);
}
}
...
...
public void processEvent(ApplicationEvent event) {
// ๋ฐํ๋ ์ด๋ฒคํธ๊ฐ ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์ ์ผ์นํ๋์ง ํ์ธํ๊ณ ์ธ์ ๋ฆฌ์คํธ ์์ฑ
Object[] args = resolveArguments(event);
// @EventListener(condition="...") ๋ฑ์ ์กฐ๊ฑด์ด ์๋ค๋ฉด ์ฒดํฌ
// ์ฌ๊ธฐ์ 2์ฐจ ํํฐ ๋์
if (shouldHandle(event, args)) {
// ์ค์ Bean์ @EventListener ๋ฉ์๋ ํธ์ถ
Object result = doInvoke(args);
if (result != null) {
// ๋ฉ์๋ ๋ฐํ๊ฐ์ด ์๋ค๋ฉด ๊ทธ ๋ฐํ๊ฐ์ ๋ค์ ์๋ก์ด ์ด๋ฒคํธ๋ก ๋ฐํ (Chaning)
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle");
}
}
}
...
...
@Nullable
protected Object[] resolveArguments(ApplicationEvent event) {
// ํ์ฌ ๋ฐํ๋ ์ด๋ฒคํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ผ๋ฏธํฐ์ ์ ์๋ ์ด๋ฒคํธ ํ์
์ ๊ฐ์ ธ์จ๋ค.
ResolvableType declaredEventType = getResolvableType(event);
if (declaredEventType == null) {
return null;
}
// @EventListener ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๊ฐ ์๋ค๋ฉด ์ธ์๋ฅผ ๋๊ธธ ํ์๊ฐ ์์ผ๋ฏ๋ก ๋น ๋ฐฐ์ด ๋ฐํ
if (this.method.getParameterCount() == 0) {
return new Object[0];
}
// ๋ฉ์๋์ ์ ์ธ๋ ํ๋ผ๋ฏธํฐ์ Class ํ์
์ ๊ฐ์ ธ์ด
Class<?> declaredEventClass = declaredEventType.toClass();
// ๋ฉ์๋ ํ๋ผ๋ฏธํฐ๊ฐ ApplicationEvent๋ฅผ ์์๋ฐ์ง ์์๊ณ (์ฆ, ์ผ๋ฐ DTO๋ String๊ณผ ๊ฐ์.)
// ๋ฉ์๋ ํ๋ผ๋ฏธํฐ๊ฐ PayloadApplicationEvent(๋ด์ฉ์ ๋ด๊ณ ์๋ ์ด๋ฒคํธ)๋ผ๋ฉด
if (!ApplicationEvent.class.isAssignableFrom(declaredEventClass) &&
event instanceof PayloadApplicationEvent<?> payloadEvent) {
// ๋ฐํ๋ ์ด๋ฒคํธ์์ ์ค์ ๋ฐ์ดํฐ(payload)๋ฅผ ๊บผ๋ธ๋ค.
Object payload = payloadEvent.getPayload();
// ๊บผ๋ธ payload ํ์
์ด ๋ฉ์๋ ํ๋ผ๋ฏธํฐ ํ์
๊ณผ ์ผ์นํ๋์ง ํ์ธํ๋ค.
if (declaredEventClass.isInstance(payload)) {
// ์ผ์นํ๋ค๋ฉด ์ด๋ฒคํธ๊ฐ์ฒด๊ฐ ์๋ ๋ฐ์ดํฐ(payload)๋ฅผ ์ธ์๋ก ๊ฒฐ์ ํ๋ค.
return new Object[] {payload};
}
}
return new Object[] {event};
}
}Java๋ค์์ @EventListener๋ฅผ ์ฌ์ฉํ๋ ์ํ์ฝ๋๋ค. ์ฃผ์์ ์ฐธ๊ณ ํ๊ธฐ ๋ฐ๋๋ค.
// @EventListener ๋ฉ์๋ - ํ๋ผ๋ฏธํฐ ํ์
์ผ๋ก ์ด๋ฒคํธ ๋งคํ
@Component
public class MyEventListener {
// ApplicationStartingEvent๊ฐ ๋ฐํ๋๋ฉด
// ApplicationEventMulticaster --> handleStarting ๋ฉ์๋ ์ฒ๋ฆฌ์ฉ ApplicationListenerMethodAdapter ํธ์ถ
// ApplicationListernerMethodAdapter์์ ๋ฆฌํ๋ ์
์ผ๋ก AplicationStartingEvent ๊ฐ์ฒด์ ํจ๊ป handleStarting์ ํธ์ถ
@EventListener
public void handleStarting(ApplicationStartingEvent event) {
log.info("Application is starting...");
}
// ApplicationReadyEvent๊ฐ ๋ฐํ๋๋ฉด
// ApplicationEventMulticaster --> handleReady ๋ฉ์๋ ์ฒ๋ฆฌ์ฉ ApplicationListenerMethodAdapter ํธ์ถ
// ApplicationListernerMethodAdapter์์ ๋ฆฌํ๋ ์
์ผ๋ก ApplicationReadyEvent ๊ฐ์ฒด์ ํจ๊ป handleReady๋ฅผ ํธ์ถ
@EventListener
public void handleReady(ApplicationReadyEvent event) {
log.info("Application is ready...");
}
// ApplicationEvent๊ฐ ๋ฐํ๋๋ฉด
// ApplicationEventMulticaster --> handleAnyEvent ๋ฉ์๋ ์ฒ๋ฆฌ์ฉ ApplicationListenerMethodAdapter ํธ์ถ
// ApplicationListernerMethodAdapter์์ ๋ฆฌํ๋ ์
์ผ๋ก ApplicationEvent ๊ฐ์ฒด์ ํจ๊ป handleAnyEvent๋ฅผ ํธ์ถ
// ApplicationEvent๋ ๋ชจ๋ ApplicationEvent ํ์
์ ์ถ์ ํด๋์ค์ด๋ฏ๋ก ๋ชจ๋ ApplicationEvent ํ์
์ ์ด๋ฒคํธ๊ฐ ๋ฐ์๋๋ฉด ํธ์ถ
@EventListener
public void handleAnyEvent(ApplicationEvent event) {
log.info("Any application event occurred: {}", event.getClass().getSimpleName());
}
// Payload ๊ธฐ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ (POJO ๋ฐฉ์)
// ApplicationEvent๋ฅผ ์์๋ฐ์ง ์์ ์์ ๊ฐ์ฒด(String, DTO ๋ฑ)๋ ์ฒ๋ฆฌ ๊ฐ๋ฅํ๋ค.
// String ํ์
์ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ ์ด๋ฒคํธ๊ฐ ๋ฐํ๋๋ฉด
// ๋ด๋ถ์ ์ผ๋ก ApplicationListenerMethodAdapter์ resolveArguments๊ฐ payload๋ฅผ ์ถ์ถํฉ๋๋ค. (String ํ์
)
// ApplicationListernerMethodAdapter์์ ๋ฆฌํ๋ ์
์ผ๋ก String ๋ฐ์ดํฐ์ ํจ๊ป handleStringPayload ํธ์ถ.
@EventListener
public void handleStringPayload(String message) {
log.info("๋ฌธ์์ด ํ์ด๋ก๋ ์ด๋ฒคํธ ์์ : {}", message);
}
// PayloadApplicationEvent ์์ฒด๋ฅผ ์ธ์๋ก ๋ฐ๋ ๊ฒฝ์ฐ
// ํ์ด๋ก๋๋ฟ๋ง ์๋๋ผ ์ด๋ฒคํธ์ ๋ถ๊ฐ ์ ๋ณด(ํ์์คํฌํ ๋ฑ)๊ฐ ํ์ํ ๋ ์ฌ์ฉํฉ๋๋ค.
@EventListener
public void handlePayloadEvent(PayloadApplicationEvent<MyCustomDto> event) {
MyCustomDto dto = event.getPayload();
log.info("PayloadApplicationEvent ๋ํผ๋ฅผ ํตํ ์์ : {}, ๋ฐํ์๊ฐ: {}",
dto.getName(), event.getTimestamp());
}
}Java์ด๋ฒคํธ ๋ฐํ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ถ์ํ
์์ ํฌ์คํ ์์ ApplicationEvent๊ฐ ๋ฐํ๋์์ ๋ ์ฌ์ฉ์ ์ ์ ์ฒ๋ฆฌ๋ฅผ ์ํด์ ApplicationListener<ApplicationEvent>๋ฅผ ๊ตฌํํ ๊ตฌํ์ฒด ํด๋์ค๋ฅผ ์ ์ํ๊ณ onApplicationEvent ๋ฉ์๋๋ฅผ override ํ์ฌ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ์๊ฐํ์๋ค.
@EventListener ์ด๋ ธํ ์ด์ ์ ApplicationListener๋ฅผ ๊ตฌํํ๋ ๋ฐฉ์์ ๋์ฒดํ ์ ์๋ ์กฐ๊ธ ๋ ํธ๋ฆฌํ๊ฒ ์ ๊ณต๋๋ ์ถ์ํ๋ผ๊ณ ๋ณผ ์ ์๋ค.
์ ํ๋ฆฌ์ผ์ด์ ๋ถํธ์คํธ๋ํ ๊ณผ์ ์์ ๋ฐ์ํ๋ ์ด๋ฒคํธ ์์
- ApplicationStartingEvent: ์ ํ๋ฆฌ์ผ์ด์ ์์ ์งํ (1๋จ๊ณ)
- ApplicationEnvironmentPreparedEvent: Environment ์ค๋น ์๋ฃ (6๋จ๊ณ)
- ApplicationContextInitializedEvent: ApplicationContext ์ด๊ธฐํ ์๋ฃ (9๋จ๊ณ)
- ApplicationContextPreparedEvent: ApplicationContext ์ค๋น, Bean ์ ์ ๋ก๋ (Bean ์์ฑ ์๋) (9๋จ๊ณ)
- ContextRefreshedEvent: ApplicationContext Refresh ์๋ฃ (10๋จ๊ณ)
- ApplicationStartedEvent: ์ ํ๋ฆฌ์ผ์ด์ ์์ ์๋ฃ – ApplicationContext๊ฐ ์์ ํ ๋ก๋๋ ์งํ
- AvailabilityChangeEvent: ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ์ฉ์ฑ ์ํ ๋ณ๊ฒฝ (12๋จ๊ณ)
- ApplicationReadyEvent: ์ ํ๋ฆฌ์ผ์ด์ ์ค๋น ์๋ฃ (13๋จ๊ณ)
ApplicationStartingEvent ๋ถํฐ ApplicationReadyEvent ๊น์ง
@EventListener ๋ฉ์๋์ ๊ฐ ApplicationEvent๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ง์ ํ์ฌ ํด๋น ์ด๋ฒคํธ ๋ฐ์์ ์ํ๋ ์์ ์ ํ ์ ์๋ค.
// ์ํ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ตฌํ ์์
@Component
@Slf4j
public class StartupEventListener {
//์๋ ์ด๋ฒคํธ๋ ํธ์ถ๋์ง ์๋๋ค.
//@Component๋ก Bean์ ๋ฑ๋กํ๋๋ฐ Bean์ด ์์ฑ๋๋ 10๋จ๊ณ (ApplicationContext Refresh)
//๊ณผ์ ์์ ์ด Bean์ด ์์ฑ๋๋๋ฐ ApplicationStartingEvent๋ ์ด Bean์ด ์์ฑ๋๊ธฐ
//์ด์ ์ ๋ฐํ๋๋ ์ด๋ฒคํธ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
@EventListener
public void handleApplicationStarting(ApplicationStartingEvent event) {
// ๊ฐ์ฅ ๋จผ์ ๋ฐ์ํ๋ ์ด๋ฒคํธ
// ๋ก๊น
์์คํ
์ด๊ธฐํ, ๊ธฐ๋ณธ ์ค์ ๋ฑ
log.info("Application starting...");
}
//์๋ ์ด๋ฒคํธ ๋ฉ์๋๋ ํธ์ถ๋์ง ์๋๋ค.
//@Component๋ก Bean์ ๋ฑ๋กํ๋๋ฐ Bean์ด ์์ฑ๋๋ 10๋จ๊ณ (ApplicationContext Refresh)
//๊ณผ์ ์์ ์ด Bean์ด ์์ฑ๋๋๋ฐ ApplicationEnvironmentPreparedEvent๋ ์ด Bean์ด ์์ฑ๋๊ธฐ
//์ด์ ์ ๋ฐํ๋๋ ์ด๋ฒคํธ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
@EventListener
public void handleEnvironmentPrepared(ApplicationEnvironmentPreparedEvent event) {
// Environment๊ฐ ์ค๋น๋์ด ํ๋กํผํฐ ์ ๊ทผ ๊ฐ๋ฅ
ConfigurableEnvironment env = event.getEnvironment();
String profile = String.join(",", env.getActiveProfiles());
log.info("Active profiles: {}", profile);
}
// ์๋ ์ด๋ฒคํธ๋ ์ด Bean์ด ์์ฑ๋ ์ด๋ฒคํธ์ด๋ฏ๋ก ํธ์ถ๋๋ค.
@EventListener
public void handleApplicationReady(ApplicationReadyEvent event) {
// ์ ํ๋ฆฌ์ผ์ด์
์ด ์์ ํ ์ค๋น๋จ
// ์ธ๋ถ ์์คํ
์ฐ๊ฒฐ, ํฌ์ค ์ฒดํฌ ๋ฑ๋ก ๋ฑ
log.info("Application ready to serve requests");
}
}Java์ด๋ฒคํธ ๋ฆฌ์ค๋ ์คํ ์์ ์ ์ด
์ฌ๋ฌ ๋ฆฌ์ค๋๊ฐ ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋ ์คํ ์์๋ฅผ ์ ์ดํ ์ ์๋ค.
@Component
public class OrderedEventListener {
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE) // ๊ฐ์ฅ ๋์ ์ฐ์ ์์
public void handleFirst(ApplicationReadyEvent event) {
log.info("First handler executed");
}
@EventListener
@Order(Ordered.LOWEST_PRECEDENCE) // ๊ฐ์ฅ ๋ฎ์ ์ฐ์ ์์
public void handleLast(ApplicationReadyEvent event) {
log.info("Last handler executed");
}
}Java์ฐ์ ์์์ ๋ฐ๋ผ handleFirst -> handleLast ์์๋ก ์คํ ๋๋ค.
์กฐ๊ฑด๋ถ ์ด๋ฒคํธ ์ฒ๋ฆฌ
SpEL(Spring Expression Language)์ ์ฌ์ฉํ์ฌ ์กฐ๊ฑด๋ถ๋ก ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค.
์กฐ๊ฑด๋ถ ์ฒดํฌ๋ ApplicationListenerMethodAdapter์ shouldHandle ๋ด์์ ์ฒดํฌ๋ฅผ ํ๋ค๊ณ ํ๋ค.
@Component
public class ConditionalEventListener {
/*
activeProfiles์ 'dev'๊ฐ ํฌํจ๋ ๊ฒฝ์ฐ์๋ง ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
*/
@EventListener(condition = "#event.environment.activeProfiles.contains('dev')")
public void handleDevOnly(ApplicationEnvironmentPreparedEvent event) {
log.info("This only runs in dev profile");
}
/*
์ ํ๋ฆฌ์ผ์ด์
์์์ 5์ด(5000ms) ์ด์ ์์๋ ๊ฒฝ์ฐ์๋ง ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
*/
@EventListener(condition = "#event.timeTaken.toMillis() > 5000")
public void handleSlowStartup(ApplicationStartedEvent event) {
log.warn("Application took more than 5 seconds to start: {} ms",
event.getTimeTaken().toMillis());
}
}Java- event.environment.activeProfiles
- ApplicationEnvironmentPreparedEvent ๊ฐ์ฒด์ ์ ๋ฌ๋ environment(Environment ๊ฐ์ฒด)๋ฅผ ๋ฐ๋ก access ํ๋ค.
- Environment ๊ฐ์ฒด์ ์ค์ ๋ ํ์ฑํ๋ ํ๋กํ์ผ(active profiles) ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค.
์ปค์คํ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ตฌํ ํจํด
์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ ๋ฆฌ์ค๋
์์์ ์ดํด๋ดค๋ @EventListener ์ด๋ ธํ ์ด์ ๋ฉ์๋๋ฅผ ํตํด์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๊ตฌํํ ์ ์๋ค.
@Component
public class CustomEventListener {
//Application Bootstraping์ด ๋๋ ํ ApplicationReadyEvent๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ ์ ์๋ค.
@EventListener
@Async
public void handleAsyncEvent(ApplicationReadyEvent event) {
// ๋น๋๊ธฐ ์ฒ๋ฆฌ
}
//Application์ด ์์๋ ์งํ ApplicationStartedEvent๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ ์ ์๋ค.
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handleHighPriorityEvent(ApplicationStartedEvent event) {
// ๋์ ์ฐ์ ์์๋ก ์ฒ๋ฆฌ
}
}JavaApplicationListener ์ธํฐํ์ด์ค ๊ตฌํ
Spring Boot โ Application Bootstraping์ ์ํ 13๋จ๊ณ ์์ ์ฝ๋์์ ์ฒ๋ฆฌํ ๋ฐฉ์์ผ๋ก ๋ฆฌ์ค๋๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค.
@Component
public class TypedEventListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// ํ์
์์ ํ ์ด๋ฒคํธ ์ฒ๋ฆฌ
log.info("Application ready at: {}", event.getTimeTaken());
}
}Java@EventListener์ ApplicationListener ๋ฑ๋ก ์ฐจ์ด
@EventListener๋ฅผ ํฌํจํ๋ ํด๋์ค๋ ๋ฐ๋์ ์คํ๋ง Bean(@Component)์ผ๋ก ๋ฑ๋ก์ ํด์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ @Component์ addListener ํน์ /META-INF/spring.factories ๋ฑ๋ก ์ฐจ์ด
ApplicationListener๋ฅผ ๊ตฌํํ ๊ฒฝ์ฐ์๋ ์คํ๋ง Bean(@Component) ํํ๋ก ๋ฑ๋กํ๊ฑฐ๋ META-INF/spring.factories์ ๋ฑ๋กํ๊ฑฐ๋ SpringApplication์ addListener๋ฅผ ํตํด์ ์ถ๊ฐํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ @Component๋ก ๋ฑ๋กํ ๊ฒฝ์ฐ์ META-INF/spring.factories ํน์ addListener๋ฅผ ํตํด์ ์ ์ฉํ๋ ๊ฒฝ์ฐ ์ฐจ์ด๋ @Component๋ก ๋ฑ๋กํ ๊ฒฝ์ฐ์๋ 10๋จ๊ณ(ApplicationContext Refresh) ๊ณผ์ ์์ ๋ฆฌ์ค๋๊ฐ ์์ฑ๋๋ฏ๋ก 10๋จ๊ณ ๋ถํฐ ์ดํ์ ๋ฐ์๋๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค.
๋ฐ๋ฉด์ addListenerํน์ META-INF/spring.factories๋ฅผ ํตํด ApplicationListener๋ฅผ ๋ฑ๋กํ ๊ฒฝ์ฐ์๋ ์ ํ๋ฆฌ์ผ์ด์ Bootstraping ์ด๊ธฐ๋ถํฐ ๋ฑ๋ก๋์ด ์ฌ์ฉ๋๋ฏ๋ก ๋ชจ๋ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ ์ ์๋ค๋ ์ฐจ์ด๊ฐ ์๋ค.
์ฌ์ฉ์ ์ ์ ์ด๋ฒคํธ ์ฌ์ฉํ๊ธฐ
Application Bootstraping ๊ณผ์ ์์ ๋ฐ์ํ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ธ์ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ๋ ํ ์งํ๋๋ ๋ฐํ์์ ์ฌ์ฉ์๊ฐ ์ง์ ์ ์ํ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ์ฌ ์ฒ๋ฆฌํ ์ ์๋ค.
์ด๋ฒคํธ ํด๋์ค ์ ์ (TaskCompletionEvent)
@Getter
public class TaskCompletionEvent {
private final Long taskId;
private final String userEmail;
public TaskCompletionEvent( Long taskId, String userEmail ) {
this.taskId = taskId;
this.userEmail = userEmail;
}
}JavaTask ์์ ์ด ์๋ฃ๋์์ ๋ ๋ฐํํ๊ธฐ ์ํ ์ด๋ฒคํธ๋ฅผ ์ ์ํ๋ค.
์ด๋ฒคํธ ๋ฐํ (TaskService)
Task ์์ ์ ์ํ ํ ์๋ฃ๋๋ฉด ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ค.
@Service
public class TaskService {
// publisher์๋ ๋ณดํต ApplicationContext ๊ตฌํ์ฒด๊ฐ ์ฃผ์
๋ฉ๋๋ค.
// ApplicationContext ์ธํฐํ์ด์ค๋ ApplicationEventPublisher๋ฅผ ํ์ฅํฉ๋๋ค.
private final ApplicationEventPublisher publisher;
public TaskService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void goTask(Long orderId, String email) {
// task ์ฒ๋ฆฌ ๋ก์ง
// task ์ฒ๋ฆฌ๊ฐ ์๋ฃ ๋๋ฉด TaskCompletionEvent ๋ฐํ
publisher.publishEvent(new TaskCompletionEvent(orderId, email));
}
}Javapublisher.publishEvent ํธ์ถ๋ก ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฒ๋ฆฌ๊ฐ ์ผ์ด๋๋ค.
- ์ด๋ฒคํธ๊ฐ ๋ฐํ๋๋ฉด ApplicationEventMulticaster๊ฐ ๋ฑ๋ก๋ ApplicationListener ๋ชฉ๋ก์์ ์ด๋ฒคํธ ํ์ (์ฌ๊ธฐ์๋ TaskCompletionEvent)์ ๋ง๋ ๋ฆฌ์ค๋๋ฅผ ์ฐพ๋๋ค. @EventListener๋ก ๋ฆฌ์ค๋๋ฅผ ์ ์ํ๋ค๋ฉด ApplicationListenerMethodAdapter ํ์ ์ ๋ฆฌ์ค๋์ผ ๊ฒ์ด๋ค.
- ๋งค์นญ๋ ๋ฆฌ์ค๋(ApplicationListenerMethodAdapter) ํธ์ถ์ ํตํด์ ๋ฆฌํ๋ ์ ์ผ๋ก TaskCompletionEvent๋ฅผ ์ ๋ฌํ๋ฉด์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์ด๋ฒคํธ ๋ฆฌ์ค๋ (TaskCompletionEventListener)
@Component
public class TaskEventListener {
// TaskCompletionEvent ๋ฐ์์ ํธ์ถ๋ฉ๋๋ค.
@EventListener
public void onTaskCompleted(TaskCompletionEvent event) {
//task ์๋ฃ ํ ์ฒ๋ฆฌ
// ๋๊ธฐ ์ฒ๋ฆฌ (๊ธฐ๋ณธ)
// ์: ์ด๋ฉ์ผ ํ ์ ์ฌ, ๊ฐ์ฌ ๋ก๊ทธ ๊ธฐ๋ก ๋ฑ
}
}Java@EventListener ๋ฉ์๋์์ ์ด๋ฒคํธ ๋ฐํ์ ํตํด์ ์ ๋ฌ๋ TaskCompletionEvent๋ฅผ ์ ๋ฌ ๋ฐ์ ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
์ง๊ธ๊น์ง Bean ์์ฑ์ ์ด๊ธฐํ์ Spring Boot Event System์ ํตํด์ @EventListener ์ด๋
ธํ
์ด์
๋ฉ์๋๊ฐ ์ด๋ค ํ๋ฆ์ ํตํด์ ํธ์ถ์ด ๋๋์ง์ ๋ํด์ ์ ๋ฆฌํด ๋ณด์๋ค.
Spring Boot ์ ํ๋ฆฌ์ผ์ด์
์ ๊ธฐ๋ํ๊ธฐ ์ํด์๋ ๋จ ํ์ค์ ์ฝ๋๋ฉด ๋์ง๋ง ๊ทธ ํ์ค์ ์ฝ๋์์๋ ์์ฒญ๋๊ฒ ๋ง์ ์ผ๋ค์ด ์ผ์ด๋๊ณ ์์์ ์ ์ ์์๋ค. Spring Boot Application์ด ์ด๋ค Bootstrap ๊ณผ์ ์ ๊ฑฐ์น๋์ง๋ฅผ ์๊ณ ์์ผ๋ฉด ๊ธฐ๋ ์์ ์ ์ด๋ค ํน์ ์์
์ ํด์ผ ํ๊ฑฐ๋ ์ผ๋ฐ์ ์ด์ง ์์ ์ค์ ์ฒ๋ฆฌ ์์
์ ํด์ผ ํ๋ ๊ฒฝ์ฐ์ ์ ์ฐํ๊ฒ ๋์ฒํ ์ ์๋ค.
๋จผ์ ๋ณด๋ฉด ์ข์ ํฌ์คํ
Spring Boot ์์ โ SpringApplication ์์ฑ๊ณผ ์๋ ๊ฐ์ง
Spring Boot โ Application Bootstraping์ ์ํ 13๋จ๊ณ
