티스토리 뷰
대부분 Spring 을 이용한 프로그래밍을 할 때에는 MVC, 요즘에는 WebFlux 을 많이 이용할 것이기 때문에 Spring Boot Starter 에서 제공하는 기능만으로도 시작 시 추가적인 동작이 필요하지 않는 경우가 대부분입니다. 하지만, 특정한 동작을 위해 서버 시작 시 어떠한 명령을 실행해야 한다던가, 반대로 종료 시 어떤 동작을 해야하는 경우는 분명 생길 수 있습니다.
가장 쉽게 생각해볼 수 있는 것은 생성자와 소멸자입니다. 그리고 jar 파일을 직접 실행할 때에는 main() method 내에 추가로 구현할 수도 있습니다. 하지만, Spring Boot 에서는 몇 가지 다른 방식으로 처리할 수 있습니다.
먼저 CommandLineRunner 나 ApplicationRunner 인터페이스를 이용해서 애플리케이션 시작 시점에 무엇인가를 실행하는 방법입니다. Spring Boot 가 시작할 때 CommandLineRunner 나 ApplicationRunner 에서 반드시 구현되어야 하는 run() method 을 작성함으로써 시작 시 특정한 기능을 실행할 수 있습니다.
이 두 Runner 을 이용하면 static 으로 되어 있는 main() 와 달리 일반 method 이기 때문에 static 으로 된 외부 값을 이용하지 않아도 된다는 점과, main() 이 있는 Class 가 아닌 @Component 로 등록되는 어떤 Class 에도 등록할 수 있기 때문에 필요에 따라 구현할 위치를 정할 수 있다는 것입니다.
@Component public class StartConfig implements CommandLineRunner { @Override public void run(String... args) { // 구현 부분 } }
@Component public class StartConfig implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 구현 부분 } }
위와 같이 간단하게 구현할 수 있습니다. 다만, 두 Runner 는 run() method 의 매개변수로 받을 수 있는 변수가 다른데, 이는 main() 이 받는 명령줄 매개변수를 그대로 전달받게 됩니다. 그래서 main() 와 형식이 유사한 CommandLineRunner 가 더 자주 쓰일 것으로 예상됩니다(저 역시 CommandLineRunner 만 쓰고 있습니다). 반대로 ApplicationRunner 은 옵션을 key 와 value 로 나누어 Collection 객체로 전달받을 수 있기 때문에 처리가 좀 더 간결해질 수 있는 부분도 존재합니다.
반대로 애플리케이션이 종료할 때에는 ApplicationListener 인터페이스를 이용해 Context 가 종료되는 event 을 전달받을 수 있습니다. ApplicationListener 은 ApplicationListener<E extends ApplicationEvent> 형태로 되어 있기 때문에 ApplicationEvent 을 상속받아 구현된 몇 가지 이벤트를 받을 수 있고(그렇게에 사실 위의 Runner 대신 이걸 이용해도 됨) Context 가 종료되는 시점의 이벤트도 받을 수 있습니다.
@Component public class ShutdownEventListener implements ApplicationListener{ @Override public void onApplicationEvent(ContextClosedEvent event) { // 구현 부분 } }
당연히 이를 이용해서 상태에 따른 이벤트를 구현할 수 있습니다. 종료 시 Gracefully 하게(우아하게) 종료하기 위한 구현 부분을 넣을 수 있습니다.
또다른 방법으로, Bean 의 생성과 소멸 시점에 호출되는 method 을 이용하는 방법도 존재합니다. 바로 InitializingBean, DisposableBean 인터페이스입니다. 아래의 예제만 보더라도 객체의 생성과 소멸 시점에 동작하는 method 을 구현하는 것을 알 수 있습니다. 대신에, 객체의 생성과 소멸이 계속 반복되면 여러번 실행하기 때문에 개인적으로는 @Configuraion 을 구현하는 곳과 같이 한 번 설정하고 계속 사용되는 곳에 implements 하는 것을 권합니다.
@Configuration public class StartStopConfig implementsInitializingBean, DisposableBean { @Override public void afterPropertiesSet() throws Exception { // Bean 생성 시 동작할 코드 구현 부분 } @Override public void destroy() throws Exception { // Bean 소멸 시 동작할 코드 구현 부분 } }
이러한 기능을 이용할 때 주의할 것이 있습니다. 실행 시 시작되는 것에는 크게 실수할 부분은 없으나, 애플리케이션 종료 시 실행되어야 하는 코드는 일반적으로 SIGTERM 입니다. 일반적으로 kill 명령을 실행하면 발생하는 signal 이고, 이 때 ContextClosedEvent 을 감지하거나 DisposableBean 의 destory() 을 실행하는데 문제가 없습니다. 하지만, kill -9 와 같이 SIGKILL 을 발생시키면 애플리케이션이 signal 을 감지하지 못하기 때문에 종료 시 필요한 처리를 하지 못합니다.
문제는, IDE 에서 재시작 같은 동작을 할 때 SIGTERM 이 아닌 SIGKILL 로 종료할 수도 있다는 것입니다.
아래 화면은 IntelliJ 인데, 수정 후 재시작을 하려고 할 때 많이 사용하는 것은 아래 표시된 Rerun 버튼일 것입니다. 그런데, 이 버튼을 누르면 SIGKILL 로 애플리케이션을 종료시킵니다.
SIGTERM 으로 종료하고 싶다면, 아래의 Exit 버튼을 눌러서 애플리케이션을 종료시키고, 종료가 완료된 뒤 시작을 해야 합니다.
실제로 Spring Batch 등에서 Rerun 을 실행할 경우 Job 의 상태 변화가 완벽히 이루어지지 않아서 문제가 발생하기도 합니다. 그렇기 때문에 이 부분을 항상 숙지하고 있어야 합니다.
'Programming > Spring Boot 시작하기' 카테고리의 다른 글
Spring Boot 에서 messages.properties 을 이용한 다국어 처리 (2) (0) | 2019.01.28 |
---|---|
Spring Boot 에서 messages.properties 을 이용한 다국어 처리 (1) (0) | 2019.01.28 |
외부 resource 을 서비스하기 (3) | 2019.01.24 |
RestTemplate 설정 변경하기 (0) | 2019.01.23 |
Jackson MessageConverter 을 상속받아 수정하기 (0) | 2019.01.22 |
- Total
- Today
- Yesterday
- Spring MVC
- 엘지
- 시니어 프로그래머
- git
- couchbase
- jooq
- NoSQL
- 프로젝트 규모
- paging
- KDE
- 페이징
- OracleJDK
- RestTemplate
- java config
- proxmox
- Nas
- Redmine
- SI
- manjaro
- Spring
- boot
- messages.properties
- 내장 WAS
- Spring Boot
- 도입기
- docker
- 워드프레스
- 외장 WAS
- 클라우드플레어
- Phabricator
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |