From 3ae5b701d22a1331097f57834f848859e9c7a5af Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sat, 29 Aug 2026 15:45:26 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20enum=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/makers/core/type/ServiceType.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 core/src/main/java/org/sopt/makers/core/type/ServiceType.java diff --git a/core/src/main/java/org/sopt/makers/core/type/ServiceType.java b/core/src/main/java/org/sopt/makers/core/type/ServiceType.java new file mode 100644 index 00000000..fb3fde33 --- /dev/null +++ b/core/src/main/java/org/sopt/makers/core/type/ServiceType.java @@ -0,0 +1,17 @@ +package org.sopt.makers.core.type; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ServiceType { + AUTH("auth"), + OFFICIAL("official"), + ADMIN("admin"), + APP("app"), + PLAYGROUND("playground"), + CREW("crew"); + + private final String value; +} From 8a1e61e021326cf92f247e00fe71801c6abd52aa Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sat, 29 Aug 2026 15:45:52 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20HTTP=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=97=90=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../clients/alarm/AlarmInstantSenderAdapter.java | 5 ++--- .../eventbridge/AlarmScheduleSenderAdapter.java | 3 ++- .../MeetingDemandNotificationSenderAdapter.java | 4 ++-- .../MeetingNotificationSenderAdapter.java | 4 ++-- .../notification/NotificationHttpClient.java | 13 +++++++------ .../sopt/makers/clients/push/PushSenderAdapter.java | 8 +++----- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java index 09660632..d6c05b8a 100644 --- a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java @@ -4,6 +4,7 @@ import java.util.Map; import lombok.RequiredArgsConstructor; import org.sopt.makers.clients.notification.NotificationHttpClient; +import org.sopt.makers.core.type.ServiceType; import org.sopt.makers.domain.admin.alarm.Alarm; import org.sopt.makers.domain.admin.alarm.AlarmLinkType; import org.sopt.makers.domain.admin.alarm.AlarmTargetType; @@ -18,14 +19,12 @@ public class AlarmInstantSenderAdapter implements AlarmInstantSenderPort { private final NotificationHttpClient notificationHttpClient; - private final AlarmProperty alarmProperty; @Override public void send(Alarm alarm) { try { Map body = buildBody(alarm); - notificationHttpClient.send( - alarmProperty.headerService(), alarm.target().sendAction().getValue(), body); + notificationHttpClient.send(ServiceType.ADMIN, alarm.target().sendAction().getValue(), body); } catch (RestClientException e) { throw new AlarmException(AlarmFailure.FAIL_SEND_ALARM); } diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java index 18b81362..853bc32c 100644 --- a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java @@ -8,6 +8,7 @@ import org.sopt.makers.clients.eventbridge.dto.AlarmScheduleEventBridgeBody; import org.sopt.makers.clients.eventbridge.dto.AlarmScheduleEventBridgeHeader; import org.sopt.makers.clients.eventbridge.dto.AlarmScheduleEventBridgeRequest; +import org.sopt.makers.core.type.ServiceType; import org.sopt.makers.domain.admin.alarm.Alarm; import org.sopt.makers.domain.admin.alarm.AlarmLinkType; import org.sopt.makers.domain.admin.alarm.exception.AlarmException; @@ -78,7 +79,7 @@ private String buildEventJson(Alarm alarm) throws JacksonException { .action(alarm.target().sendAction().getValue()) .xApiKey(alarmProperty.key()) .transactionId(UUID.randomUUID().toString()) - .service(alarmProperty.headerService()) + .service(ServiceType.ADMIN.getValue()) .build(); boolean isAppLink = AlarmLinkType.APP.equals(alarm.content().linkType()); diff --git a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java index 9b1cf333..78e216e7 100644 --- a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; +import org.sopt.makers.core.type.ServiceType; import org.sopt.makers.domain.crew.meeting.demand.notification.MeetingDemandNotification; import org.sopt.makers.domain.crew.meeting.demand.port.MeetingDemandNotificationSenderPort; import org.springframework.core.env.Environment; @@ -13,7 +14,6 @@ public class MeetingDemandNotificationSenderAdapter implements MeetingDemandNotificationSenderPort { private static final String SEND_ACTION = "send"; - private static final String SERVICE_NAME = "crew"; private static final String DEV_WEB_URL = "https://sopt-internal-dev.sopt.org/group"; private static final String PROD_WEB_URL = "https://playground.sopt.org/group"; @@ -22,7 +22,7 @@ public class MeetingDemandNotificationSenderAdapter implements MeetingDemandNoti @Override public void send(MeetingDemandNotification notification) { - notificationHttpClient.send(SERVICE_NAME, SEND_ACTION, buildBody(notification)); + notificationHttpClient.send(ServiceType.CREW, SEND_ACTION, buildBody(notification)); } private Map buildBody(MeetingDemandNotification notification) { diff --git a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java index 2e0882bc..e4b694e8 100644 --- a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; +import org.sopt.makers.core.type.ServiceType; import org.sopt.makers.domain.crew.notification.MeetingNotification; import org.sopt.makers.domain.crew.notification.exception.MeetingNotificationException; import org.sopt.makers.domain.crew.notification.port.MeetingNotificationSenderPort; @@ -17,7 +18,6 @@ public class MeetingNotificationSenderAdapter implements MeetingNotificationSenderPort { private static final String SEND_ACTION = "send"; - private static final String SERVICE_NAME = "crew"; private static final String DEV_WEB_URL = "https://sopt-internal-dev.sopt.org/group"; private static final String PROD_WEB_URL = "https://playground.sopt.org/group"; @@ -27,7 +27,7 @@ public class MeetingNotificationSenderAdapter implements MeetingNotificationSend @Override public void send(MeetingNotification notification) { try { - notificationHttpClient.send(SERVICE_NAME, SEND_ACTION, buildBody(notification)); + notificationHttpClient.send(ServiceType.CREW, SEND_ACTION, buildBody(notification)); } catch (RestClientException e) { throw new MeetingNotificationException(FAIL_SEND_MEETING_NOTIFICATION); } diff --git a/clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java b/clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java index 4ed5c68e..e10f8ad1 100644 --- a/clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java +++ b/clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java @@ -5,6 +5,7 @@ import java.util.UUID; import lombok.RequiredArgsConstructor; import org.sopt.makers.clients.alarm.AlarmProperty; +import org.sopt.makers.core.type.ServiceType; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -18,25 +19,25 @@ public class NotificationHttpClient { private final RestTemplate restTemplate; private final AlarmProperty alarmProperty; - public void send(String serviceName, String action, Object body) { - send(serviceName, action, body, null); + public void send(ServiceType serviceType, String action, Object body) { + send(serviceType, action, body, null); } - public void send(String serviceName, String action, Object body, String platform) { + public void send(ServiceType serviceType, String action, Object body, String platform) { restTemplate.postForEntity( alarmProperty.url(), - new HttpEntity<>(body, buildHeaders(serviceName, action, platform)), + new HttpEntity<>(body, buildHeaders(serviceType, action, platform)), Object.class); } - private HttpHeaders buildHeaders(String serviceName, String action, String platform) { + private HttpHeaders buildHeaders(ServiceType serviceType, String action, String platform) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8)); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.add("x-api-key", alarmProperty.key()); headers.add("action", action); headers.add("transactionId", UUID.randomUUID().toString()); - headers.add("service", serviceName); + headers.add("service", serviceType.getValue()); if (platform != null) { headers.add("platform", platform); } diff --git a/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java index d9f8bb1a..ec505765 100644 --- a/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java @@ -2,10 +2,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.sopt.makers.clients.alarm.AlarmProperty; import org.sopt.makers.clients.notification.NotificationHttpClient; import org.sopt.makers.clients.push.dto.PushSendRequest; import org.sopt.makers.clients.push.dto.PushTokenManageRequest; +import org.sopt.makers.core.type.ServiceType; import org.sopt.makers.domain.app.push.PushMessage; import org.sopt.makers.domain.app.push.PushToken; import org.sopt.makers.domain.app.push.PushTokenPlatform; @@ -25,7 +25,6 @@ public class PushSenderAdapter implements PushSenderPort { private static final String ACTION_DELETE = "cancel"; private final NotificationHttpClient notificationHttpClient; - private final AlarmProperty property; @Override public void send(PushMessage message) { @@ -33,8 +32,7 @@ public void send(PushMessage message) { return; } try { - notificationHttpClient.send( - property.appHeaderService(), ACTION_SEND, PushSendRequest.from(message)); + notificationHttpClient.send(ServiceType.APP, ACTION_SEND, PushSendRequest.from(message)); } catch (RestClientException e) { log.warn("푸시 발송 실패 - title={}, 대상 {}명", message.title(), message.userIds().size(), e); throw new PushException(PushFailure.FAIL_SEND_PUSH); @@ -54,7 +52,7 @@ public void delete(PushToken pushToken) { private void manageToken(String action, PushToken pushToken) { try { notificationHttpClient.send( - property.appHeaderService(), + ServiceType.APP, action, PushTokenManageRequest.from(pushToken), toPlatformHeader(pushToken.platform())); From 7e68a8f7b6517183933fb0d176844d2a87156d7f Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sat, 29 Aug 2026 15:46:04 +0900 Subject: [PATCH 03/11] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20service?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/main/resources/external.yml | 2 -- api/src/test/resources/application.yml | 2 -- .../java/org/sopt/makers/clients/alarm/AlarmProperty.java | 7 +------ .../sopt/makers/clients/push/PushSenderAdapterTest.java | 6 ++---- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/api/src/main/resources/external.yml b/api/src/main/resources/external.yml index 4bdaf577..266f7e66 100644 --- a/api/src/main/resources/external.yml +++ b/api/src/main/resources/external.yml @@ -22,8 +22,6 @@ external: url: ${ALARM_NOTIFICATION_URL} key: ${ALARM_NOTIFICATION_KEY} arn: ${ALARM_NOTIFICATION_ARN} - header-service: operation - app-header-service: app eventbridge: role-arn: ${AWS_EVENTBRIDGE_ROLE_ARN} region: ${AWS_REGION} diff --git a/api/src/test/resources/application.yml b/api/src/test/resources/application.yml index 63f1e1ff..23ed25e4 100644 --- a/api/src/test/resources/application.yml +++ b/api/src/test/resources/application.yml @@ -64,8 +64,6 @@ external: url: http://localhost key: test-alarm-key arn: arn:aws:scheduler:ap-northeast-2:000000000000:schedule/test - header-service: test - app-header-service: test-app eventbridge: role-arn: arn:aws:iam::000000000000:role/test region: ap-northeast-2 diff --git a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java b/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java index 9345be77..fc8553dd 100644 --- a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java +++ b/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java @@ -6,9 +6,4 @@ @Validated @ConfigurationProperties(prefix = "external.alarm") -public record AlarmProperty( - @NotBlank String url, - @NotBlank String key, - @NotBlank String arn, - @NotBlank String headerService, - @NotBlank String appHeaderService) {} +public record AlarmProperty(@NotBlank String url, @NotBlank String key, @NotBlank String arn) {} diff --git a/clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java b/clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java index afd0e88a..8a2e215e 100644 --- a/clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java +++ b/clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java @@ -31,10 +31,8 @@ void setUp() throws Exception { pushServer = new MockWebServer(); pushServer.start(); String baseUrl = pushServer.url("/").toString().replaceAll("/$", ""); - AlarmProperty property = - new AlarmProperty(baseUrl, "test-push-key", "test-arn", "operation", "app"); - adapter = - new PushSenderAdapter(new NotificationHttpClient(new RestTemplate(), property), property); + AlarmProperty property = new AlarmProperty(baseUrl, "test-push-key", "test-arn"); + adapter = new PushSenderAdapter(new NotificationHttpClient(new RestTemplate(), property)); } @AfterEach From 0afbffe562ebabf7f0e28931fab5678c10eb0e63 Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sun, 30 Aug 2026 13:32:00 +0900 Subject: [PATCH 04/11] =?UTF-8?q?refactor:=20push=20=EB=B0=9C=EC=86=A1=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alarm/AlarmInstantSenderAdapter.java | 52 -------- .../makers/clients/config/ClientsConfig.java | 4 +- .../AlarmScheduleSenderAdapter.java | 8 +- ...eetingDemandNotificationSenderAdapter.java | 41 ------- .../MeetingNotificationSenderAdapter.java | 49 -------- .../clients/push/PushClientAdapter.java | 116 ++++++++++++++++++ .../PushHttpClient.java} | 11 +- .../PushProperty.java} | 4 +- .../clients/push/PushSenderAdapter.java | 68 ---------- .../clients/push/dto/PushSendRequest.java | 47 ++++++- ...erTest.java => PushClientAdapterTest.java} | 15 +-- 11 files changed, 182 insertions(+), 233 deletions(-) delete mode 100644 clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java delete mode 100644 clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java delete mode 100644 clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java create mode 100644 clients/src/main/java/org/sopt/makers/clients/push/PushClientAdapter.java rename clients/src/main/java/org/sopt/makers/clients/{notification/NotificationHttpClient.java => push/PushHttpClient.java} (84%) rename clients/src/main/java/org/sopt/makers/clients/{alarm/AlarmProperty.java => push/PushProperty.java} (64%) delete mode 100644 clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java rename clients/src/test/java/org/sopt/makers/clients/push/{PushSenderAdapterTest.java => PushClientAdapterTest.java} (91%) diff --git a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java deleted file mode 100644 index d6c05b8a..00000000 --- a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmInstantSenderAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.sopt.makers.clients.alarm; - -import java.util.HashMap; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.sopt.makers.clients.notification.NotificationHttpClient; -import org.sopt.makers.core.type.ServiceType; -import org.sopt.makers.domain.admin.alarm.Alarm; -import org.sopt.makers.domain.admin.alarm.AlarmLinkType; -import org.sopt.makers.domain.admin.alarm.AlarmTargetType; -import org.sopt.makers.domain.admin.alarm.exception.AlarmException; -import org.sopt.makers.domain.admin.alarm.exception.AlarmFailure; -import org.sopt.makers.domain.admin.alarm.port.AlarmInstantSenderPort; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClientException; - -@Component -@RequiredArgsConstructor -public class AlarmInstantSenderAdapter implements AlarmInstantSenderPort { - - private final NotificationHttpClient notificationHttpClient; - - @Override - public void send(Alarm alarm) { - try { - Map body = buildBody(alarm); - notificationHttpClient.send(ServiceType.ADMIN, alarm.target().sendAction().getValue(), body); - } catch (RestClientException e) { - throw new AlarmException(AlarmFailure.FAIL_SEND_ALARM); - } - } - - private Map buildBody(Alarm alarm) { - Map body = new HashMap<>(); - body.put("title", alarm.content().title()); - body.put("content", alarm.content().content()); - body.put("category", alarm.content().category()); - - boolean isTargetAll = AlarmTargetType.ALL.equals(alarm.target().targetType()); - if (!isTargetAll) { - body.put("userIds", alarm.target().targetIds()); - } - - AlarmLinkType linkType = alarm.content().linkType(); - if (AlarmLinkType.WEB.equals(linkType)) { - body.put("webLink", alarm.content().linkPath()); - } else if (AlarmLinkType.APP.equals(linkType)) { - body.put("deepLink", alarm.content().linkPath()); - } - return body; - } -} diff --git a/clients/src/main/java/org/sopt/makers/clients/config/ClientsConfig.java b/clients/src/main/java/org/sopt/makers/clients/config/ClientsConfig.java index c43e397d..a9c974ea 100644 --- a/clients/src/main/java/org/sopt/makers/clients/config/ClientsConfig.java +++ b/clients/src/main/java/org/sopt/makers/clients/config/ClientsConfig.java @@ -1,8 +1,8 @@ package org.sopt.makers.clients.config; -import org.sopt.makers.clients.alarm.AlarmProperty; import org.sopt.makers.clients.crew.CrewProperty; import org.sopt.makers.clients.eventbridge.EventBridgeProperty; +import org.sopt.makers.clients.push.PushProperty; import org.sopt.makers.clients.s3.S3Property; import org.sopt.makers.clients.sms.GabiaSmsProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -15,7 +15,7 @@ OAuthProperty.class, GabiaSmsProperty.class, S3Property.class, - AlarmProperty.class, + PushProperty.class, EventBridgeProperty.class, CrewProperty.class }) diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java index 853bc32c..aa6581eb 100644 --- a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java @@ -4,10 +4,10 @@ import java.time.format.DateTimeFormatter; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.sopt.makers.clients.alarm.AlarmProperty; import org.sopt.makers.clients.eventbridge.dto.AlarmScheduleEventBridgeBody; import org.sopt.makers.clients.eventbridge.dto.AlarmScheduleEventBridgeHeader; import org.sopt.makers.clients.eventbridge.dto.AlarmScheduleEventBridgeRequest; +import org.sopt.makers.clients.push.PushProperty; import org.sopt.makers.core.type.ServiceType; import org.sopt.makers.domain.admin.alarm.Alarm; import org.sopt.makers.domain.admin.alarm.AlarmLinkType; @@ -32,7 +32,7 @@ public class AlarmScheduleSenderAdapter implements AlarmScheduleSenderPort { private final SchedulerClient schedulerClient; private final ObjectMapper objectMapper; - private final AlarmProperty alarmProperty; + private final PushProperty pushProperty; private final EventBridgeProperty eventBridgeProperty; @Override @@ -77,7 +77,7 @@ private String buildEventJson(Alarm alarm) throws JacksonException { AlarmScheduleEventBridgeHeader.builder() .alarmId(alarm.id()) .action(alarm.target().sendAction().getValue()) - .xApiKey(alarmProperty.key()) + .xApiKey(pushProperty.key()) .transactionId(UUID.randomUUID().toString()) .service(ServiceType.ADMIN.getValue()) .build(); @@ -101,7 +101,7 @@ private String buildEventJson(Alarm alarm) throws JacksonException { private Target buildTarget(String eventJson) { return Target.builder() .roleArn(eventBridgeProperty.roleArn()) - .arn(alarmProperty.arn()) + .arn(pushProperty.arn()) .input(eventJson) .build(); } diff --git a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java deleted file mode 100644 index 78e216e7..00000000 --- a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingDemandNotificationSenderAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.sopt.makers.clients.notification; - -import java.util.HashMap; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.sopt.makers.core.type.ServiceType; -import org.sopt.makers.domain.crew.meeting.demand.notification.MeetingDemandNotification; -import org.sopt.makers.domain.crew.meeting.demand.port.MeetingDemandNotificationSenderPort; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class MeetingDemandNotificationSenderAdapter implements MeetingDemandNotificationSenderPort { - - private static final String SEND_ACTION = "send"; - private static final String DEV_WEB_URL = "https://sopt-internal-dev.sopt.org/group"; - private static final String PROD_WEB_URL = "https://playground.sopt.org/group"; - - private final NotificationHttpClient notificationHttpClient; - private final Environment environment; - - @Override - public void send(MeetingDemandNotification notification) { - notificationHttpClient.send(ServiceType.CREW, SEND_ACTION, buildBody(notification)); - } - - private Map buildBody(MeetingDemandNotification notification) { - Map body = new HashMap<>(); - body.put("userIds", notification.userIds().stream().map(String::valueOf).toList()); - body.put("title", notification.title()); - body.put("content", notification.content()); - body.put("category", notification.category()); - body.put("webLink", getWebUrl() + notification.webPath()); - return body; - } - - private String getWebUrl() { - return environment.matchesProfiles("prod") ? PROD_WEB_URL : DEV_WEB_URL; - } -} diff --git a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java deleted file mode 100644 index e4b694e8..00000000 --- a/clients/src/main/java/org/sopt/makers/clients/notification/MeetingNotificationSenderAdapter.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.sopt.makers.clients.notification; - -import static org.sopt.makers.domain.crew.notification.exception.MeetingNotificationFailure.FAIL_SEND_MEETING_NOTIFICATION; - -import java.util.HashMap; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.sopt.makers.core.type.ServiceType; -import org.sopt.makers.domain.crew.notification.MeetingNotification; -import org.sopt.makers.domain.crew.notification.exception.MeetingNotificationException; -import org.sopt.makers.domain.crew.notification.port.MeetingNotificationSenderPort; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClientException; - -@Component -@RequiredArgsConstructor -public class MeetingNotificationSenderAdapter implements MeetingNotificationSenderPort { - - private static final String SEND_ACTION = "send"; - private static final String DEV_WEB_URL = "https://sopt-internal-dev.sopt.org/group"; - private static final String PROD_WEB_URL = "https://playground.sopt.org/group"; - - private final NotificationHttpClient notificationHttpClient; - private final Environment environment; - - @Override - public void send(MeetingNotification notification) { - try { - notificationHttpClient.send(ServiceType.CREW, SEND_ACTION, buildBody(notification)); - } catch (RestClientException e) { - throw new MeetingNotificationException(FAIL_SEND_MEETING_NOTIFICATION); - } - } - - private Map buildBody(MeetingNotification notification) { - Map body = new HashMap<>(); - body.put("userIds", notification.userIds().stream().map(String::valueOf).toList()); - body.put("title", notification.title()); - body.put("content", notification.content()); - body.put("category", notification.category()); - body.put("webLink", getWebUrl() + "/detail?id=" + notification.meetingId()); - return body; - } - - private String getWebUrl() { - return environment.matchesProfiles("prod") ? PROD_WEB_URL : DEV_WEB_URL; - } -} diff --git a/clients/src/main/java/org/sopt/makers/clients/push/PushClientAdapter.java b/clients/src/main/java/org/sopt/makers/clients/push/PushClientAdapter.java new file mode 100644 index 00000000..59072976 --- /dev/null +++ b/clients/src/main/java/org/sopt/makers/clients/push/PushClientAdapter.java @@ -0,0 +1,116 @@ +package org.sopt.makers.clients.push; + +import static org.sopt.makers.domain.crew.notification.exception.MeetingNotificationFailure.FAIL_SEND_MEETING_NOTIFICATION; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.sopt.makers.clients.push.dto.PushSendRequest; +import org.sopt.makers.clients.push.dto.PushTokenManageRequest; +import org.sopt.makers.core.type.ServiceType; +import org.sopt.makers.domain.admin.alarm.Alarm; +import org.sopt.makers.domain.admin.alarm.exception.AlarmException; +import org.sopt.makers.domain.admin.alarm.exception.AlarmFailure; +import org.sopt.makers.domain.admin.alarm.port.AlarmInstantSenderPort; +import org.sopt.makers.domain.app.push.PushMessage; +import org.sopt.makers.domain.app.push.PushToken; +import org.sopt.makers.domain.app.push.PushTokenPlatform; +import org.sopt.makers.domain.app.push.exception.PushException; +import org.sopt.makers.domain.app.push.exception.PushFailure; +import org.sopt.makers.domain.app.push.port.PushSenderPort; +import org.sopt.makers.domain.crew.meeting.demand.notification.MeetingDemandNotification; +import org.sopt.makers.domain.crew.meeting.demand.port.MeetingDemandNotificationSenderPort; +import org.sopt.makers.domain.crew.notification.MeetingNotification; +import org.sopt.makers.domain.crew.notification.exception.MeetingNotificationException; +import org.sopt.makers.domain.crew.notification.port.MeetingNotificationSenderPort; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClientException; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PushClientAdapter + implements AlarmInstantSenderPort, + PushSenderPort, + MeetingNotificationSenderPort, + MeetingDemandNotificationSenderPort { + + private static final String ACTION_SEND = "send"; + private static final String ACTION_REGISTER = "register"; + private static final String ACTION_DELETE = "cancel"; + private static final String DEV_CREW_WEB_URL = "https://sopt-internal-dev.sopt.org/group"; + private static final String PROD_CREW_WEB_URL = "https://playground.sopt.org/group"; + + private final PushHttpClient pushHttpClient; + private final Environment environment; + + @Override + public void send(Alarm alarm) { + try { + pushHttpClient.send( + ServiceType.ADMIN, alarm.target().sendAction().getValue(), PushSendRequest.from(alarm)); + } catch (RestClientException e) { + throw new AlarmException(AlarmFailure.FAIL_SEND_ALARM); + } + } + + @Override + public void send(PushMessage message) { + if (message.userIds().isEmpty()) { + return; + } + try { + pushHttpClient.send(ServiceType.APP, ACTION_SEND, PushSendRequest.from(message)); + } catch (RestClientException e) { + log.warn("푸시 발송 실패 - title={}, 대상 {}명", message.title(), message.userIds().size(), e); + throw new PushException(PushFailure.FAIL_SEND_PUSH); + } + } + + @Override + public void register(PushToken pushToken) { + manageToken(ACTION_REGISTER, pushToken); + } + + @Override + public void delete(PushToken pushToken) { + manageToken(ACTION_DELETE, pushToken); + } + + @Override + public void send(MeetingNotification notification) { + try { + pushHttpClient.send( + ServiceType.CREW, ACTION_SEND, PushSendRequest.from(notification, getCrewWebUrl())); + } catch (RestClientException e) { + throw new MeetingNotificationException(FAIL_SEND_MEETING_NOTIFICATION); + } + } + + @Override + public void send(MeetingDemandNotification notification) { + pushHttpClient.send( + ServiceType.CREW, ACTION_SEND, PushSendRequest.from(notification, getCrewWebUrl())); + } + + private void manageToken(String action, PushToken pushToken) { + try { + pushHttpClient.send( + ServiceType.APP, + action, + PushTokenManageRequest.from(pushToken), + toPlatformHeader(pushToken.platform())); + } catch (RestClientException e) { + log.warn("푸시 토큰 {} 실패 - userId={}", action, pushToken.userId(), e); + throw new PushException(PushFailure.FAIL_MANAGE_PUSH_TOKEN); + } + } + + private String toPlatformHeader(PushTokenPlatform platform) { + return platform == PushTokenPlatform.IOS ? "iOS" : "Android"; + } + + private String getCrewWebUrl() { + return environment.matchesProfiles("prod") ? PROD_CREW_WEB_URL : DEV_CREW_WEB_URL; + } +} diff --git a/clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java b/clients/src/main/java/org/sopt/makers/clients/push/PushHttpClient.java similarity index 84% rename from clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java rename to clients/src/main/java/org/sopt/makers/clients/push/PushHttpClient.java index e10f8ad1..0d82d4b2 100644 --- a/clients/src/main/java/org/sopt/makers/clients/notification/NotificationHttpClient.java +++ b/clients/src/main/java/org/sopt/makers/clients/push/PushHttpClient.java @@ -1,10 +1,9 @@ -package org.sopt.makers.clients.notification; +package org.sopt.makers.clients.push; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.sopt.makers.clients.alarm.AlarmProperty; import org.sopt.makers.core.type.ServiceType; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -14,10 +13,10 @@ @Component @RequiredArgsConstructor -public class NotificationHttpClient { +public class PushHttpClient { private final RestTemplate restTemplate; - private final AlarmProperty alarmProperty; + private final PushProperty pushProperty; public void send(ServiceType serviceType, String action, Object body) { send(serviceType, action, body, null); @@ -25,7 +24,7 @@ public void send(ServiceType serviceType, String action, Object body) { public void send(ServiceType serviceType, String action, Object body, String platform) { restTemplate.postForEntity( - alarmProperty.url(), + pushProperty.url(), new HttpEntity<>(body, buildHeaders(serviceType, action, platform)), Object.class); } @@ -34,7 +33,7 @@ private HttpHeaders buildHeaders(ServiceType serviceType, String action, String HttpHeaders headers = new HttpHeaders(); headers.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8)); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.add("x-api-key", alarmProperty.key()); + headers.add("x-api-key", pushProperty.key()); headers.add("action", action); headers.add("transactionId", UUID.randomUUID().toString()); headers.add("service", serviceType.getValue()); diff --git a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java b/clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java similarity index 64% rename from clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java rename to clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java index fc8553dd..139e0f94 100644 --- a/clients/src/main/java/org/sopt/makers/clients/alarm/AlarmProperty.java +++ b/clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java @@ -1,4 +1,4 @@ -package org.sopt.makers.clients.alarm; +package org.sopt.makers.clients.push; import jakarta.validation.constraints.NotBlank; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -6,4 +6,4 @@ @Validated @ConfigurationProperties(prefix = "external.alarm") -public record AlarmProperty(@NotBlank String url, @NotBlank String key, @NotBlank String arn) {} +public record PushProperty(@NotBlank String url, @NotBlank String key, @NotBlank String arn) {} diff --git a/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java deleted file mode 100644 index ec505765..00000000 --- a/clients/src/main/java/org/sopt/makers/clients/push/PushSenderAdapter.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.sopt.makers.clients.push; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.sopt.makers.clients.notification.NotificationHttpClient; -import org.sopt.makers.clients.push.dto.PushSendRequest; -import org.sopt.makers.clients.push.dto.PushTokenManageRequest; -import org.sopt.makers.core.type.ServiceType; -import org.sopt.makers.domain.app.push.PushMessage; -import org.sopt.makers.domain.app.push.PushToken; -import org.sopt.makers.domain.app.push.PushTokenPlatform; -import org.sopt.makers.domain.app.push.exception.PushException; -import org.sopt.makers.domain.app.push.exception.PushFailure; -import org.sopt.makers.domain.app.push.port.PushSenderPort; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClientException; - -@Slf4j -@Component -@RequiredArgsConstructor -public class PushSenderAdapter implements PushSenderPort { - - private static final String ACTION_SEND = "send"; - private static final String ACTION_REGISTER = "register"; - private static final String ACTION_DELETE = "cancel"; - - private final NotificationHttpClient notificationHttpClient; - - @Override - public void send(PushMessage message) { - if (message.userIds().isEmpty()) { - return; - } - try { - notificationHttpClient.send(ServiceType.APP, ACTION_SEND, PushSendRequest.from(message)); - } catch (RestClientException e) { - log.warn("푸시 발송 실패 - title={}, 대상 {}명", message.title(), message.userIds().size(), e); - throw new PushException(PushFailure.FAIL_SEND_PUSH); - } - } - - @Override - public void register(PushToken pushToken) { - manageToken(ACTION_REGISTER, pushToken); - } - - @Override - public void delete(PushToken pushToken) { - manageToken(ACTION_DELETE, pushToken); - } - - private void manageToken(String action, PushToken pushToken) { - try { - notificationHttpClient.send( - ServiceType.APP, - action, - PushTokenManageRequest.from(pushToken), - toPlatformHeader(pushToken.platform())); - } catch (RestClientException e) { - log.warn("푸시 토큰 {} 실패 - userId={}", action, pushToken.userId(), e); - throw new PushException(PushFailure.FAIL_MANAGE_PUSH_TOKEN); - } - } - - private String toPlatformHeader(PushTokenPlatform platform) { - return platform == PushTokenPlatform.IOS ? "iOS" : "Android"; - } -} diff --git a/clients/src/main/java/org/sopt/makers/clients/push/dto/PushSendRequest.java b/clients/src/main/java/org/sopt/makers/clients/push/dto/PushSendRequest.java index e929a2ef..ec8b4e76 100644 --- a/clients/src/main/java/org/sopt/makers/clients/push/dto/PushSendRequest.java +++ b/clients/src/main/java/org/sopt/makers/clients/push/dto/PushSendRequest.java @@ -1,12 +1,18 @@ package org.sopt.makers.clients.push.dto; import com.fasterxml.jackson.annotation.JsonInclude; -import java.util.Set; +import java.util.Collection; +import java.util.List; +import org.sopt.makers.domain.admin.alarm.Alarm; +import org.sopt.makers.domain.admin.alarm.AlarmLinkType; +import org.sopt.makers.domain.admin.alarm.AlarmTargetType; import org.sopt.makers.domain.app.push.PushMessage; +import org.sopt.makers.domain.crew.meeting.demand.notification.MeetingDemandNotification; +import org.sopt.makers.domain.crew.notification.MeetingNotification; @JsonInclude(JsonInclude.Include.NON_NULL) public record PushSendRequest( - Set userIds, + Collection userIds, String title, String content, String category, @@ -24,4 +30,41 @@ public static PushSendRequest from(PushMessage message) { message.deepLink(), message.webLink()); } + + public static PushSendRequest from(Alarm alarm) { + boolean isTargetAll = AlarmTargetType.ALL.equals(alarm.target().targetType()); + boolean isAppLink = AlarmLinkType.APP.equals(alarm.content().linkType()); + boolean isWebLink = AlarmLinkType.WEB.equals(alarm.content().linkType()); + return new PushSendRequest( + isTargetAll ? null : alarm.target().targetIds(), + alarm.content().title(), + alarm.content().content(), + alarm.content().category().name(), + isAppLink ? alarm.content().linkPath() : null, + isWebLink ? alarm.content().linkPath() : null); + } + + public static PushSendRequest from(MeetingNotification notification, String crewWebUrl) { + return new PushSendRequest( + toUserIds(notification.userIds()), + notification.title(), + notification.content(), + notification.category(), + null, + crewWebUrl + "/detail?id=" + notification.meetingId()); + } + + public static PushSendRequest from(MeetingDemandNotification notification, String crewWebUrl) { + return new PushSendRequest( + toUserIds(notification.userIds()), + notification.title(), + notification.content(), + notification.category(), + null, + crewWebUrl + notification.webPath()); + } + + private static List toUserIds(List userIds) { + return userIds.stream().map(String::valueOf).toList(); + } } diff --git a/clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java b/clients/src/test/java/org/sopt/makers/clients/push/PushClientAdapterTest.java similarity index 91% rename from clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java rename to clients/src/test/java/org/sopt/makers/clients/push/PushClientAdapterTest.java index 8a2e215e..fa621a7f 100644 --- a/clients/src/test/java/org/sopt/makers/clients/push/PushSenderAdapterTest.java +++ b/clients/src/test/java/org/sopt/makers/clients/push/PushClientAdapterTest.java @@ -11,28 +11,29 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.sopt.makers.clients.alarm.AlarmProperty; -import org.sopt.makers.clients.notification.NotificationHttpClient; import org.sopt.makers.domain.app.notification.NotificationCategory; import org.sopt.makers.domain.app.push.PushMessage; import org.sopt.makers.domain.app.push.PushToken; import org.sopt.makers.domain.app.push.PushTokenPlatform; import org.sopt.makers.domain.app.push.exception.PushException; +import org.springframework.core.env.StandardEnvironment; import org.springframework.web.client.RestTemplate; -@DisplayName("PushSenderAdapter 테스트") -class PushSenderAdapterTest { +@DisplayName("PushClientAdapter 테스트") +class PushClientAdapterTest { private MockWebServer pushServer; - private PushSenderAdapter adapter; + private PushClientAdapter adapter; @BeforeEach void setUp() throws Exception { pushServer = new MockWebServer(); pushServer.start(); String baseUrl = pushServer.url("/").toString().replaceAll("/$", ""); - AlarmProperty property = new AlarmProperty(baseUrl, "test-push-key", "test-arn"); - adapter = new PushSenderAdapter(new NotificationHttpClient(new RestTemplate(), property)); + PushProperty property = new PushProperty(baseUrl, "test-push-key", "test-arn"); + adapter = + new PushClientAdapter( + new PushHttpClient(new RestTemplate(), property), new StandardEnvironment()); } @AfterEach From 2e4891c5906e367ad1486d7affda365d38542a22 Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sun, 30 Aug 2026 13:40:32 +0900 Subject: [PATCH 05/11] =?UTF-8?q?refactor:=20TimeExpressionConstant=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=EC=95=88=EB=90=9C=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/alarm/dto/AdminAlarmResponse.java | 15 +++++++-------- .../eventbridge/AlarmScheduleDeleterAdapter.java | 6 +++--- .../eventbridge/AlarmScheduleSenderAdapter.java | 6 +++--- .../core/constant/TimeExpressionConstant.java | 3 +++ .../domain/admin/alarm/service/AlarmService.java | 14 +++++++------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/api/src/main/java/org/sopt/makers/api/controller/admin/alarm/dto/AdminAlarmResponse.java b/api/src/main/java/org/sopt/makers/api/controller/admin/alarm/dto/AdminAlarmResponse.java index 61c02a76..8c3f1738 100644 --- a/api/src/main/java/org/sopt/makers/api/controller/admin/alarm/dto/AdminAlarmResponse.java +++ b/api/src/main/java/org/sopt/makers/api/controller/admin/alarm/dto/AdminAlarmResponse.java @@ -1,5 +1,9 @@ package org.sopt.makers.api.controller.admin.alarm.dto; +import static org.sopt.makers.core.constant.TimeExpressionConstant.DATE; +import static org.sopt.makers.core.constant.TimeExpressionConstant.DATETIME; +import static org.sopt.makers.core.constant.TimeExpressionConstant.TIME; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import java.time.LocalDateTime; @@ -40,9 +44,6 @@ record AlarmDetail( String linkType) implements AdminAlarmResponse { - private static final String DATE_FORMAT = "yyyy-MM-dd"; - private static final String TIME_FORMAT = "HH:mm"; - public static AlarmDetail from(Alarm alarm) { return new AlarmDetail( alarm.status().getDescription(), @@ -64,11 +65,11 @@ public static AlarmDetail from(Alarm alarm) { } private static String toDate(LocalDateTime dt) { - return dt == null ? null : dt.toLocalDate().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + return dt == null ? null : dt.toLocalDate().format(DateTimeFormatter.ofPattern(DATE)); } private static String toTime(LocalDateTime dt) { - return dt == null ? null : dt.toLocalTime().format(DateTimeFormatter.ofPattern(TIME_FORMAT)); + return dt == null ? null : dt.toLocalTime().format(DateTimeFormatter.ofPattern(TIME)); } } @@ -91,8 +92,6 @@ record AlarmItem( String title, String content) { - private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm"; - private static AlarmItem from(Alarm alarm) { return new AlarmItem( alarm.id(), @@ -108,7 +107,7 @@ private static AlarmItem from(Alarm alarm) { } private static String toDateTime(LocalDateTime dt) { - return Objects.isNull(dt) ? null : dt.format(DateTimeFormatter.ofPattern(DATETIME_FORMAT)); + return Objects.isNull(dt) ? null : dt.format(DateTimeFormatter.ofPattern(DATETIME)); } } } diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java index bbdc38ab..bfcf9b4b 100644 --- a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java @@ -1,5 +1,7 @@ package org.sopt.makers.clients.eventbridge; +import static org.sopt.makers.core.constant.TimeExpressionConstant.DATE; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import lombok.RequiredArgsConstructor; @@ -14,7 +16,6 @@ @RequiredArgsConstructor public class AlarmScheduleDeleterAdapter implements AlarmScheduleDeleterPort { - private static final String DATE_FORMAT = "yyyy-MM-dd"; private static final String SCHEDULE_TIME_FORMAT = "HH-mm"; private final SchedulerClient schedulerClient; @@ -30,8 +31,7 @@ public void delete(long alarmId, LocalDateTime scheduleDateTime) { } private String buildEventName(long alarmId, LocalDateTime scheduleDateTime) { - String dateData = - scheduleDateTime.toLocalDate().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + String dateData = scheduleDateTime.toLocalDate().format(DateTimeFormatter.ofPattern(DATE)); String timeData = scheduleDateTime.toLocalTime().format(DateTimeFormatter.ofPattern(SCHEDULE_TIME_FORMAT)); return String.format("%s_%s_%d", dateData, timeData, alarmId); diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java index aa6581eb..9e912fa8 100644 --- a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java @@ -1,5 +1,7 @@ package org.sopt.makers.clients.eventbridge; +import static org.sopt.makers.core.constant.TimeExpressionConstant.DATE; + import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.UUID; @@ -27,7 +29,6 @@ @RequiredArgsConstructor public class AlarmScheduleSenderAdapter implements AlarmScheduleSenderPort { - private static final String DATE_FORMAT = "yyyy-MM-dd"; private static final String SCHEDULE_TIME_FORMAT = "HH-mm"; private final SchedulerClient schedulerClient; @@ -57,8 +58,7 @@ public void send(Alarm alarm) { } private String buildEventName(Alarm alarm) { - String dateData = - alarm.intendedAt().toLocalDate().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + String dateData = alarm.intendedAt().toLocalDate().format(DateTimeFormatter.ofPattern(DATE)); String timeData = alarm.intendedAt().toLocalTime().format(DateTimeFormatter.ofPattern(SCHEDULE_TIME_FORMAT)); return String.format("%s_%s_%d", dateData, timeData, alarm.id()); diff --git a/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java b/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java index d7ca143a..1a837660 100644 --- a/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java +++ b/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java @@ -4,5 +4,8 @@ public final class TimeExpressionConstant { private TimeExpressionConstant() {} + public static final String DATE = "yyyy-MM-dd"; + public static final String TIME = "HH:mm"; + public static final String DATETIME = DATE + " " + TIME; public static final String YEAR_MONTH = "yyyy-MM"; } diff --git a/domain/domain-admin/src/main/java/org/sopt/makers/domain/admin/alarm/service/AlarmService.java b/domain/domain-admin/src/main/java/org/sopt/makers/domain/admin/alarm/service/AlarmService.java index aeec1bcf..d15cf43f 100644 --- a/domain/domain-admin/src/main/java/org/sopt/makers/domain/admin/alarm/service/AlarmService.java +++ b/domain/domain-admin/src/main/java/org/sopt/makers/domain/admin/alarm/service/AlarmService.java @@ -1,5 +1,9 @@ package org.sopt.makers.domain.admin.alarm.service; +import static org.sopt.makers.core.constant.TimeExpressionConstant.DATE; +import static org.sopt.makers.core.constant.TimeExpressionConstant.DATETIME; +import static org.sopt.makers.core.constant.TimeExpressionConstant.TIME; + import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -31,10 +35,6 @@ @Transactional(readOnly = true) public class AlarmService { - private static final String DATE_FORMAT = "yyyy-MM-dd"; - private static final String TIME_FORMAT = "HH:mm"; - private static final String DATETIME_FORMAT = DATE_FORMAT + " " + TIME_FORMAT; - private final AlarmRepositoryPort alarmRepositoryPort; private final AlarmMemberQueryPort alarmMemberQueryPort; private final AlarmInstantSenderPort alarmInstantSenderPort; @@ -103,7 +103,7 @@ public void updateAlarmStatus(long alarmId, String sendAt) { .findById(alarmId) .orElseThrow(() -> new AlarmException(AlarmFailure.NOT_FOUND_ALARM)); LocalDateTime sendAtDateTime = - LocalDateTime.parse(sendAt, DateTimeFormatter.ofPattern(DATETIME_FORMAT)); + LocalDateTime.parse(sendAt, DateTimeFormatter.ofPattern(DATETIME)); alarmRepositoryPort.save(alarm.complete(sendAtDateTime)); } @@ -131,8 +131,8 @@ private List extractTargetIds(AlarmTarget target) { private LocalDateTime parseDateTime(String date, String time) { try { - LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern(DATE_FORMAT)); - LocalTime localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern(TIME_FORMAT)); + LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern(DATE)); + LocalTime localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern(TIME)); return LocalDateTime.of(localDate, localTime); } catch (Exception e) { throw new AlarmException(AlarmFailure.INVALID_SCHEDULE_ALARM_FORMAT); From 479e6f3047527c9bb70dee6faef5a2b949fa606e Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sun, 30 Aug 2026 13:45:04 +0900 Subject: [PATCH 06/11] =?UTF-8?q?del:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/sopt/makers/clients/eventbridge/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 clients/src/main/java/org/sopt/makers/clients/eventbridge/.gitkeep diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/.gitkeep b/clients/src/main/java/org/sopt/makers/clients/eventbridge/.gitkeep deleted file mode 100644 index e69de29b..00000000 From 4a091581b07a6f7124e4484bafc882b3b2489950 Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sun, 30 Aug 2026 13:51:41 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor:=20time=20expresssion=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../clients/eventbridge/AlarmScheduleDeleterAdapter.java | 5 ++--- .../clients/eventbridge/AlarmScheduleSenderAdapter.java | 5 ++--- .../sopt/makers/core/constant/TimeExpressionConstant.java | 1 + 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java index bfcf9b4b..f6f09908 100644 --- a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleDeleterAdapter.java @@ -1,6 +1,7 @@ package org.sopt.makers.clients.eventbridge; import static org.sopt.makers.core.constant.TimeExpressionConstant.DATE; +import static org.sopt.makers.core.constant.TimeExpressionConstant.FILE_SAFE_TIME; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -16,8 +17,6 @@ @RequiredArgsConstructor public class AlarmScheduleDeleterAdapter implements AlarmScheduleDeleterPort { - private static final String SCHEDULE_TIME_FORMAT = "HH-mm"; - private final SchedulerClient schedulerClient; @Override @@ -33,7 +32,7 @@ public void delete(long alarmId, LocalDateTime scheduleDateTime) { private String buildEventName(long alarmId, LocalDateTime scheduleDateTime) { String dateData = scheduleDateTime.toLocalDate().format(DateTimeFormatter.ofPattern(DATE)); String timeData = - scheduleDateTime.toLocalTime().format(DateTimeFormatter.ofPattern(SCHEDULE_TIME_FORMAT)); + scheduleDateTime.toLocalTime().format(DateTimeFormatter.ofPattern(FILE_SAFE_TIME)); return String.format("%s_%s_%d", dateData, timeData, alarmId); } } diff --git a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java index 9e912fa8..cb7a9d0a 100644 --- a/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java +++ b/clients/src/main/java/org/sopt/makers/clients/eventbridge/AlarmScheduleSenderAdapter.java @@ -1,6 +1,7 @@ package org.sopt.makers.clients.eventbridge; import static org.sopt.makers.core.constant.TimeExpressionConstant.DATE; +import static org.sopt.makers.core.constant.TimeExpressionConstant.FILE_SAFE_TIME; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -29,8 +30,6 @@ @RequiredArgsConstructor public class AlarmScheduleSenderAdapter implements AlarmScheduleSenderPort { - private static final String SCHEDULE_TIME_FORMAT = "HH-mm"; - private final SchedulerClient schedulerClient; private final ObjectMapper objectMapper; private final PushProperty pushProperty; @@ -60,7 +59,7 @@ public void send(Alarm alarm) { private String buildEventName(Alarm alarm) { String dateData = alarm.intendedAt().toLocalDate().format(DateTimeFormatter.ofPattern(DATE)); String timeData = - alarm.intendedAt().toLocalTime().format(DateTimeFormatter.ofPattern(SCHEDULE_TIME_FORMAT)); + alarm.intendedAt().toLocalTime().format(DateTimeFormatter.ofPattern(FILE_SAFE_TIME)); return String.format("%s_%s_%d", dateData, timeData, alarm.id()); } diff --git a/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java b/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java index 1a837660..97512abe 100644 --- a/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java +++ b/core/src/main/java/org/sopt/makers/core/constant/TimeExpressionConstant.java @@ -6,6 +6,7 @@ private TimeExpressionConstant() {} public static final String DATE = "yyyy-MM-dd"; public static final String TIME = "HH:mm"; + public static final String FILE_SAFE_TIME = "HH-mm"; public static final String DATETIME = DATE + " " + TIME; public static final String YEAR_MONTH = "yyyy-MM"; } From 62671ed12022baf7d9d12feba5ac5a65e971af16 Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sun, 30 Aug 2026 14:35:50 +0900 Subject: [PATCH 08/11] =?UTF-8?q?chore:=20created=5Fat,=20updated=5Fat=20g?= =?UTF-8?q?eneration=20=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/db/migration/V1__create_initial_schema.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/src/main/resources/db/migration/V1__create_initial_schema.sql b/api/src/main/resources/db/migration/V1__create_initial_schema.sql index 076f9fb5..a69ed769 100644 --- a/api/src/main/resources/db/migration/V1__create_initial_schema.sql +++ b/api/src/main/resources/db/migration/V1__create_initial_schema.sql @@ -26,6 +26,8 @@ CREATE TABLE generation dark_mode_text_color VARCHAR(5), light_mode_key_color VARCHAR(7), light_mode_text_color VARCHAR(5), + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, PRIMARY KEY (id) ); From 23178f856155b887a7460cc25d93050d25028337 Mon Sep 17 00:00:00 2001 From: sung-silver Date: Sun, 30 Aug 2026 14:36:47 +0900 Subject: [PATCH 09/11] =?UTF-8?q?chore:=20base=20entity=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makers/storage/db/official/entity/GenerationEntity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/src/main/java/org/sopt/makers/storage/db/official/entity/GenerationEntity.java b/storage/src/main/java/org/sopt/makers/storage/db/official/entity/GenerationEntity.java index 12795aa8..494881ee 100644 --- a/storage/src/main/java/org/sopt/makers/storage/db/official/entity/GenerationEntity.java +++ b/storage/src/main/java/org/sopt/makers/storage/db/official/entity/GenerationEntity.java @@ -11,12 +11,13 @@ import lombok.NoArgsConstructor; import org.sopt.makers.domain.official.generation.BrandingColor; import org.sopt.makers.domain.official.generation.Generation; +import org.sopt.makers.storage.db.common.BaseEntity; @Entity @Getter @NoArgsConstructor(access = PROTECTED) @Table(name = "generation") -public class GenerationEntity { +public class GenerationEntity extends BaseEntity { @Id @Column(name = "id", nullable = false) From 3f20e63796a595ef87bcdb7f53dba5c70d0629eb Mon Sep 17 00:00:00 2001 From: sung-silver Date: Mon, 31 Aug 2026 22:35:35 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor:=20alarm=20->=20push=20=EB=A1=9C?= =?UTF-8?q?=20=EB=8B=A8=EC=96=B4=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/main/resources/external.yml | 2 +- .../main/java/org/sopt/makers/clients/push/PushProperty.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/resources/external.yml b/api/src/main/resources/external.yml index c630e5f9..87b55832 100644 --- a/api/src/main/resources/external.yml +++ b/api/src/main/resources/external.yml @@ -18,7 +18,7 @@ external: playground: url: ${PLAYGROUND_URL} token: ${PLAYGROUND_TOKEN} - alarm: + push: url: ${ALARM_NOTIFICATION_URL} key: ${ALARM_NOTIFICATION_KEY} arn: ${ALARM_NOTIFICATION_ARN} diff --git a/clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java b/clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java index 139e0f94..2c30b26b 100644 --- a/clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java +++ b/clients/src/main/java/org/sopt/makers/clients/push/PushProperty.java @@ -5,5 +5,5 @@ import org.springframework.validation.annotation.Validated; @Validated -@ConfigurationProperties(prefix = "external.alarm") +@ConfigurationProperties(prefix = "external.push") public record PushProperty(@NotBlank String url, @NotBlank String key, @NotBlank String arn) {} From f5d5fb86efd9ee5418c8bde33c6831a451adb54e Mon Sep 17 00:00:00 2001 From: sung-silver Date: Mon, 31 Aug 2026 23:05:00 +0900 Subject: [PATCH 11/11] =?UTF-8?q?test:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EB=B6=88=EC=9D=BC=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/test/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/test/resources/application.yml b/api/src/test/resources/application.yml index 23ed25e4..750aa10d 100644 --- a/api/src/test/resources/application.yml +++ b/api/src/test/resources/application.yml @@ -60,7 +60,7 @@ external: playground: url: http://localhost token: test-playground-token - alarm: + push: url: http://localhost key: test-alarm-key arn: arn:aws:scheduler:ap-northeast-2:000000000000:schedule/test