diff --git a/.gitignore b/.gitignore index 9b1ac64..e6f58d9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,6 @@ out/ .vscode/ application.yml +application-dev.yml +application-prod.yml \n# Ignore environment-specific yml files\napplication-*.yml\n!application.yml diff --git a/src/main/java/com/example/umc9th/aop/ErrorNotifierAspect.java b/src/main/java/com/example/umc9th/aop/ErrorNotifierAspect.java new file mode 100644 index 0000000..5d569af --- /dev/null +++ b/src/main/java/com/example/umc9th/aop/ErrorNotifierAspect.java @@ -0,0 +1,30 @@ +package com.example.umc9th.aop; + +import com.example.umc9th.global.notification.NotificationService; +import com.example.umc9th.global.notification.NotificationType; +import com.example.umc9th.global.notification.Notifier; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +@Aspect +@Component +@Profile({"prod","staging"}) +@RequiredArgsConstructor +public class ErrorNotifierAspect extends CommonPointCut { + + private final NotificationService notifier; + + @AfterThrowing(pointcut = "controllerPointcut()", throwing = "e") + public void sendDiscordAlert(JoinPoint joinPoint, Exception e) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); + String requestURI = request.getRequestURI(); + notifier.send(NotificationType.DISCORD,e, requestURI); + } +} diff --git a/src/main/java/com/example/umc9th/config/discord/DevNotifierService.java b/src/main/java/com/example/umc9th/config/discord/DevNotifierService.java deleted file mode 100644 index 6bd8bc7..0000000 --- a/src/main/java/com/example/umc9th/config/discord/DevNotifierService.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example.umc9th.config.discord; - - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -@Service -@Profile("!prod & !staging") -public class DevNotifierService implements Notifier{ - @Override - public void sendNotification(Exception e, String requestUri) { - System.out.println("디스코드 알림 전송(로컬 확인용)"); - } -} diff --git a/src/main/java/com/example/umc9th/global/apiPayload/exception/ExceptionAdvice.java b/src/main/java/com/example/umc9th/global/apiPayload/exception/ExceptionAdvice.java index 956a281..012eb92 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/exception/ExceptionAdvice.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/exception/ExceptionAdvice.java @@ -1,7 +1,5 @@ package com.example.umc9th.global.apiPayload.exception; -import com.example.umc9th.config.discord.DiscordNotifierService; -import com.example.umc9th.config.discord.Notifier; import com.example.umc9th.global.apiPayload.ApiResponse; import com.example.umc9th.global.apiPayload.code.BaseErrorCode; import com.example.umc9th.global.apiPayload.code.status.GeneralErrorCode; @@ -9,7 +7,6 @@ // jakarta 임포트 import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -29,11 +26,8 @@ @Slf4j @RestControllerAdvice(annotations = {RestController.class}) -@RequiredArgsConstructor public class ExceptionAdvice extends ResponseEntityExceptionHandler { - private final Notifier notifier; - @ExceptionHandler public ResponseEntity validation(ConstraintViolationException e, WebRequest request) { String errorMessage = e.getConstraintViolations().stream() @@ -91,9 +85,6 @@ public ResponseEntity exception(Exception e, WebRequest request) { 결론: 서버에 기록을 남기고 장애를 추적하려면 반드시 log.error()를 사용해야 합니다. */ log.error("500 Error",e); - String requestUri = ((ServletWebRequest)request).getRequest().getRequestURI(); - notifier.sendNotification(e,requestUri); - return handleExceptionInternalFalse(e, GeneralErrorCode._INTERNAL_SERVER_ERROR.getHttpStatus(), request, e.getMessage()); } diff --git a/src/main/java/com/example/umc9th/config/discord/DiscordNotifierService.java b/src/main/java/com/example/umc9th/global/notification/DiscordNotifierService.java similarity index 94% rename from src/main/java/com/example/umc9th/config/discord/DiscordNotifierService.java rename to src/main/java/com/example/umc9th/global/notification/DiscordNotifierService.java index 3fae335..2c379b7 100644 --- a/src/main/java/com/example/umc9th/config/discord/DiscordNotifierService.java +++ b/src/main/java/com/example/umc9th/global/notification/DiscordNotifierService.java @@ -1,4 +1,4 @@ -package com.example.umc9th.config.discord; +package com.example.umc9th.global.notification; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; @@ -22,6 +22,11 @@ public class DiscordNotifierService implements Notifier{ @Value( "${discord.webhook-url}") private String discordWebhookUrl; + @Override + public NotificationType getType() { + return NotificationType.DISCORD; + } + @Async @Override public void sendNotification(Exception e, String requestUri) { diff --git a/src/main/java/com/example/umc9th/global/notification/NotificationService.java b/src/main/java/com/example/umc9th/global/notification/NotificationService.java new file mode 100644 index 0000000..55dbe24 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/notification/NotificationService.java @@ -0,0 +1,27 @@ +package com.example.umc9th.global.notification; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class NotificationService { + private final Map notifierMap; + + /** + * + * @param notifierList : 스프링이 NotificationService빈 등록시 Notifier 모든 구현체를 리스트로 주입함 + * 여러개의 Notifier구현체 중 개발자가 선택해서 사용하기 쉽도록 Map<타입,구현체>를 제공하는 서비스 클래스 + */ + public NotificationService(List notifierList) { + this.notifierMap = notifierList.stream() + .collect(Collectors.toMap(Notifier::getType, n -> n)); + } + + public void send(NotificationType type, Exception e, String requestUri) { + notifierMap.get(type).sendNotification(e, requestUri); + } +} diff --git a/src/main/java/com/example/umc9th/global/notification/NotificationType.java b/src/main/java/com/example/umc9th/global/notification/NotificationType.java new file mode 100644 index 0000000..42ecab0 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/notification/NotificationType.java @@ -0,0 +1,5 @@ +package com.example.umc9th.global.notification; + +public enum NotificationType { + DISCORD, SLACK +} diff --git a/src/main/java/com/example/umc9th/config/discord/Notifier.java b/src/main/java/com/example/umc9th/global/notification/Notifier.java similarity index 52% rename from src/main/java/com/example/umc9th/config/discord/Notifier.java rename to src/main/java/com/example/umc9th/global/notification/Notifier.java index e090961..47839aa 100644 --- a/src/main/java/com/example/umc9th/config/discord/Notifier.java +++ b/src/main/java/com/example/umc9th/global/notification/Notifier.java @@ -1,5 +1,6 @@ -package com.example.umc9th.config.discord; +package com.example.umc9th.global.notification; public interface Notifier { + NotificationType getType(); void sendNotification(Exception e, String requestUri); }