From 81fa4222ca4b8b24829f1e630e27a950255283eb Mon Sep 17 00:00:00 2001 From: coli Date: Tue, 14 Oct 2025 21:16:55 +0900 Subject: [PATCH 1/5] =?UTF-8?q?chore:=20src=20=EB=94=94=EB=A0=89=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application-ci.yml | 17 ----------------- src/test/resources/application.yml | 23 ----------------------- 2 files changed, 40 deletions(-) delete mode 100644 src/test/resources/application-ci.yml delete mode 100644 src/test/resources/application.yml diff --git a/src/test/resources/application-ci.yml b/src/test/resources/application-ci.yml deleted file mode 100644 index 4c6f5145..00000000 --- a/src/test/resources/application-ci.yml +++ /dev/null @@ -1,17 +0,0 @@ -spring: - config: - activate: - on-profile: ci - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:database - username: sa - password: - jpa: - show-sql: true - properties: - hibernate: - format_sql: true - hibernate: - ddl-auto: create-drop - defer-datasource-initialization: true diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml deleted file mode 100644 index f0d25220..00000000 --- a/src/test/resources/application.yml +++ /dev/null @@ -1,23 +0,0 @@ -spring: - profiles: - active: test - ---- - -spring: - config: - activate: - on-profile: test - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:database - username: sa - password: - jpa: - show-sql: true - properties: - hibernate: - format_sql: true - hibernate: - ddl-auto: create-drop - defer-datasource-initialization: true From 8ef613c0a05f4ee0c68db9f3df897626f55320a9 Mon Sep 17 00:00:00 2001 From: coli Date: Tue, 14 Oct 2025 21:24:25 +0900 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20=EB=94=94=EC=8A=A4=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=95=8C=EB=A6=BC=20=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gss-api-app/build.gradle | 3 ++ .../com/devoops/config/NotifierConfig.java | 40 +++++++++++++++++++ .../com/devoops/notifier/ConsoleNotifier.java | 10 +++++ .../com/devoops/notifier/DiscordNotifier.java | 35 ++++++++++++++++ .../devoops/notifier/DiscordProperties.java | 27 +++++++++++++ .../java/com/devoops/notifier/NotifyPort.java | 6 +++ .../devoops/notifier/DiscordNotifierTest.java | 21 ++++++++++ .../exception/errorcode/ErrorCode.java | 4 +- 8 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java create mode 100644 gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java create mode 100644 gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java create mode 100644 gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java create mode 100644 gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java create mode 100644 gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java diff --git a/gss-api-app/build.gradle b/gss-api-app/build.gradle index 0d13d8c8..211a71a0 100644 --- a/gss-api-app/build.gradle +++ b/gss-api-app/build.gradle @@ -29,6 +29,9 @@ dependencies { //Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' + //Discord + implementation 'net.dv8tion:JDA:5.0.0-beta.24' + testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java b/gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java new file mode 100644 index 00000000..9eeec10a --- /dev/null +++ b/gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java @@ -0,0 +1,40 @@ +package com.devoops.config; + +import com.devoops.notifier.ConsoleNotifier; +import com.devoops.notifier.DiscordNotifier; +import com.devoops.notifier.DiscordProperties; +import com.devoops.notifier.NotifyPort; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +public class NotifierConfig { + + @Profile({"dev", "prod"}) + @Configuration + @RequiredArgsConstructor + @EnableConfigurationProperties(DiscordProperties.class) + public static class DiscordNotifierConfig { + + private final DiscordProperties discordProperties; + + @Bean + public NotifyPort discordNotifier() { + return new DiscordNotifier(discordProperties); + } + } + + @Profile({"test", "local"}) + @Configuration + public static class ConsoleNotifierConfig { + + @Bean + public NotifyPort consoleNotifier() { + return new ConsoleNotifier(); + } + } +} diff --git a/gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java b/gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java new file mode 100644 index 00000000..2645d1e3 --- /dev/null +++ b/gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java @@ -0,0 +1,10 @@ +package com.devoops.notifier; + + +public class ConsoleNotifier implements NotifyPort { + + @Override + public void sendMessage(String message) { + System.out.println("[메시지 발송] : " + message); + } +} diff --git a/gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java b/gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java new file mode 100644 index 00000000..0ee1a12a --- /dev/null +++ b/gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java @@ -0,0 +1,35 @@ +package com.devoops.notifier; + + +import com.devoops.exception.custom.GssException; +import com.devoops.exception.errorcode.ErrorCode; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +public class DiscordNotifier implements NotifyPort { + + private final DiscordProperties properties; + + private final JDA jda; + + public DiscordNotifier(DiscordProperties discordProperties) { + this.properties = discordProperties; + this.jda = initializeJda(properties.getToken()); + } + + private JDA initializeJda(String token) { + try { + return JDABuilder.createDefault(token).build().awaitReady(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new GssException(ErrorCode.DISCORD_JDA_EXCEPTION); + } + } + + @Override + public void sendMessage(String message) { + TextChannel channel = jda.getTextChannelById(properties.getChannelId()); + channel.sendMessage(message).queue(); + } +} diff --git a/gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java b/gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java new file mode 100644 index 00000000..731cd792 --- /dev/null +++ b/gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java @@ -0,0 +1,27 @@ +package com.devoops.notifier; + +import com.devoops.exception.custom.GssException; +import com.devoops.exception.errorcode.ErrorCode; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@ConfigurationProperties(prefix = "discord") +public class DiscordProperties { + + private final String token; + private final String channelId; + + public DiscordProperties(String token, String channelId) { + validate(token); + validate(channelId); + this.token = token; + this.channelId = channelId; + } + + private void validate(String element) { + if (element == null || element.isBlank()) { + throw new GssException(ErrorCode.DISCORD_PROPERTIES_EMPTY); + } + } +} diff --git a/gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java b/gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java new file mode 100644 index 00000000..c3dfec92 --- /dev/null +++ b/gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java @@ -0,0 +1,6 @@ +package com.devoops.notifier; + +public interface NotifyPort { + + void sendMessage(String message); +} diff --git a/gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java b/gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java new file mode 100644 index 00000000..109ca4a5 --- /dev/null +++ b/gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java @@ -0,0 +1,21 @@ +package com.devoops.notifier; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@Disabled +class DiscordNotifierTest { + + @Nested + class SendMessage { + + @Test + void sendMessage() { + + } + } + +} diff --git a/gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java b/gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java index 03244ab2..13eecb43 100644 --- a/gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java +++ b/gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java @@ -41,7 +41,9 @@ public enum ErrorCode { GITHUB_CLIENT_ERROR(500, "깃허브 클라이언트 소통과정에 문제가 발생했습니다"), AI_RESPONSE_PARSING_ERROR(500, "AI로부터 온 질문 생성을 파싱하는 과정에 오류가 발생했습니다"), AI_CREATE_QUESTION_ERROR(500, "AI 질문 생성과정에 오류가 발생했습니다"), - AI_CHARGE_NOT_FOUND(500, "당월 AI 비용을 찾을 없습니다.") + AI_CHARGE_NOT_FOUND(500, "당월 AI 비용을 찾을 수 없습니다."), + DISCORD_PROPERTIES_EMPTY(500, "디스코드 채널 id와 키를 찾을 수 없습니다"), + DISCORD_JDA_EXCEPTION(500, "디스코드 JDA 초기화에 실패하였습니다"), ; private final int statusCode; From a6c61af4184745ac2f39de9019a55fc781ae9042 Mon Sep 17 00:00:00 2001 From: coli Date: Tue, 14 Oct 2025 23:51:38 +0900 Subject: [PATCH 3/5] =?UTF-8?q?chore:=20application-test=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/resources/{application.yml => application-test.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gss-api-app/src/test/resources/{application.yml => application-test.yml} (100%) diff --git a/gss-api-app/src/test/resources/application.yml b/gss-api-app/src/test/resources/application-test.yml similarity index 100% rename from gss-api-app/src/test/resources/application.yml rename to gss-api-app/src/test/resources/application-test.yml From 3368734c3c656c1bc200331d9c6f791b9c578a72 Mon Sep 17 00:00:00 2001 From: coli Date: Tue, 14 Oct 2025 23:53:53 +0900 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gss-api-app/build.gradle | 3 -- .../exception/GlobalExceptionHandler.java | 5 +++ .../service/auth/jwt/JwtProperties.java | 1 - .../devoops/notifier/DiscordNotifierTest.java | 21 ---------- .../notifier/adapter/ErrorNotifierTest.java | 20 ++++++++++ .../message/ErrorMessageResolver.java | 30 +++++++++++++++ .../exception}/notifier/NotifyPort.java | 2 +- gss-domain/build.gradle | 3 ++ .../com/devoops/config/NotifierConfig.java | 8 ++-- .../domain}/notifier/ConsoleNotifier.java | 4 +- .../domain}/notifier/DiscordNotifier.java | 8 ++-- .../domain}/notifier/DiscordProperties.java | 2 +- .../domain/notifier/ErrorNotifier.java | 18 +++++++++ .../com/devoops/exception/ErrorResponse.java | 19 ++++++++++ .../exception/GlobalExceptionHandler.java | 38 +++++++++++++++++++ 15 files changed, 147 insertions(+), 35 deletions(-) delete mode 100644 gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java create mode 100644 gss-api-app/src/test/java/com/devoops/notifier/adapter/ErrorNotifierTest.java create mode 100644 gss-common/src/main/java/com/devoops/exception/message/ErrorMessageResolver.java rename {gss-api-app/src/main/java/com/devoops => gss-common/src/main/java/com/devoops/exception}/notifier/NotifyPort.java (64%) rename {gss-api-app => gss-domain}/src/main/java/com/devoops/config/NotifierConfig.java (83%) rename {gss-api-app/src/main/java/com/devoops => gss-domain/src/main/java/com/devoops/domain}/notifier/ConsoleNotifier.java (68%) rename {gss-api-app/src/main/java/com/devoops => gss-domain/src/main/java/com/devoops/domain}/notifier/DiscordNotifier.java (89%) rename {gss-api-app/src/main/java/com/devoops => gss-domain/src/main/java/com/devoops/domain}/notifier/DiscordProperties.java (95%) create mode 100644 gss-domain/src/main/java/com/devoops/domain/notifier/ErrorNotifier.java create mode 100644 gss-mcp-app/src/main/java/com/devoops/exception/ErrorResponse.java create mode 100644 gss-mcp-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java diff --git a/gss-api-app/build.gradle b/gss-api-app/build.gradle index 211a71a0..0d13d8c8 100644 --- a/gss-api-app/build.gradle +++ b/gss-api-app/build.gradle @@ -29,9 +29,6 @@ dependencies { //Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' - //Discord - implementation 'net.dv8tion:JDA:5.0.0-beta.24' - testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/gss-api-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java b/gss-api-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java index 2de5c37b..5e2b8af5 100644 --- a/gss-api-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java +++ b/gss-api-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ import com.devoops.exception.custom.GssException; import com.devoops.exception.errorcode.ErrorCode; +import com.devoops.domain.notifier.ErrorNotifier; import io.jsonwebtoken.ExpiredJwtException; import jakarta.validation.ConstraintViolationException; import lombok.RequiredArgsConstructor; @@ -21,6 +22,8 @@ @RequiredArgsConstructor public class GlobalExceptionHandler { + private final ErrorNotifier errorNotifier; + @ExceptionHandler(BindException.class) public ResponseEntity handleBindingException(BindException exception) { log.warn("Binding exception occurred", exception); @@ -69,6 +72,7 @@ public ResponseEntity handleNoResourceFoundException(NoResourceFo @ExceptionHandler(GssException.class) public ResponseEntity handleGssException(GssException exception) { log.error("Custom GssException occurred: {}", exception.getMessage(), exception); + errorNotifier.notify(exception); return toResponse(exception.getErrorCode()); } @@ -80,6 +84,7 @@ public ResponseEntity handleException(ExpiredJwtException excepti @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception exception) { log.error("Unhandled exception occurred", exception); + errorNotifier.notify(exception); return toResponse(ErrorCode.INTERNAL_SERVER_ERROR); } diff --git a/gss-api-app/src/main/java/com/devoops/service/auth/jwt/JwtProperties.java b/gss-api-app/src/main/java/com/devoops/service/auth/jwt/JwtProperties.java index dc6b276a..2af11024 100644 --- a/gss-api-app/src/main/java/com/devoops/service/auth/jwt/JwtProperties.java +++ b/gss-api-app/src/main/java/com/devoops/service/auth/jwt/JwtProperties.java @@ -24,7 +24,6 @@ public JwtProperties(String secretKey, Duration accessTokenExpiration, Duration this.refreshTokenExpiration = refreshTokenExpiration; } - public Duration getExpirationByTokenType(TokenType type) { if(type == TokenType.ACCESS_TOKEN) { return accessTokenExpiration; diff --git a/gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java b/gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java deleted file mode 100644 index 109ca4a5..00000000 --- a/gss-api-app/src/test/java/com/devoops/notifier/DiscordNotifierTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.devoops.notifier; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@Disabled -class DiscordNotifierTest { - - @Nested - class SendMessage { - - @Test - void sendMessage() { - - } - } - -} diff --git a/gss-api-app/src/test/java/com/devoops/notifier/adapter/ErrorNotifierTest.java b/gss-api-app/src/test/java/com/devoops/notifier/adapter/ErrorNotifierTest.java new file mode 100644 index 00000000..3d016b28 --- /dev/null +++ b/gss-api-app/src/test/java/com/devoops/notifier/adapter/ErrorNotifierTest.java @@ -0,0 +1,20 @@ +package com.devoops.notifier.adapter; + +import com.devoops.BaseControllerTest; +import com.devoops.domain.notifier.ErrorNotifier; +import com.devoops.exception.GithubNotFoundException; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@Disabled +class ErrorNotifierTest extends BaseControllerTest { + + @Autowired + private ErrorNotifier errorNotifier; + + @Test + void sendMessage() { + errorNotifier.notify(new GithubNotFoundException("Github Not Found")); + } +} diff --git a/gss-common/src/main/java/com/devoops/exception/message/ErrorMessageResolver.java b/gss-common/src/main/java/com/devoops/exception/message/ErrorMessageResolver.java new file mode 100644 index 00000000..e39c4761 --- /dev/null +++ b/gss-common/src/main/java/com/devoops/exception/message/ErrorMessageResolver.java @@ -0,0 +1,30 @@ +package com.devoops.exception.message; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class ErrorMessageResolver { + + private static final String NOTIFICATION_PREFIX = ":rotating_light: [**Error 발생!**]\n"; + private static final String STACK_TRACE_AFFIX = "\n```\n"; + private static final String DISCORD_LINE_SEPARATOR = "\n"; + private static final int STACK_TRACE_LENGTH = 10; + + public static String resolve(Throwable throwable) { + String errorMessage = throwable.toString(); + String stackTrace = getStackTraceAsString(throwable); + + return NOTIFICATION_PREFIX + + errorMessage + + STACK_TRACE_AFFIX + + stackTrace + + STACK_TRACE_AFFIX; + } + + private static String getStackTraceAsString(Throwable throwable) { + return Arrays.stream(throwable.getStackTrace()) + .map(StackTraceElement::toString) + .limit(STACK_TRACE_LENGTH) + .collect(Collectors.joining(DISCORD_LINE_SEPARATOR)); + } +} diff --git a/gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java b/gss-common/src/main/java/com/devoops/exception/notifier/NotifyPort.java similarity index 64% rename from gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java rename to gss-common/src/main/java/com/devoops/exception/notifier/NotifyPort.java index c3dfec92..4e01dc78 100644 --- a/gss-api-app/src/main/java/com/devoops/notifier/NotifyPort.java +++ b/gss-common/src/main/java/com/devoops/exception/notifier/NotifyPort.java @@ -1,4 +1,4 @@ -package com.devoops.notifier; +package com.devoops.exception.notifier; public interface NotifyPort { diff --git a/gss-domain/build.gradle b/gss-domain/build.gradle index 5f805bd7..5c3b727e 100644 --- a/gss-domain/build.gradle +++ b/gss-domain/build.gradle @@ -20,6 +20,9 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + //Discord + implementation 'net.dv8tion:JDA:5.0.0-beta.24' + // testFixtures testFixturesImplementation 'org.springframework.boot:spring-boot-starter-data-jpa' // includes EntityManager, TX testFixturesImplementation 'org.springframework.boot:spring-boot-starter-test' // includes SpringExtension, DataJpaTest, etc. diff --git a/gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java b/gss-domain/src/main/java/com/devoops/config/NotifierConfig.java similarity index 83% rename from gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java rename to gss-domain/src/main/java/com/devoops/config/NotifierConfig.java index 9eeec10a..c1843d6b 100644 --- a/gss-api-app/src/main/java/com/devoops/config/NotifierConfig.java +++ b/gss-domain/src/main/java/com/devoops/config/NotifierConfig.java @@ -1,9 +1,9 @@ package com.devoops.config; -import com.devoops.notifier.ConsoleNotifier; -import com.devoops.notifier.DiscordNotifier; -import com.devoops.notifier.DiscordProperties; -import com.devoops.notifier.NotifyPort; +import com.devoops.exception.notifier.NotifyPort; +import com.devoops.domain.notifier.ConsoleNotifier; +import com.devoops.domain.notifier.DiscordNotifier; +import com.devoops.domain.notifier.DiscordProperties; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import org.springframework.boot.context.properties.EnableConfigurationProperties; diff --git a/gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java b/gss-domain/src/main/java/com/devoops/domain/notifier/ConsoleNotifier.java similarity index 68% rename from gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java rename to gss-domain/src/main/java/com/devoops/domain/notifier/ConsoleNotifier.java index 2645d1e3..6313cab2 100644 --- a/gss-api-app/src/main/java/com/devoops/notifier/ConsoleNotifier.java +++ b/gss-domain/src/main/java/com/devoops/domain/notifier/ConsoleNotifier.java @@ -1,6 +1,8 @@ -package com.devoops.notifier; +package com.devoops.domain.notifier; +import com.devoops.exception.notifier.NotifyPort; + public class ConsoleNotifier implements NotifyPort { @Override diff --git a/gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java b/gss-domain/src/main/java/com/devoops/domain/notifier/DiscordNotifier.java similarity index 89% rename from gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java rename to gss-domain/src/main/java/com/devoops/domain/notifier/DiscordNotifier.java index 0ee1a12a..76bc10b5 100644 --- a/gss-api-app/src/main/java/com/devoops/notifier/DiscordNotifier.java +++ b/gss-domain/src/main/java/com/devoops/domain/notifier/DiscordNotifier.java @@ -1,16 +1,17 @@ -package com.devoops.notifier; - +package com.devoops.domain.notifier; import com.devoops.exception.custom.GssException; import com.devoops.exception.errorcode.ErrorCode; +import com.devoops.exception.notifier.NotifyPort; +import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +@Slf4j public class DiscordNotifier implements NotifyPort { private final DiscordProperties properties; - private final JDA jda; public DiscordNotifier(DiscordProperties discordProperties) { @@ -33,3 +34,4 @@ public void sendMessage(String message) { channel.sendMessage(message).queue(); } } + diff --git a/gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java b/gss-domain/src/main/java/com/devoops/domain/notifier/DiscordProperties.java similarity index 95% rename from gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java rename to gss-domain/src/main/java/com/devoops/domain/notifier/DiscordProperties.java index 731cd792..cd05e70e 100644 --- a/gss-api-app/src/main/java/com/devoops/notifier/DiscordProperties.java +++ b/gss-domain/src/main/java/com/devoops/domain/notifier/DiscordProperties.java @@ -1,4 +1,4 @@ -package com.devoops.notifier; +package com.devoops.domain.notifier; import com.devoops.exception.custom.GssException; import com.devoops.exception.errorcode.ErrorCode; diff --git a/gss-domain/src/main/java/com/devoops/domain/notifier/ErrorNotifier.java b/gss-domain/src/main/java/com/devoops/domain/notifier/ErrorNotifier.java new file mode 100644 index 00000000..3a4772b3 --- /dev/null +++ b/gss-domain/src/main/java/com/devoops/domain/notifier/ErrorNotifier.java @@ -0,0 +1,18 @@ +package com.devoops.domain.notifier; + +import com.devoops.exception.message.ErrorMessageResolver; +import com.devoops.exception.notifier.NotifyPort; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ErrorNotifier { + + private final NotifyPort notifyPort; + + public void notify(Throwable throwable) { + String message = ErrorMessageResolver.resolve(throwable); + notifyPort.sendMessage(message); + } +} diff --git a/gss-mcp-app/src/main/java/com/devoops/exception/ErrorResponse.java b/gss-mcp-app/src/main/java/com/devoops/exception/ErrorResponse.java new file mode 100644 index 00000000..8674f834 --- /dev/null +++ b/gss-mcp-app/src/main/java/com/devoops/exception/ErrorResponse.java @@ -0,0 +1,19 @@ +package com.devoops.exception; + +import com.devoops.exception.errorcode.ErrorCode; +import org.springframework.http.HttpStatus; + +public record ErrorResponse( + String code, + String status, + String message +) { + + public ErrorResponse(ErrorCode errorCode) { + this( + errorCode.name(), + HttpStatus.valueOf(errorCode.getStatusCode()).name(), + errorCode.getMessage() + ); + } +} diff --git a/gss-mcp-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java b/gss-mcp-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..d22f26ff --- /dev/null +++ b/gss-mcp-app/src/main/java/com/devoops/exception/GlobalExceptionHandler.java @@ -0,0 +1,38 @@ +package com.devoops.exception; + +import com.devoops.domain.notifier.ErrorNotifier; +import com.devoops.exception.custom.GssException; +import com.devoops.exception.errorcode.ErrorCode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +@RequiredArgsConstructor +public class GlobalExceptionHandler { + + private final ErrorNotifier errorNotifier; + + @ExceptionHandler(GssException.class) + public ResponseEntity handleGssException(GssException exception) { + log.error("Custom GssException occurred: {}", exception.getMessage(), exception); + errorNotifier.notify(exception); + return toResponse(exception.getErrorCode()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception exception) { + log.error("Unhandled exception occurred", exception); + errorNotifier.notify(exception); + return toResponse(ErrorCode.INTERNAL_SERVER_ERROR); + } + + private ResponseEntity toResponse(ErrorCode errorCode) { + ErrorResponse errorResponse = new ErrorResponse(errorCode); + return ResponseEntity.status(errorCode.getStatusCode()) + .body(errorResponse); + } +} From bf4deb339f32fed29ed31714179689c83ec7965c Mon Sep 17 00:00:00 2001 From: coli Date: Wed, 15 Oct 2025 00:06:42 +0900 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/resources/application-test.yml | 29 ++++++++----------- gss-domain/build.gradle | 5 ++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/gss-api-app/src/test/resources/application-test.yml b/gss-api-app/src/test/resources/application-test.yml index 5f0377a8..e97c392e 100644 --- a/gss-api-app/src/test/resources/application-test.yml +++ b/gss-api-app/src/test/resources/application-test.yml @@ -1,21 +1,4 @@ spring: - profiles: - active: test - -jwt: - secret_key: testtesttesttesttesttesttesttest - access_token_expiration: 1h - refresh_token_expiration: 1d - -cors: - origin: http://localhost:8080 - ---- - -spring: - config: - activate: - on-profile: test data: redis: host: localhost @@ -36,6 +19,14 @@ spring: flyway: enabled: false +jwt: + secret_key: testtesttesttesttesttesttesttest + access_token_expiration: 1h + refresh_token_expiration: 1d + +cors: + origin: http://localhost:8080 + dev-oops: mcp: webhook-url: https://test.dev-oops.kr/api/webhooks @@ -44,3 +35,7 @@ dev-oops: url: https://api.github.com connect-timeout: 3000 read-timeout: 3000 + +discord: + token: testToken + channel-id: testChannel diff --git a/gss-domain/build.gradle b/gss-domain/build.gradle index 5c3b727e..62e464b6 100644 --- a/gss-domain/build.gradle +++ b/gss-domain/build.gradle @@ -36,3 +36,8 @@ dependencies { runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' } + +tasks.withType(JavaCompile) { + options.compilerArgs += "-parameters" +} +