스프링 부트 애플리케이션을 개발하고 개발 환경 및 운영 환경에 배포를 할 때, 실행 실패 시 메신저나 메일로 알림을 보내는건 중요한 작업이다.
물론, AWS 를 사용한다면 헬스 체크를 사용해서 실패할 경우 재시도하는 방법이 있지만
중요한건 실행이 실패한 이유를 파악하고, 원인을 제거하는 것이다.
AWS 를 사용할 경우 구동 실패 알림은 보낼 수 있지만, 알림 내용에 원인은 포함시킬 수 없다.
따라서, 담당자가 알림을 받고 서버에 접속 후 로그를 통해 원인을 파악해야 한다.
물론 크게 불편함은 없겠지만, 알림 내용에 원인이 들어 있으면 좀 더 빠른 대응이 가능하지 않을까 싶다.
그래서 나는 스프링 부트 애플리케이션 실행 실패 시 원인이 들어있는 내용으로 알림을 보내고자 한다.
이를 위해선 스프링 부트 스타트업 이벤트에 대한 이해가 필요하다.
스프링 부트 애플리케이션을 실행하면, 스프링 부트가 각종 단계를 거쳐 최종적으로 요청을 받을 수 있는 상태가 되는데 이 과정들을 스프링 부트 스타트업 이라고 한다.
그리고 스프링 부트는 각 단계별로 이벤트를 발행하는데, 스프링 부트 내부적으로도 이 이벤트를 구독해서 처리하기도 하고 개발자가 별도로 이 이벤트를 구독해서 원하는 처리를 할 수 있다.
이벤트의 종류는 다음과 같다.
ApplicationStartingEvent
- 애플리케이션의 실행이 시작될 때 발생
- 이 시점에서는 애플리케이션의 컨텍스트가 아직 생성되지 않았기 때문에 빈(bean)에 접근할 수 없음
ApplicationEnvironmentPreparedEvent
- 애플리케이션의 실행 환경(예: 프로퍼티 소스, 프로파일 등)이 준비되었을 때 발생
ApplicationContextInitializedEvent
- 애플리케이션의 컨텍스트가 초기화되었지만 아직 리프레시되지 않았을 때 발생
ApplicationPreparedEvent
- 애플리케이션의 컨텍스트가 리프레시되기 직전에 발생
ApplicationStartedEvent
- 애플리케이션의 컨텍스트가 리프레시된 후에 발생
ApplicationReadyEvent
- 애플리케이션의 실행이 완료되고 사용자에게 서비스를 시작할 준비가 완료되었을 때 발생
ApplicationFailedEvent
- 애플리케이션 실행 중 예외가 발생하여 시작에 실패했을 때 발생
여기서 나에게 필요한 이벤트는 ApplicationFailedEvent 이다.
그럼 이제 해야할 일은 이벤트를 구독해서 알림을 전송하는 코드를 작성해야 한다.
스프링 부트에서 스타트업 이벤트를 구독하는 방법은 두 가지가 있다.
우선 다음 코드는 스프링 부트 스타트업 과정에서 의도적으로 예외가 발생하도록 하기 위한 코드이니 한번 보고 나서 진행하자.
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
throw new RuntimeException("데이터베이스 초기화 중 예외 발생!");
}
}
@Component
public class ApplicationFailedListener {
@EventListener
public void applicationFailedListener(ApplicationFailedEvent event) {
// 메신저 및 메일 알림 발송 코드 작성
System.out.println(event.getException().getMessage());
}
}
단순화를 위해 예외 메세지 출력만 했다.
애플리케이션을 실행하면
위와 같이 "Failed to execute CommandLineRunner" 라는 메세지를 얻을 수 있다.
이로써 CommandLineRunner 가 실패의 원인임을 밝혀냈다.
ApplicationListener 인터페이스를 구현한 구현체를 빈으로 등록하면 된다.
@Component
public class ApplicationFailedListener implements ApplicationListener<ApplicationFailedEvent> {
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
// 메신저 및 메일 알림 발송 코드 작성
System.out.println(event.getException().getMessage());
}
}
스프링 부트 실행 클레스인 SpringApplication 객체에 리스너를 추가하는 방법도 있다.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
springApplication.addListeners((ApplicationFailedEvent event) -> {
// 메신저 및 메일 알림 발송 코드 작성
System.out.println(event.getException().getMessage());
});
springApplication.run(args);
}
}
참고로 여러 리스너를 등록할 수도 있으니 잘 활용하면 좋을 것 같다.