From 45dd2328d1e98ef564b408b59452f6fa56ea878e Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Mon, 28 Jul 2025 11:17:18 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20=EB=94=94=EC=8A=A4=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=97=90=EB=9F=AC=20=EB=A1=9C=EA=B9=85=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/logback-admin-dev.xml | 38 ------------------- .../resources/logback-variables.properties | 3 -- .../src/main/resources/logback-user-dev.xml | 38 ------------------- .../resources/logback-variables.properties | 3 -- 4 files changed, 82 deletions(-) delete mode 100644 nowait-app-admin-api/src/main/resources/logback-admin-dev.xml delete mode 100644 nowait-app-admin-api/src/main/resources/logback-variables.properties delete mode 100644 nowait-app-user-api/src/main/resources/logback-user-dev.xml delete mode 100644 nowait-app-user-api/src/main/resources/logback-variables.properties diff --git a/nowait-app-admin-api/src/main/resources/logback-admin-dev.xml b/nowait-app-admin-api/src/main/resources/logback-admin-dev.xml deleted file mode 100644 index ab4c82e1..00000000 --- a/nowait-app-admin-api/src/main/resources/logback-admin-dev.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - ${DISCORD_WEBHOOK_URL} - - %d{HH:mm:ss} [%thread] [%-5level] %logger{36} - %msg%n```%ex{full}``` - - 감자야...에러 났대... - https://gtablestoreimage.s3.ap-northeast-2.amazonaws.com/%EA%B8%B0%ED%83%80+%EC%9D%B4%EB%AF%B8%EC%A7%80/%E1%84%83%E1%85%A1%E1%84%8B%E1%85%AE%E1%86%AB%E1%84%85%E1%85%A9%E1%84%83%E1%85%B3.jpeg - false - - - - - - ERROR - - - - - - - - - - diff --git a/nowait-app-admin-api/src/main/resources/logback-variables.properties b/nowait-app-admin-api/src/main/resources/logback-variables.properties deleted file mode 100644 index b2823949..00000000 --- a/nowait-app-admin-api/src/main/resources/logback-variables.properties +++ /dev/null @@ -1,3 +0,0 @@ -LOG_PATH=./logs -LOG_FILE_NAME=nowait -LOG_PATTERN=%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] [%logger{36}] - %msg%n diff --git a/nowait-app-user-api/src/main/resources/logback-user-dev.xml b/nowait-app-user-api/src/main/resources/logback-user-dev.xml deleted file mode 100644 index 3c36ae1f..00000000 --- a/nowait-app-user-api/src/main/resources/logback-user-dev.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - ${DISCORD_WEBHOOK_URL} - - %d{HH:mm:ss} [%thread] [%-5level] %logger{36} - %msg%n```%ex{full}``` - - 감자야...에러 났대... - https://gtablestoreimage.s3.ap-northeast-2.amazonaws.com/%EA%B8%B0%ED%83%80+%EC%9D%B4%EB%AF%B8%EC%A7%80/%E1%84%83%E1%85%A1%E1%84%8B%E1%85%AE%E1%86%AB%E1%84%85%E1%85%A9%E1%84%83%E1%85%B3.jpeg - false - - - - - - ERROR - - - - - - - - - - diff --git a/nowait-app-user-api/src/main/resources/logback-variables.properties b/nowait-app-user-api/src/main/resources/logback-variables.properties deleted file mode 100644 index b2823949..00000000 --- a/nowait-app-user-api/src/main/resources/logback-variables.properties +++ /dev/null @@ -1,3 +0,0 @@ -LOG_PATH=./logs -LOG_FILE_NAME=nowait -LOG_PATTERN=%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] [%logger{36}] - %msg%n From c22dabffe65c7008db025119c40afae8e1a6aa94 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Mon, 28 Jul 2025 11:18:11 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20feign=20=EC=82=AC=EC=9A=A9=ED=95=9C?= =?UTF-8?q?=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=97=90=EB=9F=AC=20=EB=A1=9C?= =?UTF-8?q?=EA=B9=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nowait-app-admin-api/build.gradle | 18 ++- .../java/com/nowait/ApiAdminApplication.java | 2 + .../exception/GlobalExceptionHandler.java | 146 +++++++++++------- nowait-app-user-api/build.gradle | 18 ++- .../java/com/nowait/ApiUserApplication.java | 2 + .../exception/GlobalExceptionHandler.java | 129 ++++++++++------ nowait-infra/build.gradle | 19 +++ .../discord/client/DiscordClientAdmin.java | 18 +++ .../discord/client/DiscordClientUser.java | 18 +++ .../config/DiscordFeignConfiguration.java | 14 ++ .../nowait/discord/dto/DiscordMessage.java | 29 ++++ .../discord/service/DiscordAlarmService.java | 75 +++++++++ 12 files changed, 381 insertions(+), 107 deletions(-) create mode 100644 nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientAdmin.java create mode 100644 nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientUser.java create mode 100644 nowait-infra/src/main/java/com/nowait/discord/config/DiscordFeignConfiguration.java create mode 100644 nowait-infra/src/main/java/com/nowait/discord/dto/DiscordMessage.java create mode 100644 nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java diff --git a/nowait-app-admin-api/build.gradle b/nowait-app-admin-api/build.gradle index 3ae84d08..9fba398d 100644 --- a/nowait-app-admin-api/build.gradle +++ b/nowait-app-admin-api/build.gradle @@ -17,6 +17,16 @@ repositories { maven { url 'https://jitpack.io' } } +ext { + set('springCloudVersion', "2025.0.0") +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + dependencies { implementation project(':nowait-common') implementation project(':nowait-infra') @@ -49,13 +59,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'com.mysql:mysql-connector-j' - // 로그 - implementation 'org.springframework.boot:spring-boot-starter-aop' - runtimeOnly 'org.fusesource.jansi:jansi:2.4.0' - // 디스코드 웹훅 implementation 'com.github.napstr:logback-discord-appender:1.0.0' + //feign + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + implementation group: 'io.github.openfeign', name: 'feign-gson', version: '11.0' + // 기타 필요 라이브러리 (예: Lombok) compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/nowait-app-admin-api/src/main/java/com/nowait/ApiAdminApplication.java b/nowait-app-admin-api/src/main/java/com/nowait/ApiAdminApplication.java index b538931a..1d8d85f7 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/ApiAdminApplication.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/ApiAdminApplication.java @@ -1,11 +1,13 @@ package com.nowait; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.scheduling.annotation.EnableScheduling; @EnableJpaAuditing @EnableScheduling +@EnableFeignClients @SpringBootApplication public class ApiAdminApplication { public static void main(String[] args) { diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java index b2cbf47f..c87fe1be 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java @@ -1,8 +1,10 @@ package com.nowait.applicationadmin.exception; import static com.nowait.common.exception.ErrorMessage.*; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.UNAUTHORIZED; -import static org.springframework.http.HttpStatus.*; import java.util.Map; import java.util.stream.Collectors; @@ -15,12 +17,14 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartException; import com.nowait.applicationadmin.security.exception.ResourceNotFoundException; import com.nowait.applicationadmin.security.exception.UnauthorizedException; import com.nowait.common.exception.ErrorMessage; import com.nowait.common.exception.ErrorResponse; +import com.nowait.discord.service.DiscordAlarmService; import com.nowait.domaincorerdb.menu.exception.MenuCreationUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuDeleteUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuUpdateUnauthorizedException; @@ -41,189 +45,224 @@ import com.nowait.domaincorerdb.user.exception.UserNotFoundException; import io.swagger.v3.oas.annotations.Hidden; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Hidden @RestControllerAdvice +@RequiredArgsConstructor public class GlobalExceptionHandler { - @ResponseStatus(value = BAD_REQUEST) + private final DiscordAlarmService discordAlarmService; + + // Discord 알림 헬퍼 + private void alarm(Exception e, WebRequest request) { + discordAlarmService.sendDiscordAdminAlarm(e, request); + } + + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(BusinessException.class) - public ErrorResponse handleBusinessException(BusinessException e) { + public ErrorResponse handleBusinessException(BusinessException e, WebRequest request) { + alarm(e, request); log.error("handleBusinessException", e); return new ErrorResponse(e.getMessage(), e.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) - public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e, WebRequest request) { + alarm(e, request); log.error("handleMethodArgumentNotValidException", e); Map errors = getErrors(e); return new ErrorResponse(INVALID_INPUT_VALUE.getMessage(), INVALID_INPUT_VALUE.getCode(), errors); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) - public ErrorResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + public ErrorResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e, WebRequest request) { + alarm(e, request); log.error("handleHttpMessageNotReadableException", e); return new ErrorResponse(INVALID_INPUT_VALUE.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(IllegalArgumentException.class) - public ErrorResponse handleIllegalArgumentException(IllegalArgumentException e) { + public ErrorResponse handleIllegalArgumentException(IllegalArgumentException e, WebRequest request) { + alarm(e, request); log.error("handleIllegalArgumentException", e); return new ErrorResponse(e.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MissingRequestValueException.class) - public ErrorResponse handleMissingRequestValueException(MissingRequestValueException e) { + public ErrorResponse handleMissingRequestValueException(MissingRequestValueException e, WebRequest request) { + alarm(e, request); log.error("handleMissingRequestValueExceptionException", e); return new ErrorResponse(INVALID_INPUT_VALUE.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = UNAUTHORIZED) + @ResponseStatus(UNAUTHORIZED) @ExceptionHandler(UnauthorizedException.class) - public ErrorResponse handleUnauthorizedException(UnauthorizedException e) { - log.error("handleUnauthorizedExceptionException", e); + public ErrorResponse handleUnauthorizedException(UnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("handleUnauthorizedException", e); return new ErrorResponse(e.getMessage(), e.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(ResourceNotFoundException.class) - public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException e) { + public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException e, WebRequest request) { + alarm(e, request); log.error("handleResourceNotFoundExceptionException", e); return new ErrorResponse(e.getMessage(), e.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MultipartException.class) - public ErrorResponse handleMultipartException(MultipartException e) { + public ErrorResponse handleMultipartException(MultipartException e, WebRequest request) { + alarm(e, request); log.error("handleMultipartException", e); return new ErrorResponse(e.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(UserNotFoundException.class) - public ErrorResponse userNotFoundException(UserNotFoundException e) { + public ErrorResponse userNotFoundException(UserNotFoundException e, WebRequest request) { + alarm(e, request); log.error("userNotFoundException", e); return new ErrorResponse(e.getMessage(), NOTFOUND_USER.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(OrderParameterEmptyException.class) - public ErrorResponse orderParameterEmptyException(OrderParameterEmptyException e) { + public ErrorResponse orderParameterEmptyException(OrderParameterEmptyException e, WebRequest request) { + alarm(e, request); log.error("orderParameterEmptyException", e); return new ErrorResponse(e.getMessage(), ORDER_PARAMETER_EMPTY.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(OrderItemsEmptyException.class) - public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e) { + public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e, WebRequest request) { + alarm(e, request); log.error("orderItemsEmptyException", e); return new ErrorResponse(e.getMessage(), ORDER_ITEMS_EMPTY.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(DuplicateOrderException.class) - public ErrorResponse duplicateOrderException(DuplicateOrderException e) { + public ErrorResponse duplicateOrderException(DuplicateOrderException e, WebRequest request) { + alarm(e, request); log.error("duplicateOrderException", e); return new ErrorResponse(e.getMessage(), ErrorMessage.DUPLICATE_ORDER.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(OrderViewUnauthorizedException.class) - public ErrorResponse orderViewUnauthorizedException(OrderViewUnauthorizedException e) { + public ErrorResponse orderViewUnauthorizedException(OrderViewUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("orderViewUnauthorizedException", e); return new ErrorResponse(e.getMessage(), ORDER_VIEW_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(OrderNotFoundException.class) - public ErrorResponse orderNotFoundException(OrderNotFoundException e) { + public ErrorResponse orderNotFoundException(OrderNotFoundException e, WebRequest request) { + alarm(e, request); log.error("orderNotFoundException", e); return new ErrorResponse(e.getMessage(), ORDER_NOT_FOUND.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(OrderUpdateUnauthorizedException.class) - public ErrorResponse orderUpdateUnauthorizedException(OrderUpdateUnauthorizedException e) { + public ErrorResponse orderUpdateUnauthorizedException(OrderUpdateUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("orderUpdateUnauthorizedException", e); return new ErrorResponse(e.getMessage(), ORDER_UPDATE_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(ReservationNotFoundException.class) - public ErrorResponse reservationNotFoundException(ReservationNotFoundException e) { + public ErrorResponse reservationNotFoundException(ReservationNotFoundException e, WebRequest request) { + alarm(e, request); log.error("reservationNotFoundException", e); return new ErrorResponse(e.getMessage(), NOTFOUND_RESERVATION.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(ReservationViewUnauthorizedException.class) - public ErrorResponse reservationViewUnauthorizedException(ReservationViewUnauthorizedException e) { - log.error("reservationViewUnauthorizedException", e); + public ErrorResponse reservationViewUnauthorizedException(ReservationViewUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("reservation_viewUnauthorizedException", e); return new ErrorResponse(e.getMessage(), RESERVATION_VIEW_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(ReservationUpdateUnauthorizedException.class) - public ErrorResponse reservationUpdateUnauthorizedException(ReservationUpdateUnauthorizedException e) { + public ErrorResponse reservationUpdateUnauthorizedException(ReservationUpdateUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("reservationUpdateUnauthorizedException", e); return new ErrorResponse(e.getMessage(), RESERVATION_UPDATE_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuCreationUnauthorizedException.class) - public ErrorResponse menuCreationUnauthorizedException(MenuCreationUnauthorizedException e) { + public ErrorResponse menuCreationUnauthorizedException(MenuCreationUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("menuCreationUnauthorizedException", e); return new ErrorResponse(e.getMessage(), MENU_CREATION_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuViewUnauthorizedException.class) - public ErrorResponse menuViewUnauthorizedException(MenuViewUnauthorizedException e) { + public ErrorResponse menuViewUnauthorizedException(MenuViewUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("menuViewUnauthorizedException", e); return new ErrorResponse(e.getMessage(), MENU_VIEW_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuUpdateUnauthorizedException.class) - public ErrorResponse menuUpdateUnauthorizedException(MenuUpdateUnauthorizedException e) { + public ErrorResponse menuUpdateUnauthorizedException(MenuUpdateUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("menuUpdateUnauthorizedException", e); return new ErrorResponse(e.getMessage(), MENU_UPDATE_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuDeleteUnauthorizedException.class) - public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedException e) { + public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("menuDeleteUnauthorizedException", e); return new ErrorResponse(e.getMessage(), MENU_DELETE_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(StoreViewUnauthorizedException.class) - public ErrorResponse storeViewUnauthorizedException(StoreViewUnauthorizedException e) { + public ErrorResponse storeViewUnauthorizedException(StoreViewUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("storeViewUnauthorizedException", e); return new ErrorResponse(e.getMessage(), STORE_VIEW_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(StoreUpdateUnauthorizedException.class) - public ErrorResponse storeUpdateUnauthorizedException(StoreUpdateUnauthorizedException e) { + public ErrorResponse storeUpdateUnauthorizedException(StoreUpdateUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("storeUpdateUnauthorizedException", e); return new ErrorResponse(e.getMessage(), STORE_UPDATE_UNAUTHORIZED.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(StoreDeleteUnauthorizedException.class) - public ErrorResponse storeDeleteUnauthorizedException(StoreDeleteUnauthorizedException e) { + public ErrorResponse storeDeleteUnauthorizedException(StoreDeleteUnauthorizedException e, WebRequest request) { + alarm(e, request); log.error("storeDeleteUnauthorizedException", e); return new ErrorResponse(e.getMessage(), STORE_DELETE_UNAUTHORIZED.getCode()); } + // 공통 에러 Map 생성 private static Map getErrors(MethodArgumentNotValidException e) { return e.getBindingResult() .getAllErrors() @@ -235,5 +274,4 @@ private static Map getErrors(MethodArgumentNotValidException e) (msg1, msg2) -> msg1 + ";" + msg2 )); } - } diff --git a/nowait-app-user-api/build.gradle b/nowait-app-user-api/build.gradle index 1a9ce197..df5fb1ab 100644 --- a/nowait-app-user-api/build.gradle +++ b/nowait-app-user-api/build.gradle @@ -18,6 +18,16 @@ repositories { maven { url 'https://jitpack.io' } } +ext { + set('springCloudVersion', "2025.0.0") +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + dependencies { implementation project(':nowait-common') implementation project(':nowait-infra') // 사용자 관련 도메인 @@ -53,13 +63,13 @@ dependencies { // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' - // 로그 - implementation 'org.springframework.boot:spring-boot-starter-aop' - runtimeOnly 'org.fusesource.jansi:jansi:2.4.0' - // 디스코드 웹훅 implementation 'com.github.napstr:logback-discord-appender:1.0.0' + //feign + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + implementation group: 'io.github.openfeign', name: 'feign-gson', version: '11.0' + // 기타 필요 라이브러리 (예: Lombok) compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/nowait-app-user-api/src/main/java/com/nowait/ApiUserApplication.java b/nowait-app-user-api/src/main/java/com/nowait/ApiUserApplication.java index 07db6d34..a08337be 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/ApiUserApplication.java +++ b/nowait-app-user-api/src/main/java/com/nowait/ApiUserApplication.java @@ -2,9 +2,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @EnableJpaAuditing +@EnableFeignClients @SpringBootApplication public class ApiUserApplication { public static void main(String[] args) { diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java index 78a405c0..ef46c2a0 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java @@ -1,8 +1,11 @@ package com.nowait.applicationuser.exception; import static com.nowait.common.exception.ErrorMessage.*; +import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.UNAUTHORIZED; -import static org.springframework.http.HttpStatus.*; import java.util.Map; import java.util.stream.Collectors; @@ -16,18 +19,21 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartException; import com.nowait.applicationuser.security.exception.ResourceNotFoundException; import com.nowait.applicationuser.security.exception.UnauthorizedException; import com.nowait.common.exception.ErrorMessage; import com.nowait.common.exception.ErrorResponse; +import com.nowait.discord.service.DiscordAlarmService; import com.nowait.domaincorerdb.order.exception.DepositorNameTooLongException; import com.nowait.domaincorerdb.order.exception.DuplicateOrderException; import com.nowait.domaincorerdb.order.exception.OrderItemsEmptyException; import com.nowait.domaincorerdb.order.exception.OrderParameterEmptyException; import com.nowait.domaincorerdb.reservation.exception.DuplicateReservationException; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; +import com.nowait.domaincorerdb.store.exception.StoreNotFoundException; import com.nowait.domaincorerdb.store.exception.StoreWaitingDisabledException; import com.nowait.domaincorerdb.token.exception.BusinessException; import com.nowait.domaincorerdb.user.exception.UserNotFoundException; @@ -35,150 +41,184 @@ import com.nowait.domainuserrdb.bookmark.exception.DuplicateBookmarkException; import io.swagger.v3.oas.annotations.Hidden; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Hidden +@RequiredArgsConstructor @RestControllerAdvice public class GlobalExceptionHandler { - // OAUTH 인증 실패 에러처리 메서드 - @ResponseStatus(value = BAD_REQUEST) + private final DiscordAlarmService discordAlarmService; + + // Discord 알림 헬퍼 + private void alarm(Exception e, WebRequest request) { + discordAlarmService.sendDiscordUserAlarm(e, request); + } + + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(OAuth2AuthenticationException.class) - public ErrorResponse handlerOAuth2AuthenticationException(OAuth2AuthenticationException e) { + public ErrorResponse handlerOAuth2AuthenticationException(OAuth2AuthenticationException e, WebRequest request) { + alarm(e, request); log.error("handleOAuth2AuthenticationException", e); - return new ErrorResponse("OAuth 인증 실패 : " + e.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(BusinessException.class) - public ErrorResponse handleBusinessException(BusinessException e) { + public ErrorResponse handleBusinessException(BusinessException e, WebRequest request) { + alarm(e, request); log.error("handleBusinessException", e); return new ErrorResponse(e.getMessage(), e.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) - public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e, WebRequest request) { + alarm(e, request); log.error("handleMethodArgumentNotValidException", e); Map errors = getErrors(e); return new ErrorResponse(INVALID_INPUT_VALUE.getMessage(), INVALID_INPUT_VALUE.getCode(), errors); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) - public ErrorResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + public ErrorResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e, WebRequest request) { + alarm(e, request); log.error("handleHttpMessageNotReadableException", e); return new ErrorResponse(INVALID_INPUT_VALUE.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(IllegalArgumentException.class) - public ErrorResponse handleIllegalArgumentException(IllegalArgumentException e) { + public ErrorResponse handleIllegalArgumentException(IllegalArgumentException e, WebRequest request) { + alarm(e, request); log.error("handleIllegalArgumentException", e); return new ErrorResponse(e.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MissingRequestValueException.class) - public ErrorResponse handleMissingRequestValueException(MissingRequestValueException e) { - log.error("handleMissingRequestValueExceptionException", e); + public ErrorResponse handleMissingRequestValueException(MissingRequestValueException e, WebRequest request) { + alarm(e, request); + log.error("handleMissingRequestValueException", e); return new ErrorResponse(INVALID_INPUT_VALUE.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = UNAUTHORIZED) + @ResponseStatus(UNAUTHORIZED) @ExceptionHandler(UnauthorizedException.class) - public ErrorResponse handleUnauthorizedException(UnauthorizedException e) { - log.error("handleUnauthorizedExceptionException", e); + public ErrorResponse handleUnauthorizedException(UnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("handleUnauthorizedException", e); return new ErrorResponse(e.getMessage(), e.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(ResourceNotFoundException.class) - public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException e) { - log.error("handleResourceNotFoundExceptionException", e); + public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException e, WebRequest request) { + alarm(e, request); + log.error("handleResourceNotFoundException", e); return new ErrorResponse(e.getMessage(), e.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MultipartException.class) - public ErrorResponse handleMultipartException(MultipartException e) { + public ErrorResponse handleMultipartException(MultipartException e, WebRequest request) { + alarm(e, request); log.error("handleMultipartException", e); return new ErrorResponse(e.getMessage(), INVALID_INPUT_VALUE.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(DuplicateBookmarkException.class) - public ErrorResponse handleDuplicateBookmarkException(DuplicateBookmarkException e) { + public ErrorResponse handleDuplicateBookmarkException(DuplicateBookmarkException e, WebRequest request) { + alarm(e, request); log.error("handleDuplicateBookmarkException", e); return new ErrorResponse(e.getMessage(), ErrorMessage.DUPLICATE_BOOKMARK.getCode()); } - @ResponseStatus(value = FORBIDDEN) + @ResponseStatus(FORBIDDEN) @ExceptionHandler(BookmarkOwnerMismatchException.class) - public ErrorResponse bookmarkOwnerMismatchException(BookmarkOwnerMismatchException e) { + public ErrorResponse bookmarkOwnerMismatchException(BookmarkOwnerMismatchException e, WebRequest request) { + alarm(e, request); log.error("bookmarkOwnerMismatchException", e); return new ErrorResponse(e.getMessage(), NOT_OWN_BOOKMARK.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(UserNotFoundException.class) - public ErrorResponse userNotFoundException(UserNotFoundException e) { + public ErrorResponse userNotFoundException(UserNotFoundException e, WebRequest request) { + alarm(e, request); log.error("userNotFoundException", e); return new ErrorResponse(e.getMessage(), NOTFOUND_USER.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(OrderParameterEmptyException.class) - public ErrorResponse orderParameterEmptyException(OrderParameterEmptyException e) { + public ErrorResponse orderParameterEmptyException(OrderParameterEmptyException e, WebRequest request) { + alarm(e, request); log.error("orderParameterEmptyException", e); return new ErrorResponse(e.getMessage(), ORDER_PARAMETER_EMPTY.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(OrderItemsEmptyException.class) - public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e) { + public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e, WebRequest request) { + alarm(e, request); log.error("orderItemsEmptyException", e); return new ErrorResponse(e.getMessage(), ORDER_ITEMS_EMPTY.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(DepositorNameTooLongException.class) - public ErrorResponse depositorNameTooLongException(DepositorNameTooLongException e) { + public ErrorResponse depositorNameTooLongException(DepositorNameTooLongException e, WebRequest request) { + alarm(e, request); log.error("depositorNameTooLongException", e); return new ErrorResponse(e.getMessage(), DEPOSITOR_NAME_TOO_LONG.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(DuplicateOrderException.class) - public ErrorResponse duplicateOrderException(DuplicateOrderException e) { + public ErrorResponse duplicateOrderException(DuplicateOrderException e, WebRequest request) { + alarm(e, request); log.error("duplicateOrderException", e); return new ErrorResponse(e.getMessage(), ErrorMessage.DUPLICATE_ORDER.getCode()); } - @ResponseStatus(value = NOT_FOUND) + @ResponseStatus(NOT_FOUND) @ExceptionHandler(ReservationNotFoundException.class) - public ErrorResponse reservationNotFoundException(ReservationNotFoundException e) { + public ErrorResponse reservationNotFoundException(ReservationNotFoundException e, WebRequest request) { + alarm(e, request); log.error("reservationNotFoundException", e); return new ErrorResponse(e.getMessage(), NOTFOUND_RESERVATION.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(DuplicateReservationException.class) - public ErrorResponse duplicateReservationException(DuplicateReservationException e) { + public ErrorResponse duplicateReservationException(DuplicateReservationException e, WebRequest request) { + alarm(e, request); log.error("duplicateReservationException", e); return new ErrorResponse(e.getMessage(), DUPLICATE_RESERVATION.getCode()); } - @ResponseStatus(value = BAD_REQUEST) + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(StoreWaitingDisabledException.class) - public ErrorResponse storeWaitingDisabledException(StoreWaitingDisabledException e) { + public ErrorResponse storeWaitingDisabledException(StoreWaitingDisabledException e, WebRequest request) { + alarm(e, request); log.error("storeWaitingDisabledException", e); return new ErrorResponse(e.getMessage(), STORE_WAITING_DISABLED.getCode()); } + @ResponseStatus(NOT_FOUND) + @ExceptionHandler(StoreNotFoundException.class) + public ErrorResponse handleStoreNotFoundException(StoreNotFoundException e, WebRequest request) { + alarm(e, request); + log.error("handleStoreNotFoundException", e); + return new ErrorResponse(e.getMessage(), STORE_NOT_FOUND.getCode()); + } + // 공통 에러 Map 생성 private static Map getErrors(MethodArgumentNotValidException e) { return e.getBindingResult() .getAllErrors() @@ -190,5 +230,4 @@ private static Map getErrors(MethodArgumentNotValidException e) (msg1, msg2) -> msg1 + ";" + msg2 )); } - } diff --git a/nowait-infra/build.gradle b/nowait-infra/build.gradle index 5cbc1f17..ce38b13d 100644 --- a/nowait-infra/build.gradle +++ b/nowait-infra/build.gradle @@ -20,17 +20,36 @@ repositories { mavenCentral() } +ext { + set('springCloudVersion', "2025.0.0") +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + + dependencies { // Multipart File Upload // 추후 분리 필요 implementation 'org.springframework.boot:spring-boot-starter-web' + // 비동기 실행 implementation 'org.springframework.boot:spring-boot-starter-aop' + // S3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + // Resilience4j implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0' implementation 'io.github.resilience4j:resilience4j-bulkhead:2.3.0' + + //feign + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + implementation group: 'io.github.openfeign', name: 'feign-gson', version: '11.0' + // lombok compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' diff --git a/nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientAdmin.java b/nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientAdmin.java new file mode 100644 index 00000000..7d9adabd --- /dev/null +++ b/nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientAdmin.java @@ -0,0 +1,18 @@ +package com.nowait.discord.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import com.nowait.discord.config.DiscordFeignConfiguration; +import com.nowait.discord.dto.DiscordMessage; + +@FeignClient( + name = "discordClientAdmin", + contextId = "discordClientAdmin", + url = "https://discord.com/api/webhooks/1399070629994565653/Cy1_421v3-rN7U_7mKO7tcbwnGxS6Ufmf-T-uorAp8Bn0EoqDlrnKKvaf91PftxD1fRi", + configuration = DiscordFeignConfiguration.class) +public interface DiscordClientAdmin { + @PostMapping() + void sendAlarm(@RequestBody DiscordMessage message); +} diff --git a/nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientUser.java b/nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientUser.java new file mode 100644 index 00000000..a295e740 --- /dev/null +++ b/nowait-infra/src/main/java/com/nowait/discord/client/DiscordClientUser.java @@ -0,0 +1,18 @@ +package com.nowait.discord.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import com.nowait.discord.config.DiscordFeignConfiguration; +import com.nowait.discord.dto.DiscordMessage; + +@FeignClient( + name = "discordClientUser", + contextId = "discordClientUser", + url = "https://discord.com/api/webhooks/1399081071127171203/dvop__zPnQKCX-1VMJyBYnU_KTFNJFUwgGwta9D1Zy0xON0hKDcTW2ke9TQfnywzD2ll", + configuration = DiscordFeignConfiguration.class) +public interface DiscordClientUser { + @PostMapping() + void sendAlarm(@RequestBody DiscordMessage message); +} diff --git a/nowait-infra/src/main/java/com/nowait/discord/config/DiscordFeignConfiguration.java b/nowait-infra/src/main/java/com/nowait/discord/config/DiscordFeignConfiguration.java new file mode 100644 index 00000000..2c16cf7a --- /dev/null +++ b/nowait-infra/src/main/java/com/nowait/discord/config/DiscordFeignConfiguration.java @@ -0,0 +1,14 @@ +package com.nowait.discord.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import feign.Logger; + +@Configuration +public class DiscordFeignConfiguration { + @Bean + Logger.Level feignLoggerLevel(){ + return Logger.Level.FULL; + } +} diff --git a/nowait-infra/src/main/java/com/nowait/discord/dto/DiscordMessage.java b/nowait-infra/src/main/java/com/nowait/discord/dto/DiscordMessage.java new file mode 100644 index 00000000..8d3bd13c --- /dev/null +++ b/nowait-infra/src/main/java/com/nowait/discord/dto/DiscordMessage.java @@ -0,0 +1,29 @@ +package com.nowait.discord.dto; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class DiscordMessage { + + private String content; + private List embeds; + + @Builder + @AllArgsConstructor(access = AccessLevel.PROTECTED) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + @Getter + public static class Embed { + + private String title; + private String description; + } +} diff --git a/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java b/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java new file mode 100644 index 00000000..8eaa0e2e --- /dev/null +++ b/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java @@ -0,0 +1,75 @@ +package com.nowait.discord.service; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; + +import com.nowait.discord.client.DiscordClientAdmin; +import com.nowait.discord.dto.DiscordMessage; +import com.nowait.discord.client.DiscordClientUser; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Service +@Slf4j +@RequiredArgsConstructor +public class DiscordAlarmService { + + private final DiscordClientUser discordClientUser; + private final DiscordClientAdmin discordClientAdmin; + + public void sendDiscordUserAlarm(Exception e, WebRequest request) { + discordClientUser.sendAlarm(createMessage(e, request)); + } + + public void sendDiscordAdminAlarm(Exception e, WebRequest request) { + discordClientAdmin.sendAlarm(createMessage(e, request)); + } + + private DiscordMessage createMessage(Exception e, WebRequest request) { + return DiscordMessage.builder() + .content("# 🚨 에러 발생 비이이이이사아아아앙") + .embeds( + List.of( + DiscordMessage.Embed.builder() + .title("ℹ️ 에러 정보") + .description( + "### 🕖 발생 시간\n" + + LocalDateTime.now() + + "\n" + + "### 🔗 요청 URL\n" + + createRequestFullPath(request) + + "\n" + + "### 📄 Stack Trace\n" + + "```\n" + + getStackTrace(e).substring(0, 1000) + + "\n```") + .build() + ) + ) + .build(); + } + + private String createRequestFullPath(WebRequest webRequest) { + HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); + String fullPath = request.getMethod() + " " + request.getRequestURL(); + String queryString = request.getQueryString(); + if (queryString != null) { + fullPath += "?" + queryString; + } + return fullPath; + } + + private String getStackTrace(Exception e) { + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } +} From c31771dfed631e0aa5231ec64987a8ee8bfad91b Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Mon, 28 Jul 2025 11:28:30 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=EB=B0=B0=ED=8F=AC=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=97=90=EC=84=9C=EB=A7=8C=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EA=B0=80=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discord/service/DiscordAlarmService.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java b/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java index 8eaa0e2e..3e4e1425 100644 --- a/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java +++ b/nowait-infra/src/main/java/com/nowait/discord/service/DiscordAlarmService.java @@ -3,8 +3,10 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.List; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; @@ -24,41 +26,42 @@ public class DiscordAlarmService { private final DiscordClientUser discordClientUser; private final DiscordClientAdmin discordClientAdmin; + private final Environment environment; public void sendDiscordUserAlarm(Exception e, WebRequest request) { - discordClientUser.sendAlarm(createMessage(e, request)); + if (shouldNotifyUser()) { + discordClientUser.sendAlarm(createMessage(e, request)); + } } public void sendDiscordAdminAlarm(Exception e, WebRequest request) { - discordClientAdmin.sendAlarm(createMessage(e, request)); + if (shouldNotifyAdmin()) { + discordClientAdmin.sendAlarm(createMessage(e, request)); + } + } + + private boolean shouldNotifyAdmin() { + return Arrays.stream(environment.getActiveProfiles()).noneMatch("admin-local"::equals); + } + + private boolean shouldNotifyUser() { + return Arrays.stream(environment.getActiveProfiles()).noneMatch("user-local"::equals); } private DiscordMessage createMessage(Exception e, WebRequest request) { return DiscordMessage.builder() .content("# 🚨 에러 발생 비이이이이사아아아앙") - .embeds( - List.of( - DiscordMessage.Embed.builder() - .title("ℹ️ 에러 정보") - .description( - "### 🕖 발생 시간\n" - + LocalDateTime.now() - + "\n" - + "### 🔗 요청 URL\n" - + createRequestFullPath(request) - + "\n" - + "### 📄 Stack Trace\n" - + "```\n" - + getStackTrace(e).substring(0, 1000) - + "\n```") - .build() - ) - ) + .embeds(List.of(DiscordMessage.Embed.builder() + .title("ℹ️ 에러 정보") + .description( + "### 🕖 발생 시간\n" + LocalDateTime.now() + "\n" + "### 🔗 요청 URL\n" + createRequestFullPath(request) + + "\n" + "### 📄 Stack Trace\n" + "```\n" + getStackTrace(e).substring(0, 1000) + "\n```") + .build())) .build(); } private String createRequestFullPath(WebRequest webRequest) { - HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); + HttpServletRequest request = ((ServletWebRequest)webRequest).getRequest(); String fullPath = request.getMethod() + " " + request.getRequestURL(); String queryString = request.getQueryString(); if (queryString != null) {