개요
실시간 서비스를 제공하다보면 서버가 죽는다던가 특정 동작에서 오류가 발생하는 등 많은 문제 상황이 발생한다. 실시간 서비스에서 특히 사용자가 많이 이용하는 기능에 문제가 발생하면 사용자의 불편 뿐만 아니라 매출 하락으로도 이어질 수 있다. 이런 문제를 사전에 탐지할 수 있다면(테스트 코드와 같은 미연에 방지할 수 있는 방법들은 있다!) 좋겠지만 그렇지 않은 경우에는 개발자가 빠르게 대응을 할 수 있어야 한다.
개발자가 대응을 하기 위해선 문제 사항이 개발자에게 전달이 되어야 한다. 가장 원초적인 방법으로는 시시때때로 로그를 확인하는 방법이다. 그러나 이 방법에는 다음과 같은 제약 사항을 가진다.
1. 개발자가 직접 서버로 접속할 수 있어야 한다.(컴퓨터가 없다면 접근이 힘들다!)
2. 언제 어디서 버그가 날지 모른다.(만약 내가 확인하고 난 뒤 바로 오류가 생긴다면?)
어떻게 해결하지?
위와같은 문제가 있을 때 우리의 핵심 문제는 다음과 같이 정의할 수 있다.
컴퓨터가 없어도 어디서 버그가 났는지 쉽게 확인할 수 있어야 한다!
첫 번째로 컴퓨터가 없어도 어디서나 접근할 수 있어야 한다는 문제를 풀어보자. 우리는 데스크탑과 같은 컴퓨터가 없어도 손에 작은 컴퓨터를 가지고 있다. 바로 스마트폰이다. 스마트폰을 이용해서 카카오톡이나 문자 메세지와 같은 통신 수단을 이용해 다른 사용자와 소통한다. 그러면 이걸 사람과 사람끼리의 소통이 아닌 컴퓨터와 사람과의 소통으로 치환한다면 우리는 컴퓨터가 하는 말을 들을 수 있다.(아주 미친 소리같아보인다) 여기서 우리는 다양한 메신저 프로그램을 이용할 수 있는데 그중 디스코드를 선택했다. 디스코드를 선택한 이유는 현재 컴퓨터 환경에서 추가적인 설정없이 디스코드로 바로 접근이 가능하기에 디스코드를 사용했다.
두 번째로 어디서 버그가 났는지 확인할 수 있어야 한다는 문제인데 이건 정말로 간단하다. 간단하게 우리가 특정 부분에서 로그를 찍듯이 에러가 나면 디스코드를 통해 에러 메세지를 전송하면 된다. 구조는 아래와 같다.
구현 방법
구현 방법은 다음과 같다.
먼저 디스코드에 사용되는 알림 기능을 인터페이스로 정의한다.
package limecoding.asmrstreamingservice.service.alert;
public interface AlertService {
void sendAlert(String message);
}
메세지 DTO
package limecoding.asmrstreamingservice.dto.alert;
import lombok.*;
@Setter
@Getter
@ToString
@AllArgsConstructor
public class DiscordWebHookMessage {
private String content;
}
다음으로 구현체를 2개를 만든다.
1. discord 알림 서비스
package limecoding.asmrstreamingservice.service.alert.impl;
import limecoding.asmrstreamingservice.dto.alert.DiscordWebHookMessage;
import limecoding.asmrstreamingservice.service.alert.AlertService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@RequiredArgsConstructor
@Slf4j
@Service
@ConditionalOnProperty(name = "alert.discord.enabled", havingValue = "true")
public class DiscordAlertService implements AlertService {
@Value("${alert.discord.url}")
private String discordUrl;
private final RestTemplate restTemplate;
@Override
public void sendAlert(String message) {
DiscordWebHookMessage payload = new DiscordWebHookMessage(message);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<DiscordWebHookMessage> request = new HttpEntity<>(payload, headers);
try {
restTemplate.postForEntity(discordUrl, request, String.class);
} catch (Exception e) {
log.error("디스코드 전송 실패 : {}", e.getMessage());
}
}
}
2. 가짜 알림 서비스
package limecoding.asmrstreamingservice.service.alert.impl;
import limecoding.asmrstreamingservice.service.alert.AlertService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
@Service
@ConditionalOnProperty(name = "alert.discord.enabled", havingValue = "false")
public class NoAlertService implements AlertService {
@Override
public void sendAlert(String message) {
// Do not Send Message
}
}
이렇게 2개를 만든 이유는 알림 서비스를 on/off할 수 있도록 하기 위해서이다. application.properties에서 true, false를 놓고 if문을 이용해서 할 수도 있겠지만 보기도 지저분해지고 이로 인해 유지보수하기가 힘들다.(if문으로 조건보고 {}안에 코드 짜고 있으면 무슨 말인지 알 수 있다.) 이를 방지하고 위해 @ConditionalOnProperty를 사용했는데 이는 선택적으로 빈을 만들 때 사용한다.
이렇게 만들면 간단하게 디스코드 알림을 on/off할 수 있다. 다음 application.properties에 다음과 같이 설정한다.
#discord alert setting
alert.discord.url=<discord url>
#true/false
alert.discord.enabled=false
private final AlertService alertService;
public void test(){
alertService.sendAlert("알림 보내기")
}
다음 이런 식으로 사용하면 된다.
디스코드 webhook 문서
Discord Developer Portal — API Docs for Bots and Developers
Integrate your service with Discord — whether it's a bot or a game or whatever your wildest imagination can come up with.
discord.com