diff --git a/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java b/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java index 1b6238a1..12a9b6b4 100644 --- a/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java +++ b/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java @@ -10,8 +10,9 @@ import com.moing.backend.domain.teamMember.domain.service.TeamMemberSaveService; import com.moing.backend.domain.teamScore.application.mapper.TeamScoreMapper; import com.moing.backend.domain.teamScore.domain.service.TeamScoreSaveService; -import com.moing.backend.global.util.SlackService; +import com.moing.backend.global.config.slack.team.dto.TeamCreateEvent; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import javax.transaction.Transactional; @@ -27,7 +28,7 @@ public class CreateTeamUserCase { private final TeamMapper teamMapper; private final TeamScoreSaveService teamScoreSaveService; private final TeamScoreMapper teamScoreMapper; - private final SlackService slackService; + private final ApplicationEventPublisher eventPublisher; public CreateTeamResponse createTeam(CreateTeamRequest createTeamRequest, String socialId){ Member member = memberGetService.getMemberBySocialId(socialId); @@ -38,7 +39,7 @@ public CreateTeamResponse createTeam(CreateTeamRequest createTeamRequest, String team.approveTeam(); //====지워야 함 (테스트 용)===== teamScoreSaveService.save(teamScoreMapper.mapToTeamScore(team)); // 팀스코어 엔티티 생성 - slackService.sendSlackTeamCreatedMessage(team.getName(), team.getLeaderId()); + eventPublisher.publishEvent(new TeamCreateEvent(team.getName(), team.getLeaderId())); return new CreateTeamResponse(team.getTeamId()); } } diff --git a/src/main/java/com/moing/backend/global/config/slack/SlackConfig.java b/src/main/java/com/moing/backend/global/config/slack/SlackConfig.java new file mode 100644 index 00000000..9f3dbe0d --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/slack/SlackConfig.java @@ -0,0 +1,14 @@ +package com.moing.backend.global.config.slack; + +import com.slack.api.Slack; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SlackConfig { + + @Bean + public Slack slackClient() { + return Slack.getInstance(); + } +} \ No newline at end of file diff --git a/src/main/java/com/moing/backend/global/config/slack/exception/ExceptionEventHandler.java b/src/main/java/com/moing/backend/global/config/slack/exception/ExceptionEventHandler.java new file mode 100644 index 00000000..7466ba76 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/slack/exception/ExceptionEventHandler.java @@ -0,0 +1,21 @@ +package com.moing.backend.global.config.slack.exception; + +import com.moing.backend.global.config.slack.exception.dto.ExceptionEvent; +import com.moing.backend.global.config.slack.util.WebhookUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class ExceptionEventHandler { + + private final WebhookUtil webhookUtil; + + @Async("asyncTaskExecutor") + @EventListener + public void onExceptionEvent(ExceptionEvent event) { + webhookUtil.sendSlackAlertErrorLog(event.getRequest(), event.getException()); + } +} \ No newline at end of file diff --git a/src/main/java/com/moing/backend/global/config/slack/exception/dto/ExceptionEvent.java b/src/main/java/com/moing/backend/global/config/slack/exception/dto/ExceptionEvent.java new file mode 100644 index 00000000..b044d749 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/slack/exception/dto/ExceptionEvent.java @@ -0,0 +1,14 @@ +package com.moing.backend.global.config.slack.exception.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import javax.servlet.http.HttpServletRequest; + +@Getter +@AllArgsConstructor +public class ExceptionEvent { + + private final HttpServletRequest request; + private final Exception exception; +} diff --git a/src/main/java/com/moing/backend/global/config/slack/team/TeamCreateHandler.java b/src/main/java/com/moing/backend/global/config/slack/team/TeamCreateHandler.java new file mode 100644 index 00000000..0985a3bc --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/slack/team/TeamCreateHandler.java @@ -0,0 +1,22 @@ +package com.moing.backend.global.config.slack.team; + +import com.moing.backend.global.config.slack.team.dto.TeamCreateEvent; +import com.moing.backend.global.config.slack.util.WebhookUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@RequiredArgsConstructor +@Component +public class TeamCreateHandler { + + private final WebhookUtil webhookUtil; + + @Async("asyncTaskExecutor") + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void onTeamCreateEvent(TeamCreateEvent event) { + webhookUtil.sendSlackTeamCreatedMessage(event.getTeamName(), event.getLeaderId()); + } +} diff --git a/src/main/java/com/moing/backend/global/config/slack/team/dto/TeamCreateEvent.java b/src/main/java/com/moing/backend/global/config/slack/team/dto/TeamCreateEvent.java new file mode 100644 index 00000000..7a896a62 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/slack/team/dto/TeamCreateEvent.java @@ -0,0 +1,13 @@ +package com.moing.backend.global.config.slack.team.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TeamCreateEvent { + + private final String teamName; + private final Long leaderId; + +} diff --git a/src/main/java/com/moing/backend/global/util/SlackService.java b/src/main/java/com/moing/backend/global/config/slack/util/SlackAdapter.java similarity index 84% rename from src/main/java/com/moing/backend/global/util/SlackService.java rename to src/main/java/com/moing/backend/global/config/slack/util/SlackAdapter.java index 17a7054b..2ce8f901 100644 --- a/src/main/java/com/moing/backend/global/util/SlackService.java +++ b/src/main/java/com/moing/backend/global/config/slack/util/SlackAdapter.java @@ -1,13 +1,12 @@ -package com.moing.backend.global.util; +package com.moing.backend.global.config.slack.util; import com.slack.api.Slack; import com.slack.api.model.Attachment; import com.slack.api.model.Field; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @@ -17,9 +16,10 @@ import static com.slack.api.webhook.WebhookPayloads.payload; -@Component +@RequiredArgsConstructor @Slf4j -public class SlackService { +@Component +public class SlackAdapter implements WebhookUtil { @Value("${webhook.slack.error_url}") private String errorWebhookUrl; @@ -29,8 +29,6 @@ public class SlackService { private final Slack slackClient = Slack.getInstance(); - // 공통 슬랙 메시지 전송 메서드 - @Async public void sendSlackMessage(String webhookUrl, String message, List attachments) { try { slackClient.send(webhookUrl, payload(p -> p @@ -42,16 +40,16 @@ public void sendSlackMessage(String webhookUrl, String message, List } } - // 슬랙 에러 알림 메서드 - public void sendSlackAlertErrorLog(Exception e, HttpServletRequest request) { + @Override + public void sendSlackAlertErrorLog(HttpServletRequest request, Exception e) { String message = "[500 에러가 발생했습니다.]"; List attachments = List.of(generateSlackErrorAttachment(e, request)); sendSlackMessage(errorWebhookUrl, message, attachments); } - // 슬랙 소모임 생성 알림 메서드 + @Override public void sendSlackTeamCreatedMessage(String teamName, Long leaderId) { - String message = String.format("새로운 소모임 '%s'이(가) 생성되었습니다!", teamName); + String message = String.format("[새로운 소모임 '%s'이(가) 생성되었습니다.]", teamName); List attachments = List.of(generateSlackTeamAttachment(teamName, leaderId)); sendSlackMessage(teamAlarmWebhookUrl, message, attachments); } @@ -76,7 +74,7 @@ private Attachment generateSlackErrorAttachment(Exception e, HttpServletRequest .title(requestTime + " 발생 에러 로그") .fields(List.of( generateSlackField("Request IP", xffHeader == null ? request.getRemoteAddr() : xffHeader), - generateSlackField("Request URL", request.getRequestURL() + " " + request.getMethod()), + generateSlackField("Request URL", request.getMethod() + " " + request.getRequestURL()), generateSlackField("Error Message", e.getMessage()) ) ) @@ -90,5 +88,4 @@ private Field generateSlackField(String title, String value) { .valueShortEnough(false) .build(); } - -} +} \ No newline at end of file diff --git a/src/main/java/com/moing/backend/global/config/slack/util/WebhookUtil.java b/src/main/java/com/moing/backend/global/config/slack/util/WebhookUtil.java new file mode 100644 index 00000000..7c613543 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/slack/util/WebhookUtil.java @@ -0,0 +1,10 @@ +package com.moing.backend.global.config.slack.util; + +import javax.servlet.http.HttpServletRequest; + +public interface WebhookUtil { + + void sendSlackAlertErrorLog(HttpServletRequest request, Exception e); + + void sendSlackTeamCreatedMessage(String teamName, Long leaderId); +} diff --git a/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java b/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java index 04298e10..d48461eb 100644 --- a/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java @@ -1,10 +1,12 @@ package com.moing.backend.global.exception; +import com.moing.backend.global.config.slack.exception.dto.ExceptionEvent; import com.moing.backend.global.response.ErrorCode; import com.moing.backend.global.response.ErrorResponse; -import com.moing.backend.global.util.SlackService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -12,7 +14,6 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.context.support.DefaultMessageSourceResolvable; import javax.servlet.http.HttpServletRequest; import java.util.function.Consumer; @@ -25,7 +26,7 @@ public class GlobalExceptionHandler { private static final String LOG_FORMAT = "Class : {}, Code : {}, Message : {}"; - private final SlackService slackService; + private final ApplicationEventPublisher eventPublisher; @ExceptionHandler(ApplicationException.class) public ResponseEntity handleApplicationException(ApplicationException ex) { @@ -57,7 +58,7 @@ public ResponseEntity httpRequestNotSupportedExceptionHandler(Htt @ExceptionHandler(Exception.class) public ResponseEntity internalServerErrorHandler(Exception ex, HttpServletRequest request) { - slackService.sendSlackAlertErrorLog(ex, request); + eventPublisher.publishEvent(new ExceptionEvent(request, ex)); return handleException(ex, ErrorCode.INTERNAL_SERVER_ERROR, ErrorCode.INTERNAL_SERVER_ERROR.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR, log::error); } diff --git a/src/test/java/com/moing/backend/config/CommonControllerTest.java b/src/test/java/com/moing/backend/config/CommonControllerTest.java index 893b51c5..50f698fa 100644 --- a/src/test/java/com/moing/backend/config/CommonControllerTest.java +++ b/src/test/java/com/moing/backend/config/CommonControllerTest.java @@ -8,7 +8,6 @@ import com.moing.backend.global.config.security.filter.JwtAuthenticationEntryPoint; import com.moing.backend.global.config.security.jwt.TokenUtil; import com.moing.backend.global.config.security.util.AuthenticationUtil; -import com.moing.backend.global.util.SlackService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -58,9 +57,6 @@ public class CommonControllerTest { @MockBean public MemberGetService memberQueryService; - @MockBean - private SlackService slackService; - @BeforeEach public void setUp(final WebApplicationContext context, final RestDocumentationContextProvider provider) throws Exception {