티스토리 뷰

개요

  • Slack은 아주 편리한 엔터프라이즈 협업 도구이다. 애플리케이션에서도 Slack을 이용하면 장애와 같은 중요한 상황에서 적절한 메시지를 특정 채널에 전송할 수 있다. 이번 글에서는 Slack 연동을 통한 메시지 전송 방법을 소개하고자 한다.

Slack 채널 생성 및 WebHook URL 획득

  • Slack에 메시지를 전송하기 위해서는 먼저 채널 생성과 WebHook URL 획득이 선행되어야 한다. 방법은 아래와 같다.
Slack 로그인 → Administration → [Manage Apps] 클릭 → Search App Directory: Incoming WebHooks → [Add to Slack] 클릭
→ (채널을 이미 만들었을 경우) Choose a channel: 생성한 채널 선택
→ (채널을 만들지 않았을 경우) [create a new channel] 클릭 → Name: some-api-error → [Create] 클릭
→ [Add Incoming WebHooks integration] 클릭
→ 생성된 WebHook URL을 복사 (ex: https://hooks.slack.com/services/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)
→ [Save Settings] 클릭

환경 설정 추가

  • 프로젝트의 /src/resources/application.yml 파일에 아래 내용을 추가한다. 앞서 획득한 WebHookURL을 연결하는 작업으로 다양한 프로파일에 대한 복수개의 WebHookURL을 설정할 수 있는 장점이 있다.
slack:
  webhook-url:
    info: https://hooks.slack.com/services/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
    warn: https://hooks.slack.com/services/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
    error: https://hooks.slack.com/services/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

Slack SDK 연동

  • 프로젝트의 /build.gradle 파일에 아래 내용을 추가한다.
dependencies {
    compile group: 'com.slack.api', name: 'slack-api-client', version: '1.0.11'
    compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.7.2'
}
  • Slack SDK는 내부적으로 HTTP 요청시 OkHttp를 사용하여 통신하여 라이브러리 추가가 필요하다.

Slack 메시지 송신 레벨 정의

  • 다양한 상황에 대한 메시지를 전송하기 위해 메시지 송신 레벨을 아래와 같이 정의한다.
enum class SlackMessageLevel {

    INFO,
    WARN,
    ERROR
}

Slack 메시지 전송 서비스 작성

  • 이제 메시지 서비스를 작성할 차례이다. 먼저 인터페이스이다.
interface SlackService {

    fun sendMessage(fieldMap: Map<String, String>, messageLevel: SlackMessageLevel = SlackMessageLevel.INFO): Future<Boolean>
}
  • 다음은 인터페이스에 대한 구현체를 작성할 차례이다.
@Service
@Async("taskExecutor")
class SlackServiceImpl : SlackService {

    @Value("\${slack.webhook-url.info}")
    private lateinit var SLACK_WEBHOOK_URL_INFO: String

    @Value("\${slack.webhook-url.warn}")
    private lateinit var SLACK_WEBHOOK_URL_WARN: String

    @Value("\${slack.webhook-url.error}")
    private lateinit var SLACK_WEBHOOK_URL_ERROR: String

    override fun sendMessage(fieldMap: Map<String, String>, messageLevel: SlackMessageLevel): Future<Boolean> {

        val slack: Slack = Slack.getInstance()
        val webHookUrl = when (messageLevel) {
            SlackMessageLevel.INFO -> SLACK_WEBHOOK_URL_INFO
            SlackMessageLevel.WARN -> SLACK_WEBHOOK_URL_WARN
            SlackMessageLevel.ERROR -> SLACK_WEBHOOK_URL_ERROR
        }
        val text = StringBuilder().apply {
            fieldMap.forEach {
                if (it.value != null) {
                    append("*${it.key}*")
                    append("\n    ${it.value}\n\n")
                }
            }
        }.toString()
        val payload = Payload.builder().text(text).build()
        val response = slack.send(webHookUrl, payload)

        return AsyncResult(response.code == 200)
    }

Slack 메시지 전송 예

  • 이제 Slack 채널에 메시지를 전송할 차례이다. 사용 예는 아래와 같다.
slackService.sendMessage(MDC.getCopyOfContextMap(), SlackMessageLevel.ERROR)

Spring Boot 애플리케이션 이벤트 연동

  • 위 제작된 코드를 응용하면 아래와 같이 Spring Boot 애플리케이션의 시작 시점에도 메시지를 전송할 수 있다.
@Component
class SlackEventListener(

        val slackService: SlackService
) {
    @EventListener(ApplicationReadyEvent::class)
    fun afterApplicationReady() {

        MDC.clear()
        MDC.put("message", "APPLICATION_STARTED")
        MDC.put("environment", CommonUtil.getCurrentEnvironmentName())
        MDC.put("application", "some-api")
        MDC.put("ip_address", InetAddress.getLocalHost().hostAddress)

        slackService.sendMessage(MDC.getCopyOfContextMap(), SlackMessageLevel.INFO)
    }

    @EventListener(ApplicationFailedEvent::class)
    fun afterApplicationFailed() {

        MDC.clear()
        MDC.put("message", "APPLICATION_STARTUP_FAILED")
        MDC.put("environment", CommonUtil.getCurrentEnvironmentName())
        MDC.put("application", "some-api")
        MDC.put("ip_address", InetAddress.getLocalHost().hostAddress)

        slackService.sendMessage(MDC.getCopyOfContextMap(), SlackMessageLevel.ERROR)
    }
}

참고 글

댓글
댓글쓰기 폼