스프링 부트, 카프카, 슬랙 연동 - 실시간 에러 알림 시스템 구축
📌 서론
이전 글에서 로컬 환경에서 도커로 Kafka를 실행하고, 스프링 부트에서 카프카로 메시지를 전송했다.(producer) 이제 Kafka에서 특정 토픽으로 메시지가 들어왔을 때 해당 메시지를 슬랙에 알림으로 전송하는 프로세스만 남았다. (consumer)
이번 글에서 슬랙과 스프링 부트를 연동하고 스프링 부트에서 에러가 발생하면 해당 에러가 슬랙으로 잘 전달이 되는지 확인하자.
이전 글에서 이미 kafka를 실행시켜줬고, kafka producer까지는 완료된 상황이다. 만약 이 두 개 중 하나라도 안되어있다면 해당 글을 읽고 오는 걸 권장한다.
🔻 docker-compose로 kafka 실행 🔻
macOS에서 docker-compose로 Kafka 설치하기 (Zookeeper 사용 X)
🔻 스프링 부트 프로젝트에서 에러 발생했을때 kafka로 메시지 전송(producer) 🔻
스프링 부트에서 Kafka로 메시지 전송하기: 실시간 에러 로그 처리
Slack 설정
Slack에 채널 추가
카프카의 에러 메시지를 받을 전용 채널을 만들어준다.
나는 member-prod로 만들어줬다.
Incoming Webhook 앱 추가
채널명(member-prod)를 클릭하면 다음과 같은 팝업이 뜬다.
여기서 '앱 추가'를 클릭한다.
Incoming Webhook을 검색하고 설치해준다.
Webhook의 개념은 웹 개발에서 자주 사용되는데, 특정 이벤트가 발생했을 때 미리 정의된 URL로 HTTP POST 요청을 자동으로 보내는 방식이다. 즉, 이벤트 발생 시 특정 작업을 자동으로 트리거할 수 있는 메커니즘이다.
예를 들어, GitHub에서 코드 푸시 이벤트가 발생할 때마다 빌드 시스템에 알리거나, 결제 시스템에서 결제 완료 이벤트가 발생했을 때 주문 처리 시스템에 신호를 보내는 등의 용도로 활용된다.
슬랙의 Incoming Webhook은 슬랙 채널에 외부 시스템에서 메시지를 보내기 위해 사용하는 간단하면서도 강력한 기능이다. 이를 통해 다양한 애플리케이션, 시스템, 서버 등이 슬랙 채널에 직접 알림, 데이터, 정보 등을 전송할 수 있다.
예를 들어, 서버에 문제가 발생했을 때 자동으로 슬랙 채널에 경고를 보내거나, 새로운 사용자가 웹사이트에 가입했을 때 그 정보를 공유하는 등의 용도로 사용된다.
웹 브라우저로 넘어오면 'Slack에 추가'를 클릭한다.
그럼 어떤 채널에 이 웹훅을 연동할 건지 선택하는 화면이 나온다. 여기서 아까 생성한 채널(member-prod)을 선택해 준다.
그 뒤에 '수신 웹후크 통합 앱 추가'를 클릭하면 이동된 페이지에서 새로 생성된 웹후크 URL이 나온다.
Webhook URL 테스트
위 페이지에서 스크롤을 더 내리다 보면 웹후크 URL을 테스트할 수 있는 예시를 준다.
여기서 빨간 박스를 친 #my-channel-here라는 문구는 아까 생성해 준 채널명으로 바꿔서 작성해줘야 한다.
나는 다음 명령어로 수정하면 된다.
curl -X POST --data-urlencode "payload={\"channel\": \"#member-prod\", \"username\": \"webhookbot\", \"text\": \"이 항목은 #개의 my-channel-here에 포스트되며 webhookbot이라는 봇에서 제공됩니다.\", \"icon_emoji\": \":ghost:\"}" https://hooks.slack.com/services/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
새로운 터미널을 열어서 위 명령어를 입력하고 ok%라고 나오면 성공한 거다! 이제 슬랙을 확인해 보자.
해당 채널을 보면 연동이 잘 된 걸 확인할 수 있다.
스프링 부트와 슬랙 연동 (consumer 코드 추가)
이제 스프링 부트에서 슬랙으로 메시지를 전송하는 코드를 추가하자.
엄밀히 말하면 에러가 발생할 때 스프링 부트에서 kafka의 error-messages 토픽으로 메시지를 전송하는 코드(producer)는 이미 존재한다. 새롭게 개발해야 할 부분은 error-messages 토픽으로 전송된 메시지가 있다면 이 메시지를 받아서 슬랙으로 알림을 보내는 코드(consumer)를 추가하면 된다.
스프링 부트에 Slack Webhook 연동
슬랙 웹후크 URL을 스프링 부트 환경 변수에 추가해 주자.
환경 변수로 슬랙 웹후크 URL을 관리하는 것은 선택사항이지만, 여러 이유로 권장된다.
첫째, 보안상의 이유로, 웹후크 URL과 같은 민감한 정보를 소스 코드에 직접 하드코딩하는 것은 위험할 수 있다. 환경 변수를 사용하면 이 정보를 안전하게 보호하면서도 필요할 때 쉽게 접근할 수 있다.
둘째, 환경에 따른 유연성을 제공한다. 개발, 테스트, 운영 등 다른 환경에서 다른 웹후크 URL을 사용할 수 있으며, 환경 변수를 통해 쉽게 관리할 수 있다.
applicatoin.yml
applicatoin.yml에 Wehbook URL을 추가해 준다.
RestTemplate 빈 등록
이제 Rest API 호출을 위한 RestTemplate을 빈으로 등록하자.
이 부분도 선택 사항이다.
RestTemplate 인스턴스를 매번 새로 생성하는 대신 빈으로 등록하여 사용하는 것은 효율성과 성능 최적화 측면에서 유리하다.
빈으로 등록함으로써, 스프링 컨테이너가 관리하는 싱글톤 객체를 사용할 수 있게 되어, 애플리케이션 전반에서 RestTemplate 인스턴스를 재사용할 수 있다. 이는 객체 생성 비용을 줄이고, GC(Garbage Collection) 부담을 감소시킨다. 또한, RestTemplate의 구성(예: 타임아웃 설정, 인터셉터 추가 등)을 중앙에서 관리할 수 있어, 애플리케이션의 유지보수성을 향상시킨다.
/**
* REST API 호출을 위한 템플릿 메서드를 제공
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Kafka Consumer 코드 추가
Kafka 메시지 수신 클래스 생성
KafkaSlackListener 클래스는 Kafka의 'error-messages' 토픽으로부터 메시지를 수신하는 리스너 역할을 한다.
@Slf4j
@Component
@RequiredArgsConstructor
public class KafkaSlackListener {
private final SlackNotificationService slackNotificationService;
@KafkaListener(topics = "${spring.kafka.template.default-topic}", groupId ="${spring.kafka.consumer.group-id}")
public void listen(String message) {
// 카프카 메시지를 받았을 때 처리
log.info("Received message in group error-handler-group: " + message);
// 메시지를 Slack으로 전송
slackNotificationService.sendMessageToSlack (message) ;
}
}
@KafkaListener
@KafkaListener는 스프링 카프카의 어노테이션으로, 메서드를 Kafka 메시지 리스너로 지정한다. 이 메서드는 지정된 토픽으로부터 메시지를 수신할 때 호출된다.
topics
topics는 이 리스너가 구독할 Kafka 토픽을 지정한다. 여기서는 application.yml 파일이나 다른 외부 설정 소스에서 정의된 spring.kafka.template.default-topic 속성의 값을 사용하여 토픽 이름을 동적으로 설정한다.
groupId
groupId는 이 리스너가 속할 컨슈머 그룹의 ID를 지정한다. 컨슈머 그룹은 Kafka에서 메시지를 효율적으로 분산 처리하기 위해 사용된다. 이 값 역시 외부 설정을 통해 지정된다.
listen()
메서드는 Kafka 토픽으로부터 메시지를 수신할 때마다 호출된다. message 파라미터는 수신된 Kafka 메시지의 내용이다.
슬랙으로 메시지 전송 서비스 클래스 생성
SlackNotificationService 클래스는 슬랙으로 에러 메시지를 전송하는 책임을 진다. 이 클래스는 슬랙 웹후크 URL을 사용하여 HTTP POST 요청을 보내는 방식으로 메시지를 전송한다.
이 클래스는 어떤 특정 에러나 이벤트에 국한되지 않고, 다양한 메시지를 슬랙으로 전송할 수 있는 유연성을 보장한다.
/**
* Slack 어플에 알림을 전송하는 서비스 클래스
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SlackNotificationService {
@Value("${slack.web-hook-url}")
private String slackWebHookUrl;
private final RestTemplate restTemplate;
/**
* slack에 에러 메시지를 전송한다.
*/
public void sendMessageToSlack(String message) {
String payload = "{\"text\": \"" + message + "\"}";
restTemplate.postForEntity(slackWebHookUrl, payload, String.class);
log.info("slack에 에러 메시지 발행 완료") ;
}
}
@Value("${slack.web-hook-url}")
스프링의 @Value 어노테이션을 사용하여, application.yml 파일 등의 외부 설정 파일에서 slack.web-hook-url에 해당하는 값을 읽어와 slackWebHookUrl 필드에 할당한다. 이 값은 슬랙 웹후크 URL을 나타낸다.
RestTemplate
RestTemplate 객체를 사용하여 HTTP 요청을 보낸다.
payload
String payload는 슬랙 API에 전송할 JSON 형태의 페이로드를 문자열로 생성한다. 이 페이로드는 전송할 메시지를 담고 있다. 슬랙 웹후크 API는 JSON 형태의 페이로드를 요구하며, 여기서 "text" 키에 해당하는 값으로 실제 전송할 메시지 내용을 지정한다.
postForEntity()
RestTemplate의 postForEntity 메서드를 사용하여, 슬랙 웹후크 URL로 HTTP POST 요청을 보낸다. 첫 번째 인자는 요청을 보낼 URL, 두 번째 인자는 요청 본문(body), 세 번째 인자는 응답 본문을 매핑할 자바 타입이다. 이 경우, 응답 본문을 String 타입으로 받는다는 의미이다.
이렇게 설정하고 스프링 부트를 실행하고 일부러 에러를 발생시켜 보자!
에러 발생 시 Slack 알림
스프링 부트에서 IllegalArgumentException을 던져보자.
throw new NullPointerException("null null!!");
그럼 슬랙으로 에러 메시지가 잘 오는 걸 확인할 수 있다!
🔥 결론
이 글을 통해, 우리는 로컬 환경에서 Docker를 사용하여 Kafka를 설정하는 것부터 시작하여, 스프링 부트 애플리케이션에서 발생한 에러 메시지를 Kafka를 통해 전송하는 프로듀서 코드를 성공적으로 구현했다. 더 나아가, Kafka의 'error-messages' 토픽으로부터 메시지를 수신하여 슬랙으로 알림을 전송하는 컨슈머 코드까지 추가하는 과정을 완료했다.
이러한 과정에서 Kafka를 메시지 브로커로 사용하여 시스템 간 메시지를 비동기적으로 전달하는 방법을 학습했으며, 슬랙 API와의 연동을 통해 실시간 에러 모니터링 시스템을 구축하는 방법에 대해 알아보았다.
아직 완벽하게 설계한 게 아니기 때문에 에러 메시지의 형식과 내용에 대해 더 보완하고 문제 해결 과정에서 필요한 정확한 정보를 빠르게 파악할 수 있는 방법을 더 찾아봐야겠다.
사이드 팀원인 "개발자의 서랍"님의 블로그도 한번 방문해 보세요! 좋은 글이 많이 있습니다 :)
'Tools > Kafka (카프카)' 카테고리의 다른 글
스프링 부트에서 Kafka로 메시지 전송하기: 실시간 에러 로그 처리 (1) | 2024.02.19 |
---|---|
macOS에서 docker-compose로 Kafka 설치하기 (Zookeeper 사용 X) (1) | 2024.02.17 |