Skip to content

[UPLUS-139] message 조립 및 전송 개선#4

Merged
swthewhite merged 6 commits into
developfrom
feat/UPLUS-139
Jan 25, 2026
Merged

[UPLUS-139] message 조립 및 전송 개선#4
swthewhite merged 6 commits into
developfrom
feat/UPLUS-139

Conversation

@swthewhite
Copy link
Copy Markdown
Contributor

🍀 이슈 번호

UPLUS-139


✅ 작업 사항


📋 체크리스트

  • 코드가 정상적으로 빌드됩니다.
  • 관련 테스트 코드를 작성했습니다.
  • 기존 테스트가 모두 통과합니다.
  • 코드 스타일(Spotless, Checkstyle)을 준수합니다.

⌨ 기타

- UsageNotificationEvent 구조 변경 (templateGroupId, subscriptionInfo, variables)
- EmailSendRequest, SmsSendRequest, SendResponse DTO 추가
- RestClient 설정 및 mock-server 엔드포인트 연동
- EmailSender/SmsSender HTTP POST 호출로 변경
- MessageSendService 추가 (EMAIL 우선, SMS 폴백)
- 불필요한 파일 삭제 (NotificationService, Customer/Subscription 관련)
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @swthewhite, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 풀 리퀘스트는 알림 서비스의 메시지 조립 및 전송 시스템을 전반적으로 개선하는 데 중점을 둡니다. 기존의 단일 메시지 처리 방식에서 벗어나 배치 처리와 외부 API 연동을 통해 시스템의 효율성과 확장성을 향상시키고, 메시지 전송 로직을 재구성하여 유지보수성을 높였습니다. 또한, 데이터 중복 제거 및 템플릿 관리 방식을 최적화하여 안정적인 메시지 전달을 목표로 합니다.

Highlights

  • 메시지 전송 로직 리팩토링: 기존 NotificationService를 제거하고 MessageSendService를 도입하여 이메일 및 SMS 전송, 템플릿 렌더링, 폴백 처리, 로깅 등의 메시지 전송 관련 로직을 통합하고 개선했습니다.
  • 카프카 컨슈머 배치 처리 도입: NotificationConsumer가 단일 레코드 처리 방식에서 배치 처리 방식으로 변경되어, max-poll-records 설정을 통해 여러 메시지를 한 번에 처리할 수 있도록 효율성을 높였습니다.
  • Redis 기반 배치 중복 제거 기능 추가: NotificationSendDedupService에 Redis Lua 스크립트를 활용한 tryAcquireBatch 메서드를 추가하여, 배치 처리 시 여러 이벤트 ID에 대한 중복 제거를 효율적으로 수행할 수 있게 되었습니다.
  • 외부 메시지 전송 API 연동: 이메일 및 SMS 발송을 위한 EmailSenderSmsSender가 더 이상 로컬 로깅이 아닌, RestClient를 사용하여 실제 외부 Mock 서버 API와 연동하도록 변경되었습니다. 이를 위해 RestClientConfig가 새로 추가되었습니다.
  • 데이터 모델 및 저장소 계층 간소화: 기존 Customer, Subscription 관련 엔티티 및 리포지토리(JpaRepository 포함)가 제거되었으며, UsageNotificationEvent 레코드 구조가 templateGroupIdSubscriptionInfo를 포함하도록 변경되어 이벤트 기반의 데이터 전달 방식을 강화했습니다.
  • 템플릿 조회 로직 개선: TemplateService 및 관련 리포지토리에 templateGroupId를 기반으로 활성 템플릿 버전을 조회하는 새로운 메서드가 추가되어 템플릿 관리의 유연성을 높였습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added size/XL PR size: XL fix 버그 수정 refactor 리팩토링 labels Jan 25, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

안녕하세요. 이번 PR은 알림 서비스의 메시지 조립 및 전송 로직을 대폭 개선하는 중요한 변경 사항을 담고 있네요. 카프카 메시지 처리를 개별에서 배치 방식으로 전환하고, 외부 서비스와의 결합도를 낮추는 등 전반적인 아키텍처가 크게 향상되었습니다. 특히 Redis Lua 스크립트를 이용한 중복 제거 최적화는 인상적입니다. 코드 품질을 더욱 높이기 위해 몇 가지 제안 사항을 남깁니다. 주로 HTTP 클라이언트 설정, 상수 관리, 예외 처리, 메트릭 일관성에 대한 내용입니다. 좋은 변경사항에 감사드립니다.

I am having trouble creating individual review comments. Click here to see my feedback.

src/main/java/com/project/notification/service/MessageSendService.java (48-49)

high

tryEmailSendtrySmsFallback 메소드 내에서 템플릿 렌더링(templateEngine.render()) 시 발생하는 예외가 처리되지 않고 있습니다. 이 예외는 processEvent@Transactional 범위 밖인 NotificationConsumer까지 전파되어 처리되는데, 이 경우 MessageSendService의 트랜잭션이 롤백되어 실패에 대한 MessageLog가 기록되지 않는 문제가 있습니다. 발송 실패에 대한 모든 기록을 남기기 위해, tryEmailSendtrySmsFallback 메소드 내에서 템플릿 렌더링을 포함한 모든 과정에 대해 try-catch 블록을 적용하여 예외 발생 시 MessageLog를 저장하도록 로직을 수정해야 합니다.

src/main/java/com/project/global/config/RestClientConfig.java (24-33)

medium

현재 SimpleClientHttpRequestFactory를 사용하고 계십니다. 이 팩토리는 커넥션 풀링을 지원하지 않아, 요청마다 새로운 TCP 연결을 생성하므로 높은 처리량이 요구되는 환경에서는 성능 저하의 원인이 될 수 있습니다. HttpComponentsClientHttpRequestFactory (Apache HttpClient 기반)와 같은 커넥션 풀링을 지원하는 팩토리를 사용하시는 것을 강력히 권장합니다. 이를 통해 커넥션을 재사용하여 성능을 향상시키고 리소스를 효율적으로 관리할 수 있습니다.

src/main/java/com/project/notification/consumer/NotificationConsumer.java (55-68)

medium

현재 이벤트 처리 로직에서 dedupResultsevents 리스트의 크기가 일치하지 않을 가능성이 있습니다. records 리스트를 순회하며 역직렬화에 실패하는 경우, eventseventIds 리스트에는 해당 이벤트가 추가되지 않습니다. 하지만 dedupResults는 성공적으로 역직렬화된 eventIds를 기반으로 생성됩니다. 이후 for (int i = 0; i < events.size(); i++) 루프에서 dedupResults.get(i)를 호출하는 것은 문제가 없어 보이지만, 만약 dedupResultsevents의 인덱스가 어긋나는 엣지 케이스가 발생하면 IndexOutOfBoundsException이 발생하거나 잘못된 중복 체크를 할 수 있습니다.

보다 안전한 처리를 위해, 역직렬화된 이벤트와 그에 해당하는 원본 레코드 정보를 함께 관리하는 객체를 사용하거나, Map<String, UsageNotificationEvent>을 사용하여 eventId로 이벤트를 관리하는 방식을 고려해볼 수 있습니다. 하지만 현재 구조도 대부분의 경우 잘 동작할 것으로 보이며, 로직을 조금 더 명확하게 하기 위해 주석을 추가하는 것도 좋은 방법입니다.

src/main/java/com/project/notification/consumer/NotificationSendDedupService.java (47)

medium

Redis 키의 접두사 "notification:event:"tryAcquire 메소드와 tryAcquireBatch 메소드 양쪽에서 하드코딩되어 사용되고 있습니다. 이 값을 private static final String 상수로 추출하면 코드 중복을 줄이고, 향후 키 구조 변경 시 한 곳만 수정하면 되므로 유지보수성이 향상됩니다.

클래스 상단에 상수를 추가하고, 두 메소드에서 이를 사용하도록 수정하는 것을 권장합니다.

private static final String DEDUP_KEY_PREFIX = "notification:event:";

// ...

public boolean tryAcquire(String eventId) {
    Boolean success =
            redisTemplate.opsForValue().setIfAbsent(DEDUP_KEY_PREFIX + eventId, "1", TTL);
    return Boolean.TRUE.equals(success);
}

public List<Boolean> tryAcquireBatch(List<String> eventIds) {
    // ...
    List<String> keys = eventIds.stream()
            .map(id -> DEDUP_KEY_PREFIX + id)
            .toList();
    // ...
}

src/main/java/com/project/notification/service/MessageSendService.java (65)

medium

tryEmailSend 메소드 내에서 처리 시간을 계산하는 방식에 일관성이 부족합니다. 성공 케이스와 일부 실패 케이스에서는 processEvent에서 전달받은 startTime을 사용하고, emailSender.send()에서 예외가 발생한 경우에만 emailStartTime을 사용합니다. 이로 인해 기록되는 처리 시간이 경우에 따라 달라져 메트릭 분석에 혼란을 줄 수 있습니다. 모든 경우에 tryEmailSend 메소드 시작 시점인 emailStartTime을 기준으로 처리 시간을 계산하여 일관성을 확보하는 것이 좋습니다.

@github-actions github-actions Bot added the feat 새로운 기능 label Jan 25, 2026
@github-actions github-actions Bot added the style 코드 스타일 변경 label Jan 25, 2026
@github-actions
Copy link
Copy Markdown

SonarQube Quality Summary (Community)

Quality Gate FAILED

Branch: feat/UPLUS-139
Compared to: default branch

Issues

  • 🐞 Bugs: 0
  • 🔐 Vulnerabilities: 2
  • 📎 Code Smells: 44

Measures

  • Coverage: 0%
  • Duplication: 0%

🔗 Dashboard: https://sonarqube.swthewhite.store/dashboard?id=api-message&branch=feat/UPLUS-139

Generated automatically by GitHub Actions.

@github-actions github-actions Bot added the chore 기타 변경사항 label Jan 25, 2026
@github-actions
Copy link
Copy Markdown

SonarQube Quality Summary (Community)

Quality Gate PASSED

Branch: feat/UPLUS-139
Compared to: default branch

Issues

  • 🐞 Bugs: 0
  • 🔐 Vulnerabilities: 2
  • 📎 Code Smells: 38

Measures

  • Coverage: 0%
  • Duplication: 0%

🔗 Dashboard: https://sonarqube.swthewhite.store/dashboard?id=api-message&branch=feat/UPLUS-139

Generated automatically by GitHub Actions.

@swthewhite swthewhite merged commit 6ca9f71 into develop Jan 25, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore 기타 변경사항 feat 새로운 기능 fix 버그 수정 refactor 리팩토링 size/XL PR size: XL style 코드 스타일 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant