백엔드

[스프링부트] 디스코드 웹훅 (1) - 에러 로그 전송 (logback)

KyuminKim 2025. 1. 7. 16:38

⭐️ 배경

프로젝트 진행 중,

🙋‍♀️ 프론트엔드: 백엔드 분들.. '500 Internal Server Error' 떴어요 ㅜㅜ 

 

아!

프론트엔드는 백엔드 API 를 호출하는 사용자 입장이기 때문에,

내부 Log는 살펴볼 수 없다는 단점이 있다

 

(이 때문에 약 1시간 삽질하셨다고 한다...)

 

🙋‍♀️백엔드: 히힛 에러 코드(400번대) 노출 안해야 외부 위협에 안정적이겠지?

🙋‍♀️프론트엔드: 또 500 에러네... 뭐가 문제지?

 

문제를 해결하고자

'백엔드 스프링부트 서버 에러를 디스코드로 전송하자'

는 목표를 세웠다.


⭐️ 환경

백엔드 환경은 다음과 같다.

EC2에 올라간 백엔드


 

⭐️ 디스코드 웹훅

'백엔드 스프링부트 서버 에러를 디스코드로 전송하자'

라는 목적을 달성하기 위한 방법으로 디스코드 웹훅을 선택했다.

 

✅ 웹훅?

- 웹 서비스(서버)의 이벤트 데이터를 전달하는 HTTP 기반 콜백 함수

- API 통신 방식(polling / 웹훅) 중 하나

 

1️⃣일반적인 API 방식 (Polling)

- 클라이언트 ➔ 서버 호출

- 서버 이벤트 발생 시

    : 서버에 이벤트가 발생했는지 클라이언트가 주기적으로 요청하여 알아냄 (클라이언트 측에서 호출 가능) 

[토스] 개발 용어 사전

 

2️⃣웹훅 (역항향 API)

- 서버 ➔ 클라이언트 호출

- 이때 클라이언트의 URL = Callback URL

- 서버 이벤트 발생 시 

    : 서버 이벤트 발생 사실을 클라이언트로 요청 보냄 (서버 측에서 호출 가능)

[토스] 개발 용어 사전

 

 

 

 디스코드 웹훅을 프로젝트에 어떻게 적용할까?

디스코드 웹훅 적용

스프링부트 서버 에러를 디스코드로 전달하도록 한다!

 

즉,

웹훅 서버 = 스프링부트 웹 서버

웹훅 클라이언트 = 디스코드 서버

 

https://velog.io/@devlyny/Spring-AOP를-사용하여-Slf4j-로-로깅하기

 

Spring AOP를 사용하여 @Slf4j 로 로깅하기

서버 로깅, 저는 이렇게 합니다!

velog.io

 

구현 사항은 이 블로그를 참고하였는데,

 

스프링부트의 로깅은 기본적으로 콘솔에 나오는데,

로그 출력 부분을 콘솔 (기본) + 디스코드로 설정을 바꾼다고 생각하면 된다!

로그 출력 (콘솔 + 디스코드)

 


⭐️ 과정

1. 디스코드 웹훅 만들기

2. 스프링부트 에러를 디스코드로 전송

 

 

✅ 1. 디스코드 웹훅 만들기

1. 디스코드 서버 만들기

- 프로젝트 이름으로 서버를 자유롭게 만들어준다!

 

2. 디스코드 채널 만들기

- 방금 만든 서버 안에, 채널을 만들어준다.

- 이름은 마찬가지로 자유롭게 해도 된다 (나는 서버-에러로그 로 지어줬다)

채널 생성 완료!

3. 디스코드 웹훅 만들기

- 방금 만든 채널에 마우스 오버 ➔ [채널 편집] 클릭

- [채널 편집] ➔ [연동] ➔ [웹 후크 만들기]

- 웹 후크 생성 후, URL을 메모장에 저장해두자! 

               (이 URL이 웹훅 클라이언트 URL이다)

채널 편집
[채널 편집] -> [연동]
[연동] -> [웹후크]
웹 후크 클릭

 

✅ 2. 스프링부트 에러를 디스코드로 전송

위에서

스프링부트 로깅을 콘솔 (기본) + 디스코드로 로깅 설정을 바꿔 구현한다고 언급하였다.

 

그러기 위한 방식으로

스프링부트 로깅을 설정하는 파일 (logback-spring.xml)을 수정하

 

1. gradle 파일 수정

repositories {
	mavenCentral()
	maven { url 'https://jitpack.io' }
}

dependencies {
    // (생략)
    implementation 'org.springframework.boot:spring-boot-starter-logging'
    implementation 'com.github.napstr:logback-discord-appender:1.0.0'
}

 

 

2. application.yml 수정

# ... (생략)

logging:
  discord-error:
    webhook-url: (디스코드 웹훅 URL)
    config: classpath:logback-spring.xml

위에서 생성한 디스코드 웹훅의 URL을 여기에 넣어준다 !

 

 

3. logback-spring.xml 파일 생성

스프링부트 로그 형식을 설정하는 파일이다!

<configuration>
    <include resource="console-appender.xml"/>
    <include resource="discord-error-appender.xml"/>
    <appender-ref ref="ASYNC_DISCORD" />

    <timestamp key="BY_DATE" datePattern="yyyy-MM-dd"/>
    <!-- 로깅 테마 -->
    <property name="LOG_PATTERN"
              value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg%n"/>

    <springProperty name="DISCORD_ERROR_WEBHOOK_URL" source="logging.discord-error.webhook-url"/>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ASYNC_DISCORD" />
    </root>

</configuration>

 

console, discord 두 곳으로 로그를 출력하도록 설정할 수 있다

 

 

4. console-appender.xml 파일 생성

<included>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>
</included>

 logback-spring.xml에서 정의한 LOG_PATTERN대로 콘솔로 출력하도록 명시한다.

 

LOG_PATTERN

value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg%n"/>

 

console-appender.xml 추가 후

 

 

5. discord-error-appender.xml 파일 생성

<included>
    <appender name="DISCORD" class="com.github.napstr.logback.DiscordAppender">
        <webhookUri>${DISCORD_ERROR_WEBHOOK_URL}</webhookUri>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>

                \n [ERROR LOG] ============================================================================
                \n %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{2}.%M:%L] - %ex{short}%n
            </pattern>
        </layout>
        <username>[에러 발생]</username>
        <tts>false</tts>
    </appender>

    <appender name="ASYNC_DISCORD" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="DISCORD" />
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>
</included>

LOG_PATTERN은 콘솔과 비슷하지만, 색 지정과 같은 부분은 빠져있다

 

 


⭐️ 결과

 

에러 로깅 결과!

 

에러 로그가 잘 뜨는 모습을 볼 수 있다 !


⭐️ 여담

1.

프론트엔드 분은 백엔드 지식을 잘 모르고,

'이게 왜 이렇게 되는 건가요?'라고 직접적으로 물어보는 것이 어려울 수 있다.

 

서로 이해시키기 위해 노력하고, 그 과정을 자동화 시킨다면

프로젝트에 큰 도움이 되지 않을까 생각한다.

 

2. 

블로그를 참고하니, AOP를 적용해 로깅 적용대상 파일 / 로깅 메시지 커스터마이징할 수 있었다.

이번 경우에는 단순히 서버의 모든 에러 로그를 출력하려 하므로, AOP는 불필요하다 생각했다.


⭐️ 참고자료

웹훅 (토스 용어사전) https://docs.tosspayments.com/resources/glossary/webhook 

 

AOP를 사용해 @Slf4j로 로깅하기 https://velog.io/@devlyny/Spring-AOP를-사용하여-Slf4j-로-로깅하기