From 58201bfb45eb205f19c9978a0793805ddb1f9906 Mon Sep 17 00:00:00 2001 From: JeongHoon Lee Date: Wed, 4 Jun 2025 01:59:44 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EC=95=8C=EB=A6=BC=20=EB=A1=9C=EC=A7=81=20=EB=8F=84?= =?UTF-8?q?=EC=9E=85=20=EB=B0=8F=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FontServiceImpl 내 폰트 생성 및 완료 시점에 이벤트 발행 로직 추가 - FontCreateRequestNotificationEvent - FontCreateCompleteNotificationEvent - SmsService 인터페이스 및 구현체에 관련 메서드 추가 - 통합 테스트(FontServiceIntegrationTest, FontControllerIntegrationTest)에서 이벤트 및 알림 로직 검증 추가 비동기 이벤트 기반으로 알림 로직을 분리하여 서비스 응답 속도 개선 및 관심사 분리 달성 --- .../fontorybe/font/CoolSmsEventListener.java | 48 +++++++++++++++++++ .../FontCreateCompleteNotificationEvent.java | 11 +++++ .../FontCreateRequestNotificationEvent.java | 12 +++++ .../font/service/FontServiceImpl.java | 17 +++---- .../sms/application/SmsServiceImpl.java | 16 +++---- .../sms/application/port/SmsService.java | 4 +- .../font/FontControllerIntegrationTest.java | 3 ++ .../font/FontServiceIntegrationTest.java | 4 ++ 8 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/fontory/fontorybe/font/CoolSmsEventListener.java create mode 100644 src/main/java/org/fontory/fontorybe/font/FontCreateCompleteNotificationEvent.java create mode 100644 src/main/java/org/fontory/fontorybe/font/FontCreateRequestNotificationEvent.java diff --git a/src/main/java/org/fontory/fontorybe/font/CoolSmsEventListener.java b/src/main/java/org/fontory/fontorybe/font/CoolSmsEventListener.java new file mode 100644 index 0000000..ff276af --- /dev/null +++ b/src/main/java/org/fontory/fontorybe/font/CoolSmsEventListener.java @@ -0,0 +1,48 @@ +package org.fontory.fontorybe.font; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.fontory.fontorybe.font.domain.Font; +import org.fontory.fontorybe.sms.application.port.PhoneNumberStorage; +import org.fontory.fontorybe.sms.application.port.SmsService; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CoolSmsEventListener { + + private final PhoneNumberStorage phoneNumberStorage; + private final SmsService smsService; + + @Async + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendFontCreateRequestNotificationAndSavePhoneNumber(FontCreateRequestNotificationEvent e) { + Font font = e.getFont(); + String phoneNumber = e.getPhoneNumber(); + + log.info("sms send start & save phone number in redis - fontId={}, phoneNumber={}", font.getId(), phoneNumber); + + phoneNumberStorage.savePhoneNumber(font, phoneNumber); + smsService.sendFontCreateRequestNotification(phoneNumber, font.getName()); + + log.info("sms sent & phone number saved in redis - fontId={}, phoneNumber={}", font.getId(), phoneNumber); + } + + @Async + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendFontCreateCompleteNotificationAndRemovePhoneNumber(FontCreateCompleteNotificationEvent e) { + Font font = e.getFont(); + String phoneNumber = phoneNumberStorage.getPhoneNumber(font); + + log.info("sms send start & save phone number in redis - fontId={}, phoneNumber={}", font.getId(), phoneNumber); + + smsService.sendFontCreateCompleteNotification(phoneNumber, font.getName()); + phoneNumberStorage.removePhoneNumber(font); + + log.info("sms sent & phone number saved in redis - fontId={}, phoneNumber={}", font.getId(), phoneNumber); + } +} diff --git a/src/main/java/org/fontory/fontorybe/font/FontCreateCompleteNotificationEvent.java b/src/main/java/org/fontory/fontorybe/font/FontCreateCompleteNotificationEvent.java new file mode 100644 index 0000000..4c927c4 --- /dev/null +++ b/src/main/java/org/fontory/fontorybe/font/FontCreateCompleteNotificationEvent.java @@ -0,0 +1,11 @@ +package org.fontory.fontorybe.font; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.fontory.fontorybe.font.domain.Font; + +@Getter +@RequiredArgsConstructor +public class FontCreateCompleteNotificationEvent { + private final Font font; +} diff --git a/src/main/java/org/fontory/fontorybe/font/FontCreateRequestNotificationEvent.java b/src/main/java/org/fontory/fontorybe/font/FontCreateRequestNotificationEvent.java new file mode 100644 index 0000000..1995bcb --- /dev/null +++ b/src/main/java/org/fontory/fontorybe/font/FontCreateRequestNotificationEvent.java @@ -0,0 +1,12 @@ +package org.fontory.fontorybe.font; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.fontory.fontorybe.font.domain.Font; + +@Getter +@RequiredArgsConstructor +public class FontCreateRequestNotificationEvent { + private final Font font; + private final String phoneNumber; +} diff --git a/src/main/java/org/fontory/fontorybe/font/service/FontServiceImpl.java b/src/main/java/org/fontory/fontorybe/font/service/FontServiceImpl.java index 1527097..60c77e6 100644 --- a/src/main/java/org/fontory/fontorybe/font/service/FontServiceImpl.java +++ b/src/main/java/org/fontory/fontorybe/font/service/FontServiceImpl.java @@ -10,6 +10,8 @@ import org.fontory.fontorybe.file.application.port.FileService; import org.fontory.fontorybe.file.domain.FileMetadata; import org.fontory.fontorybe.file.domain.FileUploadResult; +import org.fontory.fontorybe.font.FontCreateCompleteNotificationEvent; +import org.fontory.fontorybe.font.FontCreateRequestNotificationEvent; import org.fontory.fontorybe.font.controller.dto.FontCreateDTO; import org.fontory.fontorybe.font.controller.dto.FontDeleteResponse; import org.fontory.fontorybe.font.controller.dto.FontDownloadResponse; @@ -33,6 +35,7 @@ import org.fontory.fontorybe.member.domain.Member; import org.fontory.fontorybe.sms.application.port.PhoneNumberStorage; import org.fontory.fontorybe.sms.application.port.SmsService; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; @@ -51,8 +54,7 @@ public class FontServiceImpl implements FontService { private final FontRequestProducer fontRequestProducer; private final CloudStorageService cloudStorageService; private final BadWordFiltering badWordFiltering; - private final SmsService smsService; - private final PhoneNumberStorage phoneNumberStorage; + private final ApplicationEventPublisher eventPublisher; @Override @Transactional @@ -73,8 +75,8 @@ public Font create(Long memberId, FontCreateDTO fontCreateDTO, FileUploadResult fontRequestProducer.sendFontRequest(FontRequestProduceDto.from(savedFont, member, fontPaperUrl)); if (fontCreateDTO.getPhoneNumber() != null && !fontCreateDTO.getPhoneNumber().isBlank()) { - phoneNumberStorage.savePhoneNumber(savedFont, fontCreateDTO.getPhoneNumber()); - smsService.sendFontCreationNotification(fontCreateDTO.getPhoneNumber(), fontCreateDTO.getName()); + String notificationPhoneNumber = fontCreateDTO.getPhoneNumber(); + eventPublisher.publishEvent(new FontCreateRequestNotificationEvent(savedFont, notificationPhoneNumber)); } log.info("Service completed: Font created with ID: {} and Font template image uploaded successfully", savedFont.getId()); @@ -302,12 +304,7 @@ public FontUpdateResponse updateProgress(Long fontId, FontProgressUpdateDTO font String woff2Url = cloudStorageService.getWoff2Url(updatedFont.getKey()); if (fontProgressUpdateDTO.getStatus() == FontStatus.DONE) { - String phoneNumber = phoneNumberStorage.getPhoneNumber(targetFont); - - if (phoneNumber != null && !phoneNumber.isBlank()) { - smsService.sendFontProgressNotification(phoneNumber, updatedFont.getName()); - phoneNumberStorage.removePhoneNumber(targetFont); - } + eventPublisher.publishEvent(new FontCreateCompleteNotificationEvent(updatedFont)); } log.info("Service completed: Font ID: {} updated successfully", fontId); diff --git a/src/main/java/org/fontory/fontorybe/sms/application/SmsServiceImpl.java b/src/main/java/org/fontory/fontorybe/sms/application/SmsServiceImpl.java index 0e7b430..e703b14 100644 --- a/src/main/java/org/fontory/fontorybe/sms/application/SmsServiceImpl.java +++ b/src/main/java/org/fontory/fontorybe/sms/application/SmsServiceImpl.java @@ -15,7 +15,7 @@ public class SmsServiceImpl implements SmsService { @Value("${coolsms.phone-number}") - private String phoneNumber; + private String fromPhoneNumber; private final DefaultMessageService messageService; @@ -23,25 +23,25 @@ public class SmsServiceImpl implements SmsService { private static final String FONT_PROGRESS_MESSAGE_FORMAT = "[Fontory]\n\"%s\" 폰트 제작이 완료되었습니다."; @Override - public void sendFontCreationNotification(String to, String fontName) { + public void sendFontCreateRequestNotification(String to, String fontName) { sendSms(to, String.format(FONT_CREATION_MESSAGE_FORMAT, fontName)); } @Override - public void sendFontProgressNotification(String to, String fontName) { + public void sendFontCreateCompleteNotification(String to, String fontName) { sendSms(to, String.format(FONT_PROGRESS_MESSAGE_FORMAT, fontName)); } - private void sendSms(String to, String text) { - if (to == null || to.isBlank()) { + private void sendSms(String toPhoneNumber, String content) { + if (toPhoneNumber == null || toPhoneNumber.isBlank()) { log.warn("Service warning: recipient phone number is empty"); return; } Message message = new Message(); - message.setFrom(phoneNumber); - message.setTo(to); - message.setText(text); + message.setFrom(fromPhoneNumber); + message.setTo(toPhoneNumber); + message.setText(content); messageService.sendOne(new SingleMessageSendingRequest(message)); } diff --git a/src/main/java/org/fontory/fontorybe/sms/application/port/SmsService.java b/src/main/java/org/fontory/fontorybe/sms/application/port/SmsService.java index 408229d..9dac948 100644 --- a/src/main/java/org/fontory/fontorybe/sms/application/port/SmsService.java +++ b/src/main/java/org/fontory/fontorybe/sms/application/port/SmsService.java @@ -1,6 +1,6 @@ package org.fontory.fontorybe.sms.application.port; public interface SmsService { - void sendFontCreationNotification(String to, String fontName); - void sendFontProgressNotification(String to, String fontName); + void sendFontCreateRequestNotification(String to, String fontName); + void sendFontCreateCompleteNotification(String to, String fontName); } diff --git a/src/test/java/org/fontory/fontorybe/integration/font/FontControllerIntegrationTest.java b/src/test/java/org/fontory/fontorybe/integration/font/FontControllerIntegrationTest.java index 97861d6..b209b41 100644 --- a/src/test/java/org/fontory/fontorybe/integration/font/FontControllerIntegrationTest.java +++ b/src/test/java/org/fontory/fontorybe/integration/font/FontControllerIntegrationTest.java @@ -37,6 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.bean.override.mockito.MockitoBean; @@ -66,6 +67,8 @@ class FontControllerIntegrationTest { private FileService fileService; @MockitoBean private FontRequestProducer fontRequestProducer; + @MockitoBean + private ApplicationEventPublisher eventPublisher; private final Long existMemberId = 999L; private final String existMemberName = "existMemberNickName"; diff --git a/src/test/java/org/fontory/fontorybe/integration/font/FontServiceIntegrationTest.java b/src/test/java/org/fontory/fontorybe/integration/font/FontServiceIntegrationTest.java index 626ba03..9a124c9 100644 --- a/src/test/java/org/fontory/fontorybe/integration/font/FontServiceIntegrationTest.java +++ b/src/test/java/org/fontory/fontorybe/integration/font/FontServiceIntegrationTest.java @@ -28,6 +28,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.jdbc.Sql; @@ -43,6 +45,8 @@ class FontServiceIntegrationTest { private FileService fileService; @MockitoBean private FontRequestProducer fontRequestProducer; + @MockitoBean + private ApplicationEventPublisher eventPublisher; private final Long existMemberId = 999L; private final String existMemberName = "existMemberNickName";