과거부터 개발하면서 꿈이 있었다면, 에러가 발생하더라도 서버에서 로그를 확인하지 않고도 바로 알도록 만드는 것이었다.
이런 시스템을 만들어두면 에러가 발생하자마자 바로바로 조치가 가능하기 때문이었다.
웹훅으로 만드려고 했으며, 가장 쉬운 슬랙을 사용했다.
우선 슬랙에서 다음과 같은 앱을 추가하자
이 Incoming Webhooks를 사용해서 채널에 알림을 보낼것이다.
여기서 webhook url을 생성하고, 이 과정은 생략하도록 하겠다.
에러를 보고하는 서비스의 추상화를 만든다.
interface ErrorReporter {
fun reportError(content: String, localDateTime: LocalDateTime)
}
현재는 슬랙으로 하고 있지만, 구현체가 변경될 수도 있으니 추상화로 만들어둔다.
@Service
class SlackErrorReporter(
@Value("\${slack.webhook_url.error}")
private val slackWebHookUrl: String
): ErrorReporter {
override fun reportError(content: String, localDateTime: LocalDateTime) {
val payload = mapOf("text" to "${localDateTime}에 $content")
WebClient.create()
.post().uri(slackWebHookUrl)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(payload)
.retrieve()
.bodyToMono(Void::class.java)
.block()
}
}
당연히 webhook의 주소는 깃에 노출되면 안되고 변경이 될 수 있기에 환경변수로 주입받는다.
http로 요청을 해야하기에 WebClient를 사용했다.
안에 들어가는 json 형식은 다음과 같다.
{
"text": "에러 내용"
}
그렇기에 나는 여기에 시간과 에러의 내용을 보고하도록 만들었다.
그리고 이제 이 reporter를 사용해보자.
당연히 전역의 ExceptionHandler를 사용했다.
현재 멀티모듈을 사용하기에 container 역할을 하는 container 모듈에 다음과 같은 ExceptionHandler를 추가했다.
@ControllerAdvice
class GlobalExceptionHandler(
private val errorReporter: ErrorReporter
) {
@ExceptionHandler(Exception::class)
fun handleException(e: Exception): ResponseEntity<ErrorResponse> {
val sw = StringWriter()
e.printStackTrace(PrintWriter(sw))
errorReporter.reportError(sw.toString(), LocalDateTime.now())
return ResponseEntity.internalServerError().build()
}
}
errorReporter를 주입받고, 해당 에러에 대한 stackTrace를 errorReporter로 넘기게 된다.
클라이언트에게는 500에러를 그대로 반환한다.
이러면 서버에서 해결하지 못했던 모든 500에러가 슬랙을 통해 개발자에게 보고된다.
테스트를 해보도록 하자.
그냥 다짜고짜 컨트롤러에서 에러를 던져보았다.
throw IllegalStateException()
컨트롤러는 해당 에러를 처리할 수 없기에 클라이언트로 넘기려고 할거고, 그러면 전역 핸들러에 걸리게 될 것이다.
일단 Swagger에는 500에러가 나오게 된다.
그리고 슬랙에서도 컨트롤러에서 IllegalStateException이 발생했다고 알림이 오는 것을 볼 수 있다.
'토이 프로젝트' 카테고리의 다른 글
스프링의 AOP @Transactional 알아보기 (0) | 2025.04.11 |
---|---|
트랜잭션 격리 수준(Transaction Isolation Level) (0) | 2025.03.26 |
메모리 단편화, 객체 생성 시간을 줄이기 위한 ObjectPool 적용 (1) | 2025.02.11 |
Stomp에서 Jwt Filter 구현하기 (1) | 2025.01.23 |
Hexagonal Architecture 적용하기 (1) | 2025.01.22 |