Skip to content

Commit 535ecda

Browse files
authored
Merge pull request #43 from KB-Hackerton/feature/10
Feature/10
2 parents caf419f + ab6860d commit 535ecda

File tree

10 files changed

+331
-25
lines changed

10 files changed

+331
-25
lines changed

.github/workflows/CI-CD.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
build:
88
runs-on: ubuntu-latest
99
steps:
10-
- name: 📢 깃허브 checkout!!!!!
10+
- name: 📢 깃허브 checkout!!!
1111
uses: actions/checkout@v3
1212

1313
- name: 📢 GitHubAction JDK 17 설치

src/main/java/kb_hack/backend/domain/member/controller/TestController.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package kb_hack.backend.domain.member.controller;
22

3+
import kb_hack.backend.global.common.exception.enums.BadStatusCode;
4+
import kb_hack.backend.global.common.exception.type.BadRequestException;
35
import kb_hack.backend.global.security.mapper.SecurityMemberMapper;
46
import lombok.RequiredArgsConstructor;
57
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -13,4 +15,24 @@ public class TestController {
1315
private final BCryptPasswordEncoder bCryptPasswordEncoder;
1416
@GetMapping("")
1517
public String test() { return "ok"; }
18+
19+
@GetMapping("/400")
20+
public String test400(@RequestParam(required = false) String name) {
21+
if (name == null || name.isBlank()) {
22+
throw new BadRequestException(BadStatusCode.INVALID_PARAMETER_EXCEPTION);
23+
}
24+
return "ok";
25+
}
26+
27+
@GetMapping("/500")
28+
public String test500() {
29+
int error = 1 / 0;
30+
return "ok";
31+
}
32+
33+
@GetMapping("/missing-param")
34+
public String testMissing(@RequestParam String name) {
35+
return "hello " + name;
36+
}
37+
1638
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package kb_hack.backend.global.Discord.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
5+
6+
public class AsyncConfig {
7+
@Bean
8+
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
9+
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
10+
taskExecutor.setCorePoolSize(2);
11+
taskExecutor.setMaxPoolSize(4);
12+
taskExecutor.setQueueCapacity(100);
13+
taskExecutor.setThreadNamePrefix("discord-");
14+
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
15+
taskExecutor.setAwaitTerminationSeconds(5);
16+
taskExecutor.initialize();
17+
return taskExecutor;
18+
}
19+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package kb_hack.backend.global.Discord.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.log4j.Log4j2;
5+
import org.springframework.beans.factory.annotation.Qualifier;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.scheduling.annotation.Async;
8+
import org.springframework.stereotype.Service;
9+
import org.springframework.web.reactive.function.client.WebClient;
10+
11+
import java.time.LocalDateTime;
12+
import java.time.format.DateTimeFormatter;
13+
import java.util.Map;
14+
15+
@Service
16+
@Log4j2
17+
@RequiredArgsConstructor
18+
public class DiscordService {
19+
20+
@Value("${discord.500.webhook-uri}")
21+
private String webhook5xxUrl;
22+
23+
@Value("${discord.400.webhook-uri}")
24+
private String webhook4xxUrl;
25+
26+
@Qualifier("discordWebClient")
27+
private final WebClient discordWebClient;
28+
29+
// ========================
30+
// 500 에러 알림
31+
// ========================
32+
@Async("threadPoolTaskExecutor")
33+
public void send5xxNotification(String errorMessage, String stackTrace, String clientInfo , String requestInfo) {
34+
try {
35+
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
36+
String errMsg = (errorMessage == null || errorMessage.isEmpty()) ? "Unknown Error" : errorMessage;
37+
38+
String method = "";
39+
String url = "";
40+
if (requestInfo != null && requestInfo.contains(" ")) {
41+
String[] parts = requestInfo.split(" ");
42+
if (parts.length >= 2) {
43+
method = parts[0];
44+
url = parts[1];
45+
}
46+
}
47+
48+
String st = (stackTrace == null)
49+
? "None"
50+
: (stackTrace.length() > 1500 ? stackTrace.substring(0, 1500) + "..." : stackTrace);
51+
52+
53+
StringBuilder message = new StringBuilder();
54+
message.append("```")
55+
.append("\n┌─ 🔥 Server Error 🔥")
56+
.append("\n│ time : ").append(timestamp)
57+
.append("\n│ method : ").append(method)
58+
.append("\n│ url : ").append(url)
59+
.append("\n│ message : ").append(errMsg);
60+
if (clientInfo != null && !clientInfo.isEmpty()) {
61+
message.append("\n│ client : ").append(clientInfo);
62+
}
63+
message.append("\n│ stack : below")
64+
.append("\n└─ \n")
65+
.append(st)
66+
.append("\n```");
67+
68+
69+
discordWebClient.post()
70+
.uri(webhook5xxUrl)
71+
.bodyValue(Map.of(
72+
"content", message.toString(),
73+
"username", "🚨 500대 에러 발생 🚨"
74+
))
75+
.retrieve()
76+
.toBodilessEntity()
77+
.doOnSuccess(res -> log.info("✅ Discord 5xx 알림 전송 성공"))
78+
.doOnError(err -> log.error("❌ Discord 5xx 알림 전송 실패: {}", err.getMessage()))
79+
.subscribe();
80+
81+
} catch (Exception e) {
82+
log.error("Discord 5xx 알림 처리 중 예외 발생: {}", e.getMessage(), e);
83+
}
84+
}
85+
86+
87+
// ========================
88+
// 400 에러 알림
89+
// ========================
90+
@Async("threadPoolTaskExecutor")
91+
public void send4xxNotification(String errorMessage, String requestInfo, String clientInfo) {
92+
try {
93+
if (webhook4xxUrl == null || webhook4xxUrl.isEmpty()) {
94+
log.debug("400대 에러 웹훅 URL이 설정되지 않음");
95+
return;
96+
}
97+
98+
// 기본 메시지 구성
99+
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
100+
String errMsg = (errorMessage == null || errorMessage.isEmpty()) ? "Unknown Error" : errorMessage;
101+
102+
// 요청 정보 파싱
103+
String method = "";
104+
String url = "";
105+
if (requestInfo != null && requestInfo.contains(" ")) {
106+
String[] parts = requestInfo.split(" ");
107+
if (parts.length >= 2) {
108+
method = parts[0];
109+
url = parts[1];
110+
}
111+
}
112+
113+
// 디스코드 박스 포맷 문자열
114+
StringBuilder message = new StringBuilder();
115+
message.append("```")
116+
.append("\n┌─ 🔥 Client Error 🔥")
117+
.append("\n│ time : ").append(timestamp)
118+
.append("\n│ method : ").append(method)
119+
.append("\n│ url : ").append(url)
120+
.append("\n│ message : ").append(errMsg);
121+
122+
if (clientInfo != null && !clientInfo.isEmpty()) {
123+
message.append("\n│ client : ").append(clientInfo);
124+
}
125+
126+
message.append("\n└─")
127+
.append("```");
128+
129+
// 전송
130+
discordWebClient.post()
131+
.uri(webhook4xxUrl)
132+
.bodyValue(Map.of(
133+
"content", message.toString(),
134+
"username", "💬 400대 에러 발생 💬"
135+
))
136+
.retrieve()
137+
.toBodilessEntity()
138+
.doOnSuccess(res -> log.info("✅ Discord 4xx 알림 전송 성공"))
139+
.doOnError(err -> log.error("❌ Discord 4xx 알림 전송 실패: {}", err.getMessage()))
140+
.subscribe();
141+
142+
} catch (Exception e) {
143+
log.error("Discord 4xx 알림 처리 중 예외 발생: {}", e.getMessage(), e);
144+
}
145+
}
146+
147+
}

src/main/java/kb_hack/backend/global/common/exception/enums/BadStatusCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public enum BadStatusCode {
1111
VERIFICATION_FAIL_EXCEPTION(HttpStatus.BAD_REQUEST, "인증 코드가 일치하지 않습니다."),
1212
INVALID_ACCESS_TOKEN_EXCEPTION(HttpStatus.BAD_REQUEST,"토큰이 유효하지 않습니다."),
1313
INVALID_AUTHORIZATION_HEADER_EXCEPTION(HttpStatus.BAD_REQUEST,"인증 헤더가 유효하지 않습니다"),
14+
INVALID_PARAMETER_EXCEPTION(HttpStatus.BAD_REQUEST,"파라미터를 잘못 입력 하셨습니다."),
1415

1516

1617
//401 UNAUTHORIZED

0 commit comments

Comments
 (0)