Jenkins 빌드 실패 시 Discord로 알림 받기 - Webhook 5분 설정

English (N/A)

Jenkins 빌드가 실패했는데 나중에 확인하면 시간 낭비다. Discord Webhook을 사용하면 빌드 실패 즉시 알림 을 받을 수 있다.

설정은 5분이면 끝 난다. 플러그인 설치도 필요 없고, 그냥 curl로 Discord API 호출하면 된다.

왜 Discord Webhook인가

다른 방법들과 비교

방법장점단점
Discord Webhook설정 간단, 플러그인 불필요, 무료-
Jenkins 이메일기본 기능스팸함 가능성, 확인 느림
Slack Webhook업무용 좋음유료 플랜 필요 (무료는 90일 제한)
Jenkins Plugin기능 많음설치/설정 복잡

Discord Webhook의 장점:

  • 무료
  • 플러그인 설치 불필요
  • 5분 설정
  • 실시간 알림 (모바일 앱)
  • 예쁜 Embed 메시지

설정 방법

1단계: Discord Webhook 생성

1-1. Discord 채널 설정

알림을 받고 싶은 채널(예: #ci-cd, #alerts)에서:

  1. 채널 설정 (톱니바퀴 아이콘) 클릭
  2. 연동 탭 클릭
  3. 웹후크 클릭
  4. 새 웹후크 클릭

1-2. Webhook 설정

  • 이름: Jenkins Bot (원하는 이름)
  • 아바타: 원하는 이미지 (선택사항)

웹후크 URL 복사 클릭

URL 형식: https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyz...

⚠️ 주의: 이 URL은 비밀번호처럼 관리해야 한다. 누구나 이 URL로 메시지를 보낼 수 있다.

2단계: Jenkinsfile 설정

기본 설정 (빌드 실패 알림)

pipeline {
    agent any
    
    environment {
        DISCORD_WEBHOOK = 'https://discord.com/api/webhooks/YOUR_WEBHOOK_URL'
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }
        
        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
    }
    
    post {
        failure {
            sh """
                curl -X POST \${DISCORD_WEBHOOK} \
                  -H "Content-Type: application/json" \
                  -d '{
                    "content": "🚨 **빌드 실패!**",
                    "embeds": [{
                      "title": "${JOB_NAME} #${BUILD_NUMBER}",
                      "url": "${BUILD_URL}console",
                      "color": 15158332,
                      "fields": [
                        {
                          "name": "브랜치",
                          "value": "${GIT_BRANCH}",
                          "inline": true
                        },
                        {
                          "name": "커밋",
                          "value": "${GIT_COMMIT}",
                          "inline": true
                        }
                      ]
                    }]
                  }'
            """
        }
    }
}

성공/실패 알림 모두 받기

post {
    success {
        sh """
            curl -X POST \${DISCORD_WEBHOOK} \
              -H "Content-Type: application/json" \
              -d '{
                "content": "✅ **빌드 성공!**",
                "embeds": [{
                  "title": "${JOB_NAME} #${BUILD_NUMBER}",
                  "url": "${BUILD_URL}",
                  "color": 3066993,
                  "description": "모든 테스트 통과"
                }]
              }'
        """
    }
    
    failure {
        sh """
            curl -X POST \${DISCORD_WEBHOOK} \
              -H "Content-Type: application/json" \
              -d '{
                "content": "🚨 **빌드 실패!**",
                "embeds": [{
                  "title": "${JOB_NAME} #${BUILD_NUMBER}",
                  "url": "${BUILD_URL}console",
                  "color": 15158332,
                  "description": "빌드 또는 테스트 실패"
                }]
              }'
        """
    }
}

3단계: 테스트

의도적으로 빌드 실패시키기

stage('Test') {
    steps {
        sh 'exit 1'  // 강제로 실패
    }
}

빌드를 실행하면 Discord 채널에 알림이 와야 한다!

고급 설정

특정 사용자 멘션

빌드 실패 시 특정 사용자에게 알림:

failure {
    sh """
        curl -X POST \${DISCORD_WEBHOOK} \
          -H "Content-Type: application/json" \
          -d '{
            "content": "<@YOUR_USER_ID> 🚨 빌드 실패!",
            "embeds": [{
              "title": "${JOB_NAME} #${BUILD_NUMBER}",
              "url": "${BUILD_URL}console",
              "color": 15158332
            }]
          }'
    """
}

User ID 확인 방법:

  1. Discord에서 사용자 설정고급개발자 모드 켜기
  2. 자신의 프로필 우클릭 → 사용자 ID 복사

에러 로그 포함

마지막 20줄의 에러 로그를 포함:

failure {
    script {
        def log = sh(
            script: "tail -20 ${WORKSPACE}/build.log || echo 'No log available'",
            returnStdout: true
        ).trim()
        
        sh """
            curl -X POST \${DISCORD_WEBHOOK} \
              -H "Content-Type: application/json" \
              -d '{
                "content": "🚨 **빌드 실패!**",
                "embeds": [{
                  "title": "${JOB_NAME} #${BUILD_NUMBER}",
                  "url": "${BUILD_URL}console",
                  "color": 15158332,
                  "fields": [{
                    "name": "에러 로그 (마지막 20줄)",
                    "value": "\\`\\`\\`${log}\\`\\`\\`"
                  }]
                }]
              }'
        """
    }
}

브랜치별 다른 채널

main 브랜치는 중요한 채널, develop은 일반 채널:

environment {
    DISCORD_WEBHOOK_MAIN = 'https://discord.com/api/webhooks/MAIN_WEBHOOK'
    DISCORD_WEBHOOK_DEV = 'https://discord.com/api/webhooks/DEV_WEBHOOK'
}

post {
    failure {
        script {
            def webhook = env.GIT_BRANCH == 'main' ? 
                env.DISCORD_WEBHOOK_MAIN : env.DISCORD_WEBHOOK_DEV
            
            sh """
                curl -X POST ${webhook} \
                  -H "Content-Type: application/json" \
                  -d '{
                    "content": "🚨 빌드 실패!",
                    "embeds": [{
                      "title": "${JOB_NAME} #${BUILD_NUMBER}",
                      "url": "${BUILD_URL}console",
                      "color": 15158332
                    }]
                  }'
            """
        }
    }
}

빌드 시간 표시

post {
    always {
        script {
            def duration = currentBuild.durationString.replace(' and counting', '')
            def status = currentBuild.result == 'SUCCESS' ? '✅' : '🚨'
            def color = currentBuild.result == 'SUCCESS' ? 3066993 : 15158332
            
            sh """
                curl -X POST \${DISCORD_WEBHOOK} \
                  -H "Content-Type: application/json" \
                  -d '{
                    "content": "${status} 빌드 ${currentBuild.result}",
                    "embeds": [{
                      "title": "${JOB_NAME} #${BUILD_NUMBER}",
                      "url": "${BUILD_URL}",
                      "color": ${color},
                      "fields": [
                        {
                          "name": "소요 시간",
                          "value": "${duration}",
                          "inline": true
                        },
                        {
                          "name": "브랜치",
                          "value": "${GIT_BRANCH}",
                          "inline": true
                        }
                      ],
                      "timestamp": "${new Date().format("yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'")}"
                    }]
                  }'
            """
        }
    }
}

Discord Embed 색상 코드

상태색상코드
성공🟢 초록색3066993
실패🔴 빨간색15158332
경고🟡 노란색16776960
정보🔵 파란색3447003

보안 고려사항

Webhook URL 보호

Webhook URL이 노출되면 누구나 메시지를 보낼 수 있다.

Jenkins Credentials 사용

방법 1: Jenkins Credentials Store

  1. Jenkins 관리Manage Credentials
  2. Add Credentials 클릭
  3. Kind: Secret text
  4. Secret: Webhook URL 입력
  5. ID: discord-webhook

Jenkinsfile:

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }
    }
    
    post {
        failure {
            withCredentials([string(credentialsId: 'discord-webhook', variable: 'WEBHOOK_URL')]) {
                sh """
                    curl -X POST \${WEBHOOK_URL} \
                      -H "Content-Type: application/json" \
                      -d '{
                        "content": "🚨 빌드 실패!",
                        "embeds": [{
                          "title": "${JOB_NAME} #${BUILD_NUMBER}",
                          "url": "${BUILD_URL}console",
                          "color": 15158332
                        }]
                      }'
                """
            }
        }
    }
}

방법 2: 환경 변수

Jenkins 시스템 설정에서 환경 변수로 등록:

Jenkins 관리시스템 설정Global propertiesEnvironment variables

  • Name: DISCORD_WEBHOOK
  • Value: Webhook URL

Jenkinsfile에서 ${DISCORD_WEBHOOK} 사용

Webhook URL 재생성

혹시 URL이 노출되었다면:

  1. Discord 채널 설정 → 연동웹후크
  2. 기존 Webhook 삭제
  3. 새 Webhook 생성
  4. Jenkins 설정 업데이트

실전 팁

알림 피로 방지

모든 빌드에 알림을 보내면 알림 피로 가 생긴다.

추천 설정:

  • main 브랜치 빌드 실패: 즉시 알림
  • 첫 빌드 실패: 알림
  • 연속 실패: 첫 실패만 알림
  • 실패 후 성공: 알림 (복구 확인)
post {
    failure {
        script {
            // 이전 빌드가 성공이었을 때만 알림
            if (currentBuild.getPreviousBuild()?.result == 'SUCCESS') {
                sh """
                    curl -X POST \${DISCORD_WEBHOOK} \
                      -H "Content-Type: application/json" \
                      -d '{
                        "content": "🚨 빌드 실패! (이전 빌드는 성공)",
                        "embeds": [{
                          "title": "${JOB_NAME} #${BUILD_NUMBER}",
                          "url": "${BUILD_URL}console",
                          "color": 15158332
                        }]
                      }'
                """
            }
        }
    }
    
    success {
        script {
            // 이전 빌드가 실패였을 때만 알림
            if (currentBuild.getPreviousBuild()?.result == 'FAILURE') {
                sh """
                    curl -X POST \${DISCORD_WEBHOOK} \
                      -H "Content-Type: application/json" \
                      -d '{
                        "content": "✅ 빌드 복구됨!",
                        "embeds": [{
                          "title": "${JOB_NAME} #${BUILD_NUMBER}",
                          "url": "${BUILD_URL}",
                          "color": 3066993
                        }]
                      }'
                """
            }
        }
    }
}

멀티 브랜치 파이프라인

여러 브랜치를 동시에 빌드하는 경우:

post {
    failure {
        sh """
            curl -X POST \${DISCORD_WEBHOOK} \
              -H "Content-Type: application/json" \
              -d '{
                "content": "🚨 **${env.BRANCH_NAME}** 브랜치 빌드 실패!",
                "embeds": [{
                  "title": "${JOB_NAME} #${BUILD_NUMBER}",
                  "url": "${BUILD_URL}console",
                  "color": 15158332,
                  "fields": [
                    {
                      "name": "브랜치",
                      "value": "${env.BRANCH_NAME}",
                      "inline": true
                    },
                    {
                      "name": "커밋 메시지",
                      "value": "$(git log -1 --pretty=%B | head -1)",
                      "inline": false
                    }
                  ]
                }]
              }'
        """
    }
}

스테이지별 실패 알림

어느 스테이지에서 실패했는지 알림:

stage('Build') {
    steps {
        script {
            try {
                sh 'npm run build'
            } catch (Exception e) {
                sh """
                    curl -X POST \${DISCORD_WEBHOOK} \
                      -H "Content-Type: application/json" \
                      -d '{
                        "content": "🚨 **Build 스테이지** 실패!",
                        "embeds": [{
                          "title": "${JOB_NAME} #${BUILD_NUMBER}",
                          "url": "${BUILD_URL}console",
                          "color": 15158332,
                          "description": "빌드 중 오류 발생"
                        }]
                      }'
                """
                throw e
            }
        }
    }
}

트러블슈팅

curl 명령어가 실행되지 않음

증상: curl: command not found

해결:

// Docker agent 사용 시
agent {
    docker {
        image 'node:18'
        args '-u root'  // curl 설치 권한
    }
}

stages {
    stage('Install curl') {
        steps {
            sh 'apt-get update && apt-get install -y curl'
        }
    }
}

JSON 파싱 에러

증상: Discord에서 Invalid JSON 에러

해결:

  • JSON 내부의 따옴표를 이스케이프: \"
  • Groovy 변수를 문자열에 포함할 때 주의
  • JSON validator로 검증: jsonlint.com

Webhook URL이 작동하지 않음

확인 사항:

  1. Webhook URL이 정확한지
  2. Webhook이 삭제되지 않았는지
  3. Discord 채널 권한 확인

테스트:

curl -X POST https://discord.com/api/webhooks/YOUR_WEBHOOK_URL \
  -H "Content-Type: application/json" \
  -d '{"content": "테스트 메시지"}'

한글이 깨짐

해결:

sh """
    curl -X POST \${DISCORD_WEBHOOK} \
      -H "Content-Type: application/json; charset=utf-8" \
      -d '{
        "content": "🚨 빌드 실패!"
      }'
"""

응용: Slack 대신 Discord 사용

회사에서 Slack 유료 플랜을 쓰지 않는다면 Discord가 좋은 대안이다.

Discord의 장점:

  • ✅ 무료 무제한 메시지 히스토리
  • ✅ Webhook 무료
  • ✅ 모바일 알림 좋음
  • ✅ 음성 채널 무료

단점:

  • ❌ 업무용 이미지 (게임 메신저 느낌)
  • ❌ 엔터프라이즈 기능 부족

마무리

Jenkins Discord Webhook 알림은:

  • 5분 설정
  • 무료
  • 플러그인 불필요
  • 실시간 알림

빌드 실패를 놓치지 말고, 지금 바로 설정해보자!

참고