-
Notifications
You must be signed in to change notification settings - Fork 0
[UPLUS-138] 이메일, SMS 전송 로직 구현, usage-noti request 복호화/마스킹 #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cb15584
03a8dfa
cfe644b
1ea4be7
e68dcae
439251f
3307352
67d7958
e5cb4b7
3e4284b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,51 @@ | ||||||||||||||||||
| package com.project.controller; | ||||||||||||||||||
|
|
||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||
|
|
||||||||||||||||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestBody; | ||||||||||||||||||
| import org.springframework.web.bind.annotation.RestController; | ||||||||||||||||||
|
|
||||||||||||||||||
| import com.project.notification.consumer.UsageNotificationEvent; | ||||||||||||||||||
| import com.project.notification.service.MessageSendService; | ||||||||||||||||||
|
|
||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||
|
|
||||||||||||||||||
| @Slf4j | ||||||||||||||||||
| @RestController | ||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||
| public class TestNotificationController { | ||||||||||||||||||
|
|
||||||||||||||||||
| private final MessageSendService messageSendService; | ||||||||||||||||||
|
|
||||||||||||||||||
| /** [테스트용] Kafka 없이 HTTP 요청으로 알림 발송 로직 직접 트리거 */ | ||||||||||||||||||
| @PostMapping("/test/send-notification") | ||||||||||||||||||
| public String sendTest(@RequestBody Map<String, Object> request) { | ||||||||||||||||||
|
|
||||||||||||||||||
| // 1. Postman JSON 데이터를 추출 | ||||||||||||||||||
| Long subId = Long.valueOf((Integer) request.get("subId")); | ||||||||||||||||||
| String email = (String) request.get("email"); | ||||||||||||||||||
| String phoneNumber = (String) request.get("phoneNumber"); | ||||||||||||||||||
| Long templateGroupId = Long.valueOf((Integer) request.get("templateGroupId")); | ||||||||||||||||||
|
Comment on lines
+28
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
|
|
||||||||||||||||||
| // variables는 리스트가 포함될 수 있으므로 Object로 캐스팅 | ||||||||||||||||||
| Map<String, Object> variables = (Map<String, Object>) request.get("variables"); | ||||||||||||||||||
|
|
||||||||||||||||||
| // 2. 가짜 이벤트 객체(UsageNotificationEvent) 생성 | ||||||||||||||||||
| UsageNotificationEvent event = | ||||||||||||||||||
| new UsageNotificationEvent( | ||||||||||||||||||
| UUID.randomUUID(), // 임의의 Event ID 생성 | ||||||||||||||||||
| templateGroupId, | ||||||||||||||||||
| new UsageNotificationEvent.SubscriptionInfo(subId, phoneNumber, email), | ||||||||||||||||||
| variables); | ||||||||||||||||||
|
|
||||||||||||||||||
| log.info("[TEST TRIGGER] subId={}, groupId={}", subId, templateGroupId); | ||||||||||||||||||
|
|
||||||||||||||||||
| // 3. 서비스 로직 실행 (템플릿 조립 -> Mock Server 전송) | ||||||||||||||||||
| messageSendService.processEvent(event); | ||||||||||||||||||
|
|
||||||||||||||||||
| return "Test Triggered! EventID: " + event.eventId(); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| package com.project.global.util; | ||
|
|
||
| public final class MaskingUtil { | ||
|
|
||
| private MaskingUtil() {} | ||
|
|
||
| // 마스킹 010-**12-**12의 형식 | ||
| public static String maskPhone(String phone) { | ||
| if (phone == null || phone.isBlank()) { | ||
| return phone; | ||
| } | ||
|
|
||
| // 숫자만 추출 | ||
| String digits = phone.replaceAll("\\D", ""); | ||
|
|
||
| // 휴대폰 번호 길이 최소 검증 (010XXXXXXXX 기준) | ||
| if (digits.length() != 11) { | ||
| return "***"; | ||
| } | ||
|
|
||
| String first = digits.substring(0, 3); | ||
| String middle = digits.substring(3, 7); | ||
| String last = digits.substring(7, 11); | ||
|
|
||
| // 010-**34-**12 | ||
| return String.format("%s-**%s-**%s", first, middle.substring(2), last.substring(2)); | ||
| } | ||
|
|
||
| public static String maskEmail(String email) { | ||
| if (email == null || email.isBlank()) { | ||
| return email; | ||
| } | ||
|
|
||
| int at = email.indexOf('@'); | ||
| if (at < 0) { | ||
| return null; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } // @ 없으면 null | ||
|
|
||
| String local = email.substring(0, at); | ||
| String domain = email.substring(at); // "@domain.com" | ||
|
|
||
| // local 비어있으면 "***@domain.com" | ||
| if (local.isEmpty()) { | ||
| return "***" + domain; | ||
| } | ||
|
|
||
| // local 1글자 이상이면 "첫 글자 + *** + @domain.com" | ||
| return local.substring(0, 1) + "***" + domain; | ||
| } | ||
|
|
||
| public static Object maskByFieldName(String key, Object value) { | ||
| if (!(value instanceof String strVal)) return value; | ||
| String lowerKey = key.toLowerCase(); | ||
|
|
||
| if (lowerKey.contains("email")) return maskEmail(strVal); | ||
| if (lowerKey.contains("phone") || lowerKey.contains("contact")) return maskPhone(strVal); | ||
|
|
||
| return value; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요청 본문을
Map<String, Object>로 받는 것은 타입 안전성을 보장하지 않으며, 런타임에ClassCastException과 같은 예외를 발생시킬 수 있습니다. 또한 API의 명세를 코드만으로 파악하기 어렵게 만듭니다. 별도의 DTO(Data Transfer Object) 클래스를 정의하여 사용하면 코드가 더 견고해지고 가독성이 향상되며, API 명세가 명확해집니다.